Clean up code

This commit is contained in:
Tulir Asokan
2018-12-23 19:51:02 +02:00
parent 06f1bcfb3f
commit dad0ccb3c0
18 changed files with 90 additions and 83 deletions
+3 -4
View File
@@ -14,7 +14,7 @@
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
from typing import Coroutine, List from typing import Awaitable, List, Any
import argparse import argparse
import asyncio import asyncio
import logging.config import logging.config
@@ -88,7 +88,7 @@ if config["appservice.sqlalchemy_core_mode"]:
loop = asyncio.get_event_loop() # type: asyncio.AbstractEventLoop loop = asyncio.get_event_loop() # type: asyncio.AbstractEventLoop
state_store = SQLStateStore(db_session) state_store = SQLStateStore()
mebibyte = 1024 ** 2 mebibyte = 1024 ** 2
appserv = AppService(config["homeserver.address"], config["homeserver.domain"], appserv = AppService(config["homeserver.address"], config["homeserver.domain"],
config["appservice.as_token"], config["appservice.hs_token"], config["appservice.as_token"], config["appservice.hs_token"],
@@ -121,8 +121,7 @@ with appserv.run(config["appservice.hostname"], config["appservice.port"]) as st
init_portal(context) init_portal(context)
startup_actions = (init_puppet(context) + startup_actions = (init_puppet(context) +
init_user(context) + init_user(context) +
[start, [start, context.mx.init_as_bot()]) # type: List[Awaitable[Any]]
context.mx.init_as_bot()]) # type: List[Coroutine]
if context.bot: if context.bot:
startup_actions.append(context.bot.start()) startup_actions.append(context.bot.start())
+9 -9
View File
@@ -21,14 +21,14 @@ import logging
import platform import platform
from sqlalchemy import orm from sqlalchemy import orm
from telethon.tl.types import Channel, ChannelForbidden, Chat, ChatForbidden, Message, \ from telethon.tl.patched import MessageService, Message
MessageActionChannelMigrateFrom, MessageService, PeerUser, TypeUpdate, \ from telethon.tl.types import (
UpdateChannelPinnedMessage, UpdateChatAdmins, UpdateChatParticipantAdmin, \ Channel, ChannelForbidden, Chat, ChatForbidden, MessageActionChannelMigrateFrom, PeerUser,
UpdateChatParticipants, UpdateChatUserTyping, UpdateDeleteChannelMessages, \ TypeUpdate, UpdateChannelPinnedMessage, UpdateChatAdmins, UpdateChatParticipantAdmin,
UpdateDeleteMessages, UpdateEditChannelMessage, UpdateEditMessage, UpdateNewChannelMessage, \ UpdateChatParticipants, UpdateChatUserTyping, UpdateDeleteChannelMessages, UpdateDeleteMessages,
UpdateNewMessage, UpdateReadHistoryOutbox, UpdateShortChatMessage, UpdateShortMessage, \ UpdateEditChannelMessage, UpdateEditMessage, UpdateNewChannelMessage, UpdateNewMessage,
UpdateUserName, UpdateUserPhoto, UpdateUserStatus, UpdateUserTyping, User, UserStatusOffline, \ UpdateReadHistoryOutbox, UpdateShortChatMessage, UpdateShortMessage, UpdateUserName,
UserStatusOnline UpdateUserPhoto, UpdateUserStatus, UpdateUserTyping, User, UserStatusOffline, UserStatusOnline)
from mautrix_appservice import MatrixRequestError, AppService from mautrix_appservice import MatrixRequestError, AppService
from alchemysession import AlchemySessionContainer from alchemysession import AlchemySessionContainer
@@ -231,7 +231,7 @@ class AbstractUser(ABC):
return return
# We check that these are user read receipts, so tg_space is always the user ID. # We check that these are user read receipts, so tg_space is always the user ID.
message = DBMessage.get_by_tgid(update.max_id, self.tgid) message = DBMessage.get_by_tgid(TelegramID(update.max_id), self.tgid)
if not message: if not message:
return return
+9 -6
View File
@@ -18,11 +18,12 @@ from typing import Awaitable, Callable, Dict, List, Optional, Pattern, TYPE_CHEC
import logging import logging
import re import re
from telethon.tl.patched import Message, MessageService
from telethon.tl.types import ( from telethon.tl.types import (
ChannelParticipantAdmin, ChannelParticipantCreator, ChatForbidden, ChatParticipantAdmin, ChannelParticipantAdmin, ChannelParticipantCreator, ChatForbidden, ChatParticipantAdmin,
ChatParticipantCreator, InputChannel, InputUser, Message, MessageActionChatAddUser, ChatParticipantCreator, InputChannel, InputUser, MessageActionChatAddUser,
MessageActionChatDeleteUser, MessageEntityBotCommand, MessageService, PeerChannel, PeerChat, MessageActionChatDeleteUser, MessageEntityBotCommand, PeerChannel, PeerChat, TypePeer,
TypePeer, UpdateNewChannelMessage, UpdateNewMessage) UpdateNewChannelMessage, UpdateNewMessage)
from telethon.tl.functions.messages import GetChatsRequest, GetFullChatRequest from telethon.tl.functions.messages import GetChatsRequest, GetFullChatRequest
from telethon.tl.functions.channels import GetChannelsRequest, GetParticipantRequest from telethon.tl.functions.channels import GetChannelsRequest, GetParticipantRequest
from telethon.errors import ChannelInvalidError, ChannelPrivateError from telethon.errors import ChannelInvalidError, ChannelPrivateError
@@ -30,6 +31,7 @@ from telethon.errors import ChannelInvalidError, ChannelPrivateError
from .types import MatrixUserID from .types import MatrixUserID
from .abstract_user import AbstractUser from .abstract_user import AbstractUser
from .db import BotChat from .db import BotChat
from .types import TelegramID
from . import puppet as pu, portal as po, user as u from . import puppet as pu, portal as po, user as u
if TYPE_CHECKING: if TYPE_CHECKING:
@@ -115,7 +117,7 @@ class Bot(AbstractUser):
def add_chat(self, chat_id: int, chat_type: str) -> None: def add_chat(self, chat_id: int, chat_type: str) -> None:
if chat_id not in self.chats: if chat_id not in self.chats:
self.chats[chat_id] = chat_type self.chats[chat_id] = chat_type
self.db.add(BotChat(id=chat_id, type=chat_type)) self.db.add(BotChat(id=TelegramID(chat_id), type=chat_type))
self.db.commit() self.db.commit()
def remove_chat(self, chat_id: int) -> None: def remove_chat(self, chat_id: int) -> None:
@@ -155,7 +157,7 @@ class Bot(AbstractUser):
return False return False
return True return True
async def handle_command_portal(self, portal: po.Portal, reply: ReplyFunc) -> None: async def handle_command_portal(self, portal: po.Portal, reply: ReplyFunc) -> Message:
if not config["bridge.relaybot.authless_portals"]: if not config["bridge.relaybot.authless_portals"]:
return await reply("This bridge doesn't allow portal creation from Telegram.") return await reply("This bridge doesn't allow portal creation from Telegram.")
@@ -221,7 +223,8 @@ class Bot(AbstractUser):
text = message.message text = message.message
if self.match_command(text, "id"): if self.match_command(text, "id"):
return await self.handle_command_id(message, reply) await self.handle_command_id(message, reply)
return
portal = po.Portal.get_by_entity(message.to_id) portal = po.Portal.get_by_entity(message.to_id)
+3 -3
View File
@@ -27,7 +27,7 @@ from telethon.tl.functions.account import UpdateUsernameRequest
from . import command_handler, CommandEvent, SECTION_AUTH from . import command_handler, CommandEvent, SECTION_AUTH
from .. import puppet as pu, user as u from .. import puppet as pu, user as u
from ..util import format_duration from ..util import format_duration, ignore_coro
@command_handler(needs_auth=False, @command_handler(needs_auth=False,
@@ -153,7 +153,7 @@ async def enter_code_register(evt: CommandEvent) -> Dict:
await evt.sender.ensure_started(even_if_no_session=True) await evt.sender.ensure_started(even_if_no_session=True)
first_name, last_name = evt.sender.command_status["full_name"] first_name, last_name = evt.sender.command_status["full_name"]
user = await evt.sender.client.sign_up(evt.args[0], first_name, last_name) user = await evt.sender.client.sign_up(evt.args[0], first_name, last_name)
asyncio.ensure_future(evt.sender.post_login(user), loop=evt.loop) ignore_coro(asyncio.ensure_future(evt.sender.post_login(user), loop=evt.loop))
evt.sender.command_status = None evt.sender.command_status = None
return await evt.reply(f"Successfully registered to Telegram.") return await evt.reply(f"Successfully registered to Telegram.")
except PhoneNumberOccupiedError: except PhoneNumberOccupiedError:
@@ -334,7 +334,7 @@ async def _sign_in(evt: CommandEvent, **sign_in_info) -> Dict:
await evt.reply(f"[{existing_user.displayname}]" await evt.reply(f"[{existing_user.displayname}]"
f"(https://matrix.to/#/{existing_user.mxid})" f"(https://matrix.to/#/{existing_user.mxid})"
" was logged out from the account.") " was logged out from the account.")
asyncio.ensure_future(evt.sender.post_login(user), loop=evt.loop) ignore_coro(asyncio.ensure_future(evt.sender.post_login(user), loop=evt.loop))
evt.sender.command_status = None evt.sender.command_status = None
name = f"@{user.username}" if user.username else f"+{user.phone}" name = f"@{user.username}" if user.username else f"+{user.phone}"
return await evt.reply(f"Successfully logged in as {name}") return await evt.reply(f"Successfully logged in as {name}")
+3 -5
View File
@@ -126,8 +126,7 @@ class CommandHandler:
(not self.needs_admin or is_admin) and (not self.needs_admin or is_admin) and
(not self.needs_auth or is_logged_in)) (not self.needs_auth or is_logged_in))
async def __call__(self, evt: CommandEvent async def __call__(self, evt: CommandEvent) -> Dict:
) -> Dict:
error = await self.get_permission_error(evt) error = await self.get_permission_error(evt)
if error is not None: if error is not None:
return await evt.reply(error) return await evt.reply(error)
@@ -154,12 +153,11 @@ def command_handler(_func: Optional[Callable[[CommandEvent], Awaitable[Dict]]] =
help_section: HelpSection = None help_section: HelpSection = None
) -> Callable[[Callable[[CommandEvent], Awaitable[Optional[Dict]]]], ) -> Callable[[Callable[[CommandEvent], Awaitable[Optional[Dict]]]],
CommandHandler]: CommandHandler]:
input_name = name
def decorator(func: Callable[[CommandEvent], Awaitable[Optional[Dict]]]) -> CommandHandler: def decorator(func: Callable[[CommandEvent], Awaitable[Optional[Dict]]]) -> CommandHandler:
name = input_name or func.__name__.replace("_", "-") actual_name = name or func.__name__.replace("_", "-")
handler = CommandHandler(func, needs_auth, needs_puppeting, needs_matrix_puppeting, handler = CommandHandler(func, needs_auth, needs_puppeting, needs_matrix_puppeting,
needs_admin, management_only, name, help_text, help_args, needs_admin, management_only, actual_name, help_text, help_args,
help_section) help_section)
command_handlers[handler.name] = handler command_handlers[handler.name] = handler
return handler return handler
+2 -2
View File
@@ -51,8 +51,8 @@ async def _get_help_text(evt: CommandEvent) -> str:
help_sections.setdefault(handler.help_section, []) help_sections.setdefault(handler.help_section, [])
help_sections[handler.help_section].append(handler.help + " ") help_sections[handler.help_section].append(handler.help + " ")
help_sorted = sorted(help_sections.items(), key=lambda item: item[0].order) help_sorted = sorted(help_sections.items(), key=lambda item: item[0].order)
help = ["#### {}\n{}\n".format(key.name, "\n".join(value)) for key, value in help_sorted] helps = ["#### {}\n{}\n".format(key.name, "\n".join(value)) for key, value in help_sorted]
help_cache[cache_key] = "\n".join(help) help_cache[cache_key] = "\n".join(helps)
return help_cache[cache_key] return help_cache[cache_key]
+5 -3
View File
@@ -25,6 +25,7 @@ from mautrix_appservice import MatrixRequestError, IntentAPI
from ..types import MatrixRoomID, TelegramID from ..types import MatrixRoomID, TelegramID
from ..config import yaml from ..config import yaml
from ..util import ignore_coro
from .. import portal as po, user as u, util from .. import portal as po, user as u, util
from . import (command_handler, CommandEvent, from . import (command_handler, CommandEvent,
SECTION_ADMIN, SECTION_CREATING_PORTALS, SECTION_PORTAL_MANAGEMENT) SECTION_ADMIN, SECTION_CREATING_PORTALS, SECTION_PORTAL_MANAGEMENT)
@@ -271,7 +272,7 @@ async def confirm_bridge(evt: CommandEvent) -> Optional[Dict]:
if not ok: if not ok:
return None return None
elif coro: elif coro:
asyncio.ensure_future(coro, loop=evt.loop) ignore_coro(asyncio.ensure_future(coro, loop=evt.loop))
await evt.reply("Cleaning up previous portal room...") await evt.reply("Cleaning up previous portal room...")
elif portal.mxid: elif portal.mxid:
evt.sender.command_status = None evt.sender.command_status = None
@@ -308,8 +309,9 @@ async def confirm_bridge(evt: CommandEvent) -> Optional[Dict]:
portal.photo_id = "" portal.photo_id = ""
portal.save() portal.save()
asyncio.ensure_future(portal.update_matrix_room(user, entity, direct, levels=levels), ignore_coro(asyncio.ensure_future(portal.update_matrix_room(user, entity, direct,
loop=evt.loop) levels=levels),
loop=evt.loop))
return await evt.reply("Bridging complete. Portal synchronization should begin momentarily.") return await evt.reply("Bridging complete. Portal synchronization should begin momentarily.")
+1 -1
View File
@@ -21,7 +21,7 @@ import re
from telethon.errors import (InviteHashInvalidError, InviteHashExpiredError, from telethon.errors import (InviteHashInvalidError, InviteHashExpiredError,
UserAlreadyParticipantError) UserAlreadyParticipantError)
from telethon.tl.types import User as TLUser, TypeUpdates, MessageMediaGame, PeerChat from telethon.tl.types import User as TLUser, TypeUpdates, MessageMediaGame
from telethon.tl.types.messages import BotCallbackAnswer from telethon.tl.types.messages import BotCallbackAnswer
from telethon.tl.functions.messages import (ImportChatInviteRequest, CheckChatInviteRequest, from telethon.tl.functions.messages import (ImportChatInviteRequest, CheckChatInviteRequest,
GetBotCallbackAnswerRequest) GetBotCallbackAnswerRequest)
+1 -1
View File
@@ -296,7 +296,7 @@ class MatrixHandler:
events = new_events - old_events events = new_events - old_events
if len(events) > 0: if len(events) > 0:
# New event pinned, set that as pinned in Telegram. # New event pinned, set that as pinned in Telegram.
await portal.handle_matrix_pin(sender, events.pop()) await portal.handle_matrix_pin(sender, MatrixEventID(events.pop()))
elif len(new_events) == 0: elif len(new_events) == 0:
# All pinned events removed, remove pinned event in Telegram. # All pinned events removed, remove pinned event in Telegram.
await portal.handle_matrix_pin(sender, None) await portal.handle_matrix_pin(sender, None)
+37 -33
View File
@@ -32,8 +32,7 @@ import re
import magic import magic
from sqlalchemy import orm from sqlalchemy import orm
from sqlalchemy.exc import IntegrityError, InvalidRequestError from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm.exc import FlushError
from telethon.tl.functions.messages import ( from telethon.tl.functions.messages import (
AddChatUserRequest, CreateChatRequest, DeleteChatUserRequest, EditChatAdminRequest, AddChatUserRequest, CreateChatRequest, DeleteChatUserRequest, EditChatAdminRequest,
@@ -46,28 +45,30 @@ from telethon.tl.functions.channels import (
from telethon.tl.functions.messages import ReadHistoryRequest as ReadMessageHistoryRequest from telethon.tl.functions.messages import ReadHistoryRequest as ReadMessageHistoryRequest
from telethon.tl.functions.channels import ReadHistoryRequest as ReadChannelHistoryRequest from telethon.tl.functions.channels import ReadHistoryRequest as ReadChannelHistoryRequest
from telethon.errors import ChatAdminRequiredError, ChatNotModifiedError from telethon.errors import ChatAdminRequiredError, ChatNotModifiedError
from telethon.tl.patched import Message, MessageService
from telethon.tl.types import ( from telethon.tl.types import (
Channel, ChannelAdminRights, ChannelBannedRights, ChannelFull, ChannelParticipantAdmin, Channel, ChannelAdminRights, ChannelBannedRights, ChannelFull, ChannelParticipantAdmin,
ChannelParticipantCreator, ChannelParticipantsRecent, ChannelParticipantsSearch, Chat, ChannelParticipantCreator, ChannelParticipantsRecent, ChannelParticipantsSearch, Chat,
ChatFull, ChatInviteEmpty, ChatParticipantAdmin, ChatParticipantCreator, ChatPhoto, ChatFull, ChatInviteEmpty, ChatParticipantAdmin, ChatParticipantCreator, ChatPhoto,
DocumentAttributeFilename, DocumentAttributeImageSize, DocumentAttributeSticker, DocumentAttributeFilename, DocumentAttributeImageSize, DocumentAttributeSticker,
DocumentAttributeVideo, FileLocation, GeoPoint, InputChannel, InputChatUploadedPhoto, DocumentAttributeVideo, FileLocation, GeoPoint, InputChannel, InputChatUploadedPhoto,
InputPeerChannel, InputPeerChat, InputPeerUser, InputUser, InputUserSelf, Message, InputPeerChannel, InputPeerChat, InputPeerUser, InputUser, InputUserSelf,
MessageActionChannelCreate, MessageActionChatAddUser, MessageActionChatCreate, MessageActionChannelCreate, MessageActionChatAddUser, MessageActionChatCreate,
MessageActionChatDeletePhoto, MessageActionChatDeleteUser, MessageActionChatEditPhoto, MessageActionChatDeletePhoto, MessageActionChatDeleteUser, MessageActionChatEditPhoto,
MessageActionChatEditTitle, MessageActionChatJoinedByLink, MessageActionChatMigrateTo, MessageActionChatEditTitle, MessageActionChatJoinedByLink, MessageActionChatMigrateTo,
MessageActionPinMessage, MessageMediaContact, MessageMediaDocument, MessageMediaGeo, MessageActionPinMessage, MessageActionGameScore, MessageMediaContact, MessageMediaDocument,
MessageMediaPhoto, MessageMediaUnsupported, MessageMediaGame, MessageService, PeerChannel, MessageMediaGeo, MessageMediaPhoto, MessageMediaUnsupported, MessageMediaGame, PeerChannel,
PeerChat, PeerUser, Photo, PhotoCachedSize, SendMessageCancelAction, SendMessageTypingAction, PeerChat, PeerUser, Photo, PhotoCachedSize, SendMessageCancelAction, SendMessageTypingAction,
TypeChannelParticipant, TypeChat, TypeChatParticipant, TypeDocumentAttribute, TypeInputPeer, TypeChannelParticipant, TypeChat, TypeChatParticipant, TypeDocumentAttribute, TypeInputPeer,
TypeMessageAction, TypeMessageEntity, TypePeer, TypePhotoSize, TypeUpdates, TypeUser, TypeMessageAction, TypeMessageEntity, TypePeer, TypePhotoSize, TypeUpdates, TypeUser,
TypeUserFull, UpdateChatUserTyping, UpdateNewChannelMessage, UpdateNewMessage, UpdateUserTyping, TypeUserFull, UpdateChatUserTyping, UpdateNewChannelMessage, UpdateNewMessage, UpdateUserTyping,
User, UserFull, MessageEntityCode) User, UserFull, MessageEntityPre)
from mautrix_appservice import MatrixRequestError, IntentError, AppService, IntentAPI from mautrix_appservice import MatrixRequestError, IntentError, AppService, IntentAPI
from .types import MatrixEventID, MatrixRoomID, MatrixUserID, TelegramID from .types import MatrixEventID, MatrixRoomID, MatrixUserID, TelegramID
from .context import Context from .context import Context
from .db import Portal as DBPortal, Message as DBMessage, TelegramFile as DBTelegramFile from .db import Portal as DBPortal, Message as DBMessage, TelegramFile as DBTelegramFile
from .util import ignore_coro
from . import puppet as p, user as u, formatter, util from . import puppet as p, user as u, formatter, util
if TYPE_CHECKING: if TYPE_CHECKING:
@@ -76,6 +77,7 @@ if TYPE_CHECKING:
from .config import Config from .config import Config
from .tgclient import MautrixTelegramClient from .tgclient import MautrixTelegramClient
mimetypes.init() mimetypes.init()
config = None # type: Config config = None # type: Config
@@ -338,7 +340,7 @@ class Portal:
if synchronous: if synchronous:
await update await update
else: else:
asyncio.ensure_future(update, loop=self.loop) ignore_coro(asyncio.ensure_future(update, loop=self.loop))
await self.invite_to_matrix(invites or []) await self.invite_to_matrix(invites or [])
return self.mxid return self.mxid
async with self._room_create_lock: async with self._room_create_lock:
@@ -405,10 +407,10 @@ class Portal:
self.save() self.save()
self.az.state_store.set_power_levels(self.mxid, power_levels) self.az.state_store.set_power_levels(self.mxid, power_levels)
user.register_portal(self) user.register_portal(self)
asyncio.ensure_future(self.update_matrix_room(user, entity, direct, puppet, ignore_coro(asyncio.ensure_future(self.update_matrix_room(user, entity, direct, puppet,
levels=power_levels, users=users, levels=power_levels, users=users,
participants=participants), participants=participants),
loop=self.loop) loop=self.loop))
return self.mxid return self.mxid
@@ -631,7 +633,8 @@ class Portal:
entity, ChannelParticipantsRecent(), offset=0, limit=limit, hash=0)) entity, ChannelParticipantsRecent(), offset=0, limit=limit, hash=0))
return response.users, response.participants return response.users, response.participants
elif limit > 200 or limit == -1: elif limit > 200 or limit == -1:
users, participants = [], [] # type: Tuple[List[TypeUser], List[TypeParticipant]] users = [] # type: List[TypeUser]
participants = [] # type: List[TypeParticipant]
offset = 0 offset = 0
remaining_quota = limit if limit > 0 else 1000000 remaining_quota = limit if limit > 0 else 1000000
query = (ChannelParticipantsSearch("") if limit == -1 query = (ChannelParticipantsSearch("") if limit == -1
@@ -886,8 +889,8 @@ class Portal:
await self._apply_msg_format(sender, msgtype, message) await self._apply_msg_format(sender, msgtype, message)
@staticmethod @staticmethod
def _matrix_event_to_entities(event: Dict[str, Any]) -> Tuple[ def _matrix_event_to_entities(event: Dict[str, Any]
str, Optional[List[TypeMessageEntity]]]: ) -> Tuple[str, Optional[List[TypeMessageEntity]]]:
try: try:
if event.get("format", None) == "org.matrix.custom.html": if event.get("format", None) == "org.matrix.custom.html":
message, entities = formatter.matrix_to_telegram(event.get("formatted_body", "")) message, entities = formatter.matrix_to_telegram(event.get("formatted_body", ""))
@@ -983,7 +986,7 @@ class Portal:
self.log.debug("Handled Matrix message: %s", response) self.log.debug("Handled Matrix message: %s", response)
self.is_duplicate(response, (event_id, space)) self.is_duplicate(response, (event_id, space))
DBMessage( DBMessage(
tgid=response.id, tgid=TelegramID(response.id),
tg_space=space, tg_space=space,
mx_room=self.mxid, mx_room=self.mxid,
mxid=event_id).insert() mxid=event_id).insert()
@@ -1415,7 +1418,7 @@ class Portal:
external_url=self.get_external_url(evt)) external_url=self.get_external_url(evt))
async def handle_telegram_unsupported(self, source: 'AbstractUser', intent: IntentAPI, async def handle_telegram_unsupported(self, source: 'AbstractUser', intent: IntentAPI,
evt: Message, relates_to: dict = None) -> dict: evt: Message, _: dict = None) -> dict:
override_text = ("This message is not supported on your version of Mautrix-Telegram. " override_text = ("This message is not supported on your version of Mautrix-Telegram. "
"Please check https://github.com/tulir/mautrix-telegram or ask your " "Please check https://github.com/tulir/mautrix-telegram or ask your "
"bridge administrator about possible updates.") "bridge administrator about possible updates.")
@@ -1433,11 +1436,11 @@ class Portal:
@staticmethod @staticmethod
def _int_to_bytes(i: int) -> bytes: def _int_to_bytes(i: int) -> bytes:
hex = "{0:010x}".format(i) hex_value = "{0:010x}".format(i)
return codecs.decode(hex, "hex_codec") return codecs.decode(hex_value, "hex_codec")
async def handle_telegram_game(self, source: 'AbstractUser', intent: IntentAPI, async def handle_telegram_game(self, source: 'AbstractUser', intent: IntentAPI,
evt: Message, relates_to: dict = None): evt: Message, _: dict = None):
game = evt.media.game game = evt.media.game
if self.peer_type == "channel": if self.peer_type == "channel":
play_id = base64.b64encode(b"c" play_id = base64.b64encode(b"c"
@@ -1457,7 +1460,7 @@ class Portal:
play_id = play_id.decode("utf-8").rstrip("=") play_id = play_id.decode("utf-8").rstrip("=")
command = f"!tg play {play_id}" command = f"!tg play {play_id}"
override_text = f"Run {command} in your bridge management room to play {game.title}" override_text = f"Run {command} in your bridge management room to play {game.title}"
override_entities = [MessageEntityCode(offset=len("Run "), length=len(command))] override_entities = [MessageEntityPre(offset=len("Run "), length=len(command), language="")]
text, html, relates_to = await formatter.telegram_to_matrix( text, html, relates_to = await formatter.telegram_to_matrix(
evt, source, self.main_intent, evt, source, self.main_intent,
override_text=override_text, override_entities=override_entities) override_text=override_text, override_entities=override_entities)
@@ -1478,6 +1481,9 @@ class Portal:
elif not self.get_config("edits_as_replies"): elif not self.get_config("edits_as_replies"):
self.log.debug("Edits as replies disabled, ignoring edit event...") self.log.debug("Edits as replies disabled, ignoring edit event...")
return return
elif hasattr(evt, "media") and isinstance(evt.media, (MessageMediaGame,)):
self.log.debug("Ignoring game message edit event")
return
lock = self.optional_send_lock(sender.tgid if sender else None) lock = self.optional_send_lock(sender.tgid if sender else None)
if lock: if lock:
@@ -1492,7 +1498,7 @@ class Portal:
if duplicate_found: if duplicate_found:
mxid, other_tg_space = duplicate_found mxid, other_tg_space = duplicate_found
if tg_space != other_tg_space: if tg_space != other_tg_space:
DBMessage.update_by_tgid(evt.id, tg_space, DBMessage.update_by_tgid(TelegramID(evt.id), tg_space,
mxid=mxid, mxid=mxid,
mx_room=self.mxid) mx_room=self.mxid)
return return
@@ -1507,7 +1513,7 @@ class Portal:
mxid = response["event_id"] mxid = response["event_id"]
msg = DBMessage.get_by_tgid(evt.id, tg_space) msg = DBMessage.get_by_tgid(TelegramID(evt.id), tg_space)
if not msg: if not msg:
self.log.info(f"Didn't find edited message {evt.id}@{tg_space} (src {source.tgid}) " self.log.info(f"Didn't find edited message {evt.id}@{tg_space} (src {source.tgid}) "
"in database.") "in database.")
@@ -1536,11 +1542,12 @@ class Portal:
self.log.debug(f"Ignoring message {evt.id}@{tg_space} (src {source.tgid}) " self.log.debug(f"Ignoring message {evt.id}@{tg_space} (src {source.tgid}) "
f"as it was already handled (in space {other_tg_space})") f"as it was already handled (in space {other_tg_space})")
if tg_space != other_tg_space: if tg_space != other_tg_space:
DBMessage(tgid=evt.id, mx_room=self.mxid, mxid=mxid, tg_space=tg_space).insert() DBMessage(tgid=TelegramID(evt.id), mx_room=self.mxid, mxid=mxid,
tg_space=tg_space).insert()
return return
if self.dedup_pre_db_check and self.peer_type == "channel": if self.dedup_pre_db_check and self.peer_type == "channel":
msg = DBMessage.get_by_tgid(evt.id, tg_space) msg = DBMessage.get_by_tgid(TelegramID(evt.id), tg_space)
if msg: if msg:
self.log.debug(f"Ignoring message {evt.id} (src {source.tgid}) as it was already" self.log.debug(f"Ignoring message {evt.id} (src {source.tgid}) as it was already"
f"handled into {msg.mxid}. This duplicate was catched in the db " f"handled into {msg.mxid}. This duplicate was catched in the db "
@@ -1593,21 +1600,15 @@ class Portal:
self.log.debug("Handled Telegram message: %s", evt) self.log.debug("Handled Telegram message: %s", evt)
try: try:
DBMessage(tgid=evt.id, mx_room=self.mxid, mxid=mxid, tg_space=tg_space).insert() DBMessage(tgid=TelegramID(evt.id), mx_room=self.mxid, mxid=mxid,
tg_space=tg_space).insert()
DBMessage.update_by_mxid(temporary_identifier, self.mxid, mxid=mxid) DBMessage.update_by_mxid(temporary_identifier, self.mxid, mxid=mxid)
except FlushError as e: except IntegrityError as e:
self.log.exception(f"{e.__class__.__name__} while saving message mapping. " self.log.exception(f"{e.__class__.__name__} while saving message mapping. "
"This might mean that an update was handled after it left the " "This might mean that an update was handled after it left the "
"dedup cache queue. You can try enabling bridge.deduplication." "dedup cache queue. You can try enabling bridge.deduplication."
"pre_db_check in the config.") "pre_db_check in the config.")
await intent.redact(self.mxid, mxid) await intent.redact(self.mxid, mxid)
except (IntegrityError, InvalidRequestError) as e:
self.log.exception(f"{e.__class__.__name__} while saving message mapping. "
"This might mean that an update was handled after it left the "
"dedup cache queue. You can try enabling bridge.deduplication."
"pre_db_check in the config.")
self.db.rollback()
await intent.redact(self.mxid, mxid)
async def _create_room_on_action(self, source: 'AbstractUser', async def _create_room_on_action(self, source: 'AbstractUser',
action: TypeMessageAction) -> bool: action: TypeMessageAction) -> bool:
@@ -1650,6 +1651,9 @@ class Portal:
await sender.intent.send_emote(self.mxid, "upgraded this group to a supergroup.") await sender.intent.send_emote(self.mxid, "upgraded this group to a supergroup.")
elif isinstance(action, MessageActionPinMessage): elif isinstance(action, MessageActionPinMessage):
await self.receive_telegram_pin_sender(sender) await self.receive_telegram_pin_sender(sender)
elif isinstance(action, MessageActionGameScore):
# TODO handle game score
pass
else: else:
self.log.debug("Unhandled Telegram action in %s: %s", self.title, action) self.log.debug("Unhandled Telegram action in %s: %s", self.title, action)
+2 -2
View File
@@ -29,13 +29,13 @@ from mautrix_appservice import AppService, IntentAPI, IntentError, MatrixRequest
from .types import MatrixUserID, TelegramID from .types import MatrixUserID, TelegramID
from .db import Puppet as DBPuppet from .db import Puppet as DBPuppet
from .util import ignore_coro
from . import util from . import util
if TYPE_CHECKING: if TYPE_CHECKING:
from .matrix import MatrixHandler from .matrix import MatrixHandler
from .config import Config from .config import Config
from .context import Context from .context import Context
from . import user as u
from .abstract_user import AbstractUser from .abstract_user import AbstractUser
PuppetError = Enum('PuppetError', 'Success OnlyLoginSelf InvalidAccessToken') PuppetError = Enum('PuppetError', 'Success OnlyLoginSelf InvalidAccessToken')
@@ -153,7 +153,7 @@ class Puppet:
return PuppetError.OnlyLoginSelf return PuppetError.OnlyLoginSelf
return PuppetError.InvalidAccessToken return PuppetError.InvalidAccessToken
if config["bridge.sync_with_custom_puppets"]: if config["bridge.sync_with_custom_puppets"]:
asyncio.ensure_future(self.sync(), loop=self.loop) ignore_coro(asyncio.ensure_future(self.sync(), loop=self.loop))
return PuppetError.Success return PuppetError.Success
async def leave_rooms_with_default_user(self) -> None: async def leave_rooms_with_default_user(self) -> None:
+1 -4
View File
@@ -16,8 +16,6 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
from typing import Dict, Tuple from typing import Dict, Tuple
from sqlalchemy import orm
from mautrix_appservice import StateStore from mautrix_appservice import StateStore
from .types import MatrixUserID, MatrixRoomID from .types import MatrixUserID, MatrixRoomID
@@ -26,9 +24,8 @@ from .db import RoomState, UserProfile
class SQLStateStore(StateStore): class SQLStateStore(StateStore):
def __init__(self, db: orm.Session) -> None: def __init__(self) -> None:
super().__init__() super().__init__()
self.db = db # type: orm.Session
self.profile_cache = {} # type: Dict[Tuple[str, str], UserProfile] self.profile_cache = {} # type: Dict[Tuple[str, str], UserProfile]
self.room_state_cache = {} # type: Dict[str, RoomState] self.room_state_cache = {} # type: Dict[str, RoomState]
+2 -2
View File
@@ -21,7 +21,7 @@ from telethon.tl.functions.messages import SendMediaRequest
from telethon.tl.types import ( from telethon.tl.types import (
InputMediaUploadedDocument, InputMediaUploadedPhoto, TypeDocumentAttribute, TypeInputMedia, InputMediaUploadedDocument, InputMediaUploadedPhoto, TypeDocumentAttribute, TypeInputMedia,
TypeInputPeer, TypeMessageEntity, TypeMessageMedia, TypePeer) TypeInputPeer, TypeMessageEntity, TypeMessageMedia, TypePeer)
from telethon.tl import custom from telethon.tl.patched import Message
class MautrixTelegramClient(TelegramClient): class MautrixTelegramClient(TelegramClient):
@@ -45,7 +45,7 @@ class MautrixTelegramClient(TelegramClient):
async def send_media(self, entity: Union[TypeInputPeer, TypePeer], async def send_media(self, entity: Union[TypeInputPeer, TypePeer],
media: Union[TypeInputMedia, TypeMessageMedia], media: Union[TypeInputMedia, TypeMessageMedia],
caption: str = None, entities: List[TypeMessageEntity] = None, caption: str = None, entities: List[TypeMessageEntity] = None,
reply_to: int = None) -> Optional[custom.Message]: reply_to: int = None) -> Optional[Message]:
entity = await self.get_input_entity(entity) entity = await self.get_input_entity(entity)
reply_to = utils.get_message_id(reply_to) reply_to = utils.get_message_id(reply_to)
request = SendMediaRequest(entity, media, message=caption or "", entities=entities or [], request = SendMediaRequest(entity, media, message=caption or "", entities=entities or [],
+1 -2
View File
@@ -21,8 +21,7 @@ import re
from telethon.tl.types import ( from telethon.tl.types import (
TypeUpdate, UpdateNewMessage, UpdateNewChannelMessage, PeerUser, TypeUpdate, UpdateNewMessage, UpdateNewChannelMessage, PeerUser,
UpdateShortChatMessage, UpdateShortMessage) UpdateShortChatMessage, UpdateShortMessage, User as TLUser)
from telethon.tl.types import User as TLUser
from telethon.tl.types.contacts import ContactsNotModified from telethon.tl.types.contacts import ContactsNotModified
from telethon.tl.functions.contacts import GetContactsRequest, SearchRequest from telethon.tl.functions.contacts import GetContactsRequest, SearchRequest
from telethon.tl.functions.account import UpdateStatusRequest from telethon.tl.functions.account import UpdateStatusRequest
+4
View File
@@ -2,3 +2,7 @@ from .file_transfer import transfer_file_to_matrix, convert_image
from .format_duration import format_duration from .format_duration import format_duration
from .signed_token import sign_token, verify_token from .signed_token import sign_token, verify_token
from .recursive_dict import recursive_del, recursive_set, recursive_get from .recursive_dict import recursive_del, recursive_set, recursive_get
def ignore_coro(coro):
pass
-1
View File
@@ -21,7 +21,6 @@ import logging
import asyncio import asyncio
import magic import magic
from sqlalchemy import orm
from sqlalchemy.exc import IntegrityError, InvalidRequestError from sqlalchemy.exc import IntegrityError, InvalidRequestError
from telethon.tl.types import (Document, FileLocation, InputFileLocation, from telethon.tl.types import (Document, FileLocation, InputFileLocation,
+2 -2
View File
@@ -25,7 +25,7 @@ import logging
from telethon.errors import * from telethon.errors import *
from ...commands.auth import enter_password from ...commands.auth import enter_password
from ...util import format_duration from ...util import format_duration, ignore_coro
from ...puppet import Puppet, PuppetError from ...puppet import Puppet, PuppetError
from ...user import User from ...user import User
@@ -112,7 +112,7 @@ class AuthAPI(abc.ABC):
existing_user = User.get_by_tgid(user_info.id) existing_user = User.get_by_tgid(user_info.id)
if existing_user and existing_user != user: if existing_user and existing_user != user:
await existing_user.log_out() await existing_user.log_out()
asyncio.ensure_future(user.post_login(user_info), loop=self.loop) ignore_coro(asyncio.ensure_future(user.post_login(user_info), loop=self.loop))
if user.command_status and user.command_status["action"] == "Login": if user.command_status and user.command_status["action"] == "Login":
user.command_status = None user.command_status = None
@@ -27,6 +27,7 @@ from mautrix_appservice import AppService, MatrixRequestError, IntentError
from ...types import MatrixUserID, TelegramID from ...types import MatrixUserID, TelegramID
from ...user import User from ...user import User
from ...portal import Portal from ...portal import Portal
from ...util import ignore_coro
from ...commands.portal import user_has_power_level, get_initial_state from ...commands.portal import user_has_power_level, get_initial_state
from ..common import AuthAPI from ..common import AuthAPI
@@ -190,8 +191,9 @@ class ProvisioningAPI(AuthAPI):
portal.photo_id = "" portal.photo_id = ""
portal.save() portal.save()
asyncio.ensure_future(portal.update_matrix_room(user, entity, direct, levels=levels), ignore_coro(asyncio.ensure_future(portal.update_matrix_room(user, entity, direct,
loop=self.loop) levels=levels),
loop=self.loop))
return web.Response(status=202, body="{}") return web.Response(status=202, body="{}")
@@ -285,7 +287,7 @@ class ProvisioningAPI(AuthAPI):
self.log.exception("Failed to disconnect chat") self.log.exception("Failed to disconnect chat")
return self.get_error_response(500, "exception", "Failed to disconnect chat") return self.get_error_response(500, "exception", "Failed to disconnect chat")
else: else:
asyncio.ensure_future(coro, loop=self.loop) ignore_coro(asyncio.ensure_future(coro, loop=self.loop))
return web.json_response({}, status=200 if sync else 202) return web.json_response({}, status=200 if sync else 202)
async def get_user_info(self, request: web.Request) -> web.Response: async def get_user_info(self, request: web.Request) -> web.Response: