diff --git a/mautrix_telegram/abstract_user.py b/mautrix_telegram/abstract_user.py index f1a72db4..300a4e29 100644 --- a/mautrix_telegram/abstract_user.py +++ b/mautrix_telegram/abstract_user.py @@ -60,7 +60,7 @@ class AbstractUser(ABC): bot = None # type: Bot ignore_incoming_bot_events = True # type: bool - def __init__(self): + def __init__(self) -> None: self.is_admin = False # type: bool self.matrix_puppet_whitelisted = False # type: bool self.puppet_whitelisted = False # type: bool @@ -93,7 +93,7 @@ class AbstractUser(ABC): config["telegram.proxy.rdns"], config["telegram.proxy.username"], config["telegram.proxy.password"]) - def _init_client(self): + def _init_client(self) -> None: self.log.debug(f"Initializing client for {self.name}") device = f"{platform.system()} {platform.release()}" sysversion = MautrixTelegramClient.__version__ @@ -114,18 +114,18 @@ class AbstractUser(ABC): return False @abstractmethod - async def post_login(self): + async def post_login(self) -> None: raise NotImplementedError() @abstractmethod - def register_portal(self, portal: po.Portal): + def register_portal(self, portal: po.Portal) -> None: raise NotImplementedError() @abstractmethod - def unregister_portal(self, portal: po.Portal): + def unregister_portal(self, portal: po.Portal) -> None: raise NotImplementedError() - async def _update_catch(self, update: TypeUpdate): + async def _update_catch(self, update: TypeUpdate) -> None: try: if not await self.update(update): await self._update(update) @@ -175,13 +175,13 @@ class AbstractUser(ABC): await self.start(delete_unless_authenticated=not even_if_no_session) return self - async def stop(self): + async def stop(self) -> None: await self.client.disconnect() self.client = None # region Telegram update handling - async def _update(self, update: TypeUpdate): + async def _update(self, update: TypeUpdate) -> None: if isinstance(update, (UpdateShortChatMessage, UpdateShortMessage, UpdateNewChannelMessage, UpdateNewMessage, UpdateEditMessage, UpdateEditChannelMessage)): await self.update_message(update) @@ -207,18 +207,18 @@ class AbstractUser(ABC): self.log.debug("Unhandled update: %s", update) @staticmethod - async def update_pinned_messages(update: UpdateChannelPinnedMessage): + async def update_pinned_messages(update: UpdateChannelPinnedMessage) -> None: portal = po.Portal.get_by_tgid(update.channel_id) if portal and portal.mxid: await portal.receive_telegram_pin_id(update.id) @staticmethod - async def update_participants(update: UpdateChatParticipants): + async def update_participants(update: UpdateChatParticipants) -> None: portal = po.Portal.get_by_tgid(update.participants.chat_id) if portal and portal.mxid: await portal.update_telegram_participants(update.participants.participants) - async def update_read_receipt(self, update: UpdateReadHistoryOutbox): + async def update_read_receipt(self, update: UpdateReadHistoryOutbox) -> None: if not isinstance(update.peer, PeerUser): self.log.debug("Unexpected read receipt peer: %s", update.peer) return @@ -235,7 +235,8 @@ class AbstractUser(ABC): puppet = pu.Puppet.get(update.peer.user_id) await puppet.intent.mark_read(portal.mxid, message.mxid) - async def update_admin(self, update: Union[UpdateChatAdmins, UpdateChatParticipantAdmin]): + async def update_admin(self, + update: Union[UpdateChatAdmins, UpdateChatParticipantAdmin]) -> None: # TODO duplication not checked portal = po.Portal.get_by_tgid(update.chat_id, peer_type="chat") if isinstance(update, UpdateChatAdmins): @@ -245,7 +246,7 @@ class AbstractUser(ABC): else: self.log.warning("Unexpected admin status update: %s", update) - async def update_typing(self, update: Union[UpdateUserTyping, UpdateChatUserTyping]): + async def update_typing(self, update: Union[UpdateUserTyping, UpdateChatUserTyping]) -> None: if isinstance(update, UpdateUserTyping): portal = po.Portal.get_by_tgid(update.user_id, self.tgid, "user") else: @@ -253,7 +254,7 @@ class AbstractUser(ABC): sender = pu.Puppet.get(update.user_id) await portal.handle_telegram_typing(sender, update) - async def update_others_info(self, update: Union[UpdateUserName, UpdateUserPhoto]): + async def update_others_info(self, update: Union[UpdateUserName, UpdateUserPhoto]) -> None: # TODO duplication not checked puppet = pu.Puppet.get(update.user_id) if isinstance(update, UpdateUserName): @@ -265,7 +266,7 @@ class AbstractUser(ABC): else: self.log.warning("Unexpected other user info update: %s", update) - async def update_status(self, update: UpdateUserStatus): + async def update_status(self, update: UpdateUserStatus) -> None: puppet = pu.Puppet.get(update.user_id) if isinstance(update.status, UserStatusOnline): await puppet.default_mxid_intent.set_presence("online") @@ -300,7 +301,7 @@ class AbstractUser(ABC): return update, sender, portal @staticmethod - async def _try_redact(portal: po.Portal, message: DBMessage): + async def _try_redact(portal: po.Portal, message: DBMessage) -> None: if not portal: return try: @@ -308,7 +309,7 @@ class AbstractUser(ABC): except MatrixRequestError: pass - async def delete_message(self, update: UpdateDeleteMessages): + async def delete_message(self, update: UpdateDeleteMessages) -> None: if len(update.messages) > MAX_DELETIONS: return @@ -324,7 +325,7 @@ class AbstractUser(ABC): await self._try_redact(portal, message) self.db.commit() - async def delete_channel_message(self, update: UpdateDeleteChannelMessages): + async def delete_channel_message(self, update: UpdateDeleteChannelMessages) -> None: if len(update.messages) > MAX_DELETIONS: return @@ -340,7 +341,7 @@ class AbstractUser(ABC): await self._try_redact(portal, message) self.db.commit() - async def update_message(self, original_update: UpdateMessage): + async def update_message(self, original_update: UpdateMessage) -> None: update, sender, portal = self.get_message_details(original_update) if self.ignore_incoming_bot_events and self.bot and sender.id == self.bot.tgid: self.log.debug(f"Ignoring relaybot-sent message %s to %s", update, portal.tgid_log) @@ -369,7 +370,7 @@ class AbstractUser(ABC): # endregion -def init(context: "Context"): +def init(context: "Context") -> None: global config, MAX_DELETIONS AbstractUser.az, AbstractUser.db, config, AbstractUser.loop, AbstractUser.relaybot = context AbstractUser.ignore_incoming_bot_events = config["bridge.relaybot.ignore_own_incoming_events"] diff --git a/mautrix_telegram/bot.py b/mautrix_telegram/bot.py index 51a6a110..2211e4ff 100644 --- a/mautrix_telegram/bot.py +++ b/mautrix_telegram/bot.py @@ -39,7 +39,7 @@ class Bot(AbstractUser): log = logging.getLogger("mau.bot") # type: logging.Logger mxid_regex = re.compile("@.+:.+") # type: Pattern - def __init__(self, token: str): + def __init__(self, token: str) -> None: super().__init__() self.token = token # type: str self.puppet_whitelisted = True # type: bool @@ -53,7 +53,7 @@ class Bot(AbstractUser): self.whitelist_group_admins = (config["bridge.relaybot.whitelist_group_admins"] or False) # type: bool - async def init_permissions(self): + async def init_permissions(self) -> None: whitelist = config["bridge.relaybot.whitelist"] or [] for id in whitelist: if isinstance(id, str): @@ -72,7 +72,7 @@ class Bot(AbstractUser): await self.post_login() return self - async def post_login(self): + async def post_login(self) -> None: await self.init_permissions() info = await self.client.get_me() self.tgid = info.id @@ -100,19 +100,19 @@ class Bot(AbstractUser): except Exception: self.log.exception("Failed to run catch_up() for bot") - def register_portal(self, portal: po.Portal): + def register_portal(self, portal: po.Portal) -> None: self.add_chat(portal.tgid, portal.peer_type) - def unregister_portal(self, portal: po.Portal): + def unregister_portal(self, portal: po.Portal) -> None: self.remove_chat(portal.tgid) - def add_chat(self, id: int, type: str): + def add_chat(self, id: int, type: str) -> None: if id not in self.chats: self.chats[id] = type self.db.add(BotChat(id=id, type=type)) self.db.commit() - def remove_chat(self, id: int): + def remove_chat(self, id: int) -> None: try: del self.chats[id] except KeyError: @@ -148,7 +148,7 @@ class Bot(AbstractUser): return False return True - async def handle_command_portal(self, portal: po.Portal, reply: ReplyFunc): + async def handle_command_portal(self, portal: po.Portal, reply: ReplyFunc) -> None: if not config["bridge.relaybot.authless_portals"]: return await reply("This bridge doesn't allow portal creation from Telegram.") @@ -164,7 +164,7 @@ class Bot(AbstractUser): return await reply( "Portal is not public. Use `/invite ` to get an invite.") - async def handle_command_invite(self, portal: po.Portal, reply: ReplyFunc, mxid: str): + async def handle_command_invite(self, portal: po.Portal, reply: ReplyFunc, mxid: str) -> None: if len(mxid) == 0: return await reply("Usage: `/invite `") elif not portal.mxid: @@ -183,7 +183,7 @@ class Bot(AbstractUser): await portal.main_intent.invite(portal.mxid, user.mxid) return await reply(f"Invited `{user.mxid}` to the portal.") - def handle_command_id(self, message: Message, reply: ReplyFunc): + def handle_command_id(self, message: Message, reply: ReplyFunc) -> None: # Provide the prefixed ID to the user so that the user wouldn't need to specify whether the # chat is a normal group or a supergroup/channel when using the ID. if isinstance(message.to_id, PeerChannel): @@ -205,8 +205,8 @@ class Bot(AbstractUser): return False - async def handle_command(self, message: Message): - def reply(reply_text): + async def handle_command(self, message: Message) -> None: + def reply(reply_text) -> None: return self.client.send_message(message.to_id, reply_text, reply_to=message.id) text = message.message @@ -229,7 +229,7 @@ class Bot(AbstractUser): mxid = "" await self.handle_command_invite(portal, reply, mxid=mxid) - def handle_service_message(self, message: MessageService): + def handle_service_message(self, message: MessageService) -> None: to_id = message.to_id if isinstance(to_id, PeerChannel): to_id = to_id.channel_id @@ -246,7 +246,7 @@ class Bot(AbstractUser): elif isinstance(action, MessageActionChatDeleteUser) and action.user_id == self.tgid: self.remove_chat(to_id) - async def update(self, update): + async def update(self, update) -> None: if not isinstance(update, (UpdateNewMessage, UpdateNewChannelMessage)): return if isinstance(update.message, MessageService): diff --git a/mautrix_telegram/commands/auth.py b/mautrix_telegram/commands/auth.py index 38b1fbe2..79278171 100644 --- a/mautrix_telegram/commands/auth.py +++ b/mautrix_telegram/commands/auth.py @@ -27,7 +27,7 @@ from ..util import format_duration @command_handler(needs_auth=False, help_section=SECTION_AUTH, help_text="Check if you're logged into Telegram.") -async def ping(evt: CommandEvent): +async def ping(evt: CommandEvent) -> None: me = await evt.sender.client.get_me() if await evt.sender.is_logged_in() else None if me: return await evt.reply(f"You're logged in as @{me.username}") @@ -38,7 +38,7 @@ async def ping(evt: CommandEvent): @command_handler(needs_auth=False, needs_puppeting=False, help_section=SECTION_AUTH, help_text="Get the info of the message relay Telegram bot.") -async def ping_bot(evt: CommandEvent): +async def ping_bot(evt: CommandEvent) -> None: if not evt.tgbot: return await evt.reply("Telegram message relay bot not configured.") bot_info = await evt.tgbot.client.get_me() @@ -53,7 +53,7 @@ async def ping_bot(evt: CommandEvent): help_section=SECTION_AUTH, help_text="Revert your Telegram account's Matrix puppet to use the default Matrix " "account.") -async def logout_matrix(evt: CommandEvent): +async def logout_matrix(evt: CommandEvent) -> None: puppet = pu.Puppet.get(evt.sender.tgid) if not puppet.is_real_user: return await evt.reply("You are not logged in with your Matrix account.") @@ -65,7 +65,7 @@ async def logout_matrix(evt: CommandEvent): help_section=SECTION_AUTH, help_text="Replace your Telegram account's Matrix puppet with your own Matrix " "account") -async def login_matrix(evt: CommandEvent): +async def login_matrix(evt: CommandEvent) -> None: puppet = pu.Puppet.get(evt.sender.tgid) if puppet.is_real_user: return await evt.reply("You have already logged in with your Matrix account. " @@ -96,7 +96,7 @@ async def login_matrix(evt: CommandEvent): return await evt.reply("This bridge instance has been configured to not allow logging in.") -async def enter_matrix_token(evt: CommandEvent): +async def enter_matrix_token(evt: CommandEvent) -> None: evt.sender.command_status = None puppet = pu.Puppet.get(evt.sender.tgid) @@ -117,7 +117,7 @@ async def enter_matrix_token(evt: CommandEvent): help_section=SECTION_AUTH, help_args="<_phone_> <_full name_>", help_text="Register to Telegram") -async def register(evt: CommandEvent): +async def register(evt: CommandEvent) -> None: if await evt.sender.is_logged_in(): return await evt.reply("You are already logged in.") elif len(evt.args) < 1: @@ -136,7 +136,7 @@ async def register(evt: CommandEvent): }) -async def enter_code_register(evt: CommandEvent): +async def enter_code_register(evt: CommandEvent) -> None: if len(evt.args) == 0: return await evt.reply("**Usage:** `$cmdprefix+sp `") try: @@ -165,7 +165,7 @@ async def enter_code_register(evt: CommandEvent): @command_handler(needs_auth=False, management_only=True, help_section=SECTION_AUTH, help_text="Get instructions on how to log in.") -async def login(evt: CommandEvent): +async def login(evt: CommandEvent) -> None: if await evt.sender.is_logged_in(): return await evt.reply("You are already logged in.") @@ -196,7 +196,7 @@ async def login(evt: CommandEvent): return await evt.reply("This bridge instance has been configured to not allow logging in.") -async def request_code(evt: CommandEvent, phone_number: str, next_status: Dict[str, str]): +async def request_code(evt: CommandEvent, phone_number: str, next_status: Dict[str, str]) -> None: ok = False try: await evt.sender.ensure_started(even_if_no_session=True) @@ -228,7 +228,7 @@ async def request_code(evt: CommandEvent, phone_number: str, next_status: Dict[s @command_handler(needs_auth=False) -async def enter_phone_or_token(evt: CommandEvent): +async def enter_phone_or_token(evt: CommandEvent) -> None: if len(evt.args) == 0: return await evt.reply("**Usage:** `$cmdprefix+sp enter-phone-or-token `") elif not evt.config.get("bridge.allow_matrix_login", True): @@ -251,7 +251,7 @@ async def enter_phone_or_token(evt: CommandEvent): @command_handler(needs_auth=False) -async def enter_code(evt: CommandEvent): +async def enter_code(evt: CommandEvent) -> None: if len(evt.args) == 0: return await evt.reply("**Usage:** `$cmdprefix+sp enter-code `") elif not evt.config.get("bridge.allow_matrix_login", True): @@ -266,7 +266,7 @@ async def enter_code(evt: CommandEvent): @command_handler(needs_auth=False) -async def enter_password(evt: CommandEvent): +async def enter_password(evt: CommandEvent) -> None: if len(evt.args) == 0: return await evt.reply("**Usage:** `$cmdprefix+sp enter-password `") elif not evt.config.get("bridge.allow_matrix_login", True): @@ -284,7 +284,7 @@ async def enter_password(evt: CommandEvent): "Check console for more details.") -async def sign_in(evt: CommandEvent, **sign_in_info): +async def sign_in(evt: CommandEvent, **sign_in_info) -> None: try: await evt.sender.ensure_started(even_if_no_session=True) user = await evt.sender.client.sign_in(**sign_in_info) @@ -309,7 +309,7 @@ async def sign_in(evt: CommandEvent, **sign_in_info): @command_handler(needs_auth=True, help_section=SECTION_AUTH, help_text="Log out from Telegram.") -async def logout(evt: CommandEvent): +async def logout(evt: CommandEvent) -> None: if await evt.sender.log_out(): return await evt.reply("Logged out successfully.") return await evt.reply("Failed to log out.") diff --git a/mautrix_telegram/commands/clean_rooms.py b/mautrix_telegram/commands/clean_rooms.py index aac5a54d..dce36902 100644 --- a/mautrix_telegram/commands/clean_rooms.py +++ b/mautrix_telegram/commands/clean_rooms.py @@ -61,7 +61,7 @@ async def _find_rooms(intent: IntentAPI) -> Tuple[ManagementRoomList, RoomIDList @command_handler(needs_admin=True, needs_auth=False, management_only=True, name="clean-rooms", help_section=SECTION_ADMIN, help_text="Clean up unused portal/management rooms.") -async def clean_rooms(evt: CommandEvent): +async def clean_rooms(evt: CommandEvent) -> None: management_rooms, unidentified_rooms, portals, empty_portals = await _find_rooms(evt.az.intent) reply = ["#### Management rooms (M)"] @@ -108,7 +108,7 @@ async def clean_rooms(evt: CommandEvent): async def set_rooms_to_clean(evt, management_rooms: ManagementRoomList, unidentified_rooms: RoomIDList, portals: List["po.Portal"], - empty_portals: List["po.Portal"]): + empty_portals: List["po.Portal"]) -> None: command = evt.args[0] rooms_to_clean = [] if command == "clean-recommended": @@ -158,7 +158,7 @@ async def set_rooms_to_clean(evt, management_rooms: ManagementRoomList, "`$cmdprefix+sp confirm-clean`.") -async def execute_room_cleanup(evt, rooms_to_clean): +async def execute_room_cleanup(evt, rooms_to_clean) -> None: if len(evt.args) > 0 and evt.args[0] == "confirm-clean": await evt.reply(f"Cleaning {len(rooms_to_clean)} rooms. " "This might take a while.") diff --git a/mautrix_telegram/commands/handler.py b/mautrix_telegram/commands/handler.py index c7d2b1c2..2c28bef6 100644 --- a/mautrix_telegram/commands/handler.py +++ b/mautrix_telegram/commands/handler.py @@ -38,7 +38,7 @@ SECTION_ADMIN = HelpSection("Administration", 50, "") class CommandEvent: def __init__(self, processor: "CommandProcessor", room: str, sender: u.User, command: str, - args: List[str], is_management: bool, is_portal: bool): + args: List[str], is_management: bool, is_portal: bool) -> None: self.az = processor.az self.log = processor.log self.loop = processor.loop @@ -53,7 +53,7 @@ class CommandEvent: self.is_management = is_management self.is_portal = is_portal - def reply(self, message: str, allow_html: bool = False, render_markdown: bool = True): + def reply(self, message: str, allow_html: bool = False, render_markdown: bool = True) -> None: message = message.replace("$cmdprefix+sp ", "" if self.is_management else f"{self.command_prefix} ") message = message.replace("$cmdprefix", self.command_prefix) @@ -69,7 +69,7 @@ class CommandHandler: def __init__(self, handler: Callable[[CommandEvent], None], needs_auth: bool, needs_puppeting: bool, needs_matrix_puppeting: bool, needs_admin: bool, management_only: bool, name: str, help_text: str, help_args: str, - help_section: HelpSection): + help_section: HelpSection) -> None: self._handler = handler self.needs_auth = needs_auth self.needs_puppeting = needs_puppeting @@ -103,7 +103,7 @@ class CommandHandler: (not self.needs_admin or is_admin) and (not self.needs_auth or is_logged_in)) - async def __call__(self, evt: CommandEvent): + async def __call__(self, evt: CommandEvent) -> None: error = await self.get_permission_error(evt) if error is not None: return await evt.reply(error) @@ -121,10 +121,10 @@ class CommandHandler: def command_handler(_func: Optional[Callable[[CommandEvent], None]] = None, *, needs_auth=True, needs_puppeting=True, needs_matrix_puppeting=False, needs_admin=False, management_only=False, name=None, help_text="", help_args="", - help_section=None): + help_section=None) -> None: input_name = name - def decorator(func: Callable[[CommandEvent], None]): + def decorator(func: Callable[[CommandEvent], None]) -> None: name = input_name or func.__name__.replace("_", "-") handler = CommandHandler(func, needs_auth, needs_puppeting, needs_matrix_puppeting, needs_admin, management_only, name, help_text, help_args, @@ -138,13 +138,13 @@ def command_handler(_func: Optional[Callable[[CommandEvent], None]] = None, *, n class CommandProcessor: log = logging.getLogger("mau.commands") - def __init__(self, context: c.Context): + def __init__(self, context: c.Context) -> None: self.az, self.db, self.config, self.loop, self.tgbot = context self.public_website = context.public_website self.command_prefix = self.config["bridge.command_prefix"] async def handle(self, room: str, sender: u.User, command: str, args: List[str], - is_management: bool, is_portal: bool): + is_management: bool, is_portal: bool) -> None: evt = CommandEvent(self, room, sender, command, args, is_management, is_portal) orig_command = command command = command.lower() diff --git a/mautrix_telegram/commands/meta.py b/mautrix_telegram/commands/meta.py index f50a83e0..8478a636 100644 --- a/mautrix_telegram/commands/meta.py +++ b/mautrix_telegram/commands/meta.py @@ -20,7 +20,7 @@ from . import command_handler, CommandEvent, _command_handlers, SECTION_GENERAL @command_handler(needs_auth=False, needs_puppeting=False, help_section=SECTION_GENERAL, help_text="Cancel an ongoing action (such as login)") -def cancel(evt: CommandEvent): +def cancel(evt: CommandEvent) -> None: if evt.sender.command_status: action = evt.sender.command_status["action"] evt.sender.command_status = None @@ -30,14 +30,14 @@ def cancel(evt: CommandEvent): @command_handler(needs_auth=False, needs_puppeting=False) -def unknown_command(evt: CommandEvent): +def unknown_command(evt: CommandEvent) -> None: return evt.reply("Unknown command. Try `$cmdprefix+sp help` for help.") help_cache = {} -async def _get_help_text(evt: CommandEvent): +async def _get_help_text(evt: CommandEvent) -> None: cache_key = (evt.is_management, evt.sender.puppet_whitelisted, evt.sender.matrix_puppet_whitelisted, evt.sender.is_admin, await evt.sender.is_logged_in()) @@ -53,7 +53,7 @@ async def _get_help_text(evt: CommandEvent): return help_cache[cache_key] -def _get_management_status(evt: CommandEvent): +def _get_management_status(evt: CommandEvent) -> None: if evt.is_management: return "This is a management room: prefixing commands with `$cmdprefix` is not required." elif evt.is_portal: @@ -65,5 +65,5 @@ def _get_management_status(evt: CommandEvent): @command_handler(needs_auth=False, needs_puppeting=False, help_section=SECTION_GENERAL, help_text="Show this help message.") -async def help(evt: CommandEvent): +async def help(evt: CommandEvent) -> None: return await evt.reply(_get_management_status(evt) + "\n" + await _get_help_text(evt)) diff --git a/mautrix_telegram/commands/portal.py b/mautrix_telegram/commands/portal.py index c2ff2347..69aaf957 100644 --- a/mautrix_telegram/commands/portal.py +++ b/mautrix_telegram/commands/portal.py @@ -30,7 +30,7 @@ from . import (command_handler, CommandEvent, help_section=SECTION_ADMIN, help_args="<_level_> [_mxid_]", help_text="Set a temporary power level without affecting Telegram.") -async def set_power_level(evt: CommandEvent): +async def set_power_level(evt: CommandEvent) -> None: try: level = int(evt.args[0]) except KeyError: @@ -49,7 +49,7 @@ async def set_power_level(evt: CommandEvent): @command_handler(help_section=SECTION_PORTAL_MANAGEMENT, help_text="Get a Telegram invite link to the current chat.") -async def invite_link(evt: CommandEvent): +async def invite_link(evt: CommandEvent) -> None: portal = po.Portal.get_by_mxid(evt.room_id) if not portal: return await evt.reply("This is not a portal room.") @@ -66,7 +66,8 @@ async def invite_link(evt: CommandEvent): return await evt.reply("You don't have the permission to create an invite link.") -async def user_has_power_level(room: str, intent, sender: u.User, event: str, default: int = 50): +async def user_has_power_level(room: str, intent, sender: u.User, event: str, default: int = 50 + ) -> None: if sender.is_admin: return True # Make sure the state store contains the power levels. @@ -80,7 +81,7 @@ async def user_has_power_level(room: str, intent, sender: u.User, event: str, de async def _get_portal_and_check_permission(evt: CommandEvent, permission: str, - action: Optional[str] = None): + action: Optional[str] = None) -> None: room_id = evt.args[0] if len(evt.args) > 0 else evt.room_id portal = po.Portal.get_by_mxid(room_id) @@ -95,8 +96,8 @@ async def _get_portal_and_check_permission(evt: CommandEvent, permission: str, def _get_portal_murder_function(action: str, room_id: str, function: Callable, command: str, - completed_message: str): - async def post_confirm(confirm): + completed_message: str) -> None: + async def post_confirm(confirm) -> None: confirm.sender.command_status = None if len(confirm.args) > 0 and confirm.args[0] == f"confirm-{command}": await function() @@ -116,7 +117,7 @@ def _get_portal_murder_function(action: str, room_id: str, function: Callable, c help_text="Remove all users from the current portal room and forget the portal. " "Only works for group chats; to delete a private chat portal, simply " "leave the room.") -async def delete_portal(evt: CommandEvent): +async def delete_portal(evt: CommandEvent) -> None: portal, ok = await _get_portal_and_check_permission(evt, "unbridge") if not ok: return @@ -137,7 +138,7 @@ async def delete_portal(evt: CommandEvent): @command_handler(needs_auth=False, needs_puppeting=False, help_section=SECTION_PORTAL_MANAGEMENT, help_text="Remove puppets from the current portal room and forget the portal.") -async def unbridge(evt: CommandEvent): +async def unbridge(evt: CommandEvent) -> None: portal, ok = await _get_portal_and_check_permission(evt, "unbridge") if not ok: return @@ -156,7 +157,7 @@ async def unbridge(evt: CommandEvent): help_text="Bridge the current Matrix room to the Telegram chat with the given " "ID. The ID must be the prefixed version that you get with the `/id` " "command of the Telegram-side bot.") -async def bridge(evt: CommandEvent): +async def bridge(evt: CommandEvent) -> None: if len(evt.args) == 0: return await evt.reply("**Usage:** " "`$cmdprefix+sp bridge [Matrix room ID]`") @@ -222,7 +223,7 @@ async def bridge(evt: CommandEvent): "chat to this room, use `$cmdprefix+sp continue`") -async def cleanup_old_portal_while_bridging(evt: CommandEvent, portal: "po.Portal"): +async def cleanup_old_portal_while_bridging(evt: CommandEvent, portal: "po.Portal") -> None: if not portal.mxid: await evt.reply("The portal seems to have lost its Matrix room between you" "calling `$cmdprefix+sp bridge` and this command.\n\n" @@ -245,7 +246,7 @@ async def cleanup_old_portal_while_bridging(evt: CommandEvent, portal: "po.Porta return False, None -async def confirm_bridge(evt: CommandEvent): +async def confirm_bridge(evt: CommandEvent) -> None: status = evt.sender.command_status try: portal = po.Portal.get_by_tgid(status["tgid"], peer_type=status["peer_type"]) @@ -302,7 +303,7 @@ async def confirm_bridge(evt: CommandEvent): return await evt.reply("Bridging complete. Portal synchronization should begin momentarily.") -async def get_initial_state(intent: IntentAPI, room_id: str): +async def get_initial_state(intent: IntentAPI, room_id: str) -> None: state = await intent.get_room_state(room_id) title = None about = None @@ -328,7 +329,7 @@ async def get_initial_state(intent: IntentAPI, room_id: str): help_text="Create a Telegram chat of the given type for the current Matrix room. " "The type is either `group`, `supergroup` or `channel` (defaults to " "`group`).") -async def create(evt: CommandEvent): +async def create(evt: CommandEvent) -> None: type = evt.args[0] if len(evt.args) > 0 else "group" if type not in {"chat", "group", "supergroup", "channel"}: return await evt.reply( @@ -363,7 +364,7 @@ async def create(evt: CommandEvent): @command_handler(help_section=SECTION_PORTAL_MANAGEMENT, help_text="Upgrade a normal Telegram group to a supergroup.") -async def upgrade(evt: CommandEvent): +async def upgrade(evt: CommandEvent) -> None: portal = po.Portal.get_by_mxid(evt.room_id) if not portal: return await evt.reply("This is not a portal room.") @@ -385,7 +386,7 @@ async def upgrade(evt: CommandEvent): help_args="<_name_|`-`>", help_text="Change the username of a supergroup/channel. " "To disable, use a dash (`-`) as the name.") -async def group_name(evt: CommandEvent): +async def group_name(evt: CommandEvent) -> None: if len(evt.args) == 0: return await evt.reply("**Usage:** `$cmdprefix+sp group-name `") @@ -421,7 +422,7 @@ async def group_name(evt: CommandEvent): help_args="<`whitelist`|`blacklist`>", help_text="Change whether the bridge will allow or disallow bridging rooms by " "default.") -async def filter_mode(evt: CommandEvent): +async def filter_mode(evt: CommandEvent) -> None: try: mode = evt.args[0] if mode not in ("whitelist", "blacklist"): @@ -446,7 +447,7 @@ async def filter_mode(evt: CommandEvent): help_section=SECTION_ADMIN, help_args="<`whitelist`|`blacklist`> <_chat ID_>", help_text="Allow or disallow bridging a specific chat.") -async def filter(evt: CommandEvent): +async def filter(evt: CommandEvent) -> None: try: action = evt.args[0] if action not in ("whitelist", "blacklist", "add", "remove"): @@ -471,7 +472,7 @@ async def filter(evt: CommandEvent): if action in ("blacklist", "whitelist"): action = "add" if mode == action else "remove" - def save(): + def save() -> None: evt.config["bridge.filter.list"] = list evt.config.save() po.Portal.filter_list = list diff --git a/mautrix_telegram/commands/telegram.py b/mautrix_telegram/commands/telegram.py index 75221c21..ea616d5a 100644 --- a/mautrix_telegram/commands/telegram.py +++ b/mautrix_telegram/commands/telegram.py @@ -26,7 +26,7 @@ from . import command_handler, CommandEvent, SECTION_MISC, SECTION_CREATING_PORT @command_handler(help_section=SECTION_MISC, help_args="[_-r|--remote_] <_query_>", help_text="Search your contacts or the Telegram servers for users.") -async def search(evt: CommandEvent): +async def search(evt: CommandEvent) -> None: if len(evt.args) == 0: return await evt.reply("**Usage:** `$cmdprefix+sp search [-r|--remote] `") @@ -68,7 +68,7 @@ async def search(evt: CommandEvent): "either the internal user ID, the username or the phone number. " "**N.B.** The phone numbers you start chats with must already be in " "your contacts.") -async def private_message(evt: CommandEvent): +async def private_message(evt: CommandEvent) -> None: if len(evt.args) == 0: return await evt.reply("**Usage:** `$cmdprefix+sp pm `") @@ -87,7 +87,7 @@ async def private_message(evt: CommandEvent): f"{pu.Puppet.get_displayname(user, False)}") -async def _join(evt: CommandEvent, arg: str): +async def _join(evt: CommandEvent, arg: str) -> None: if arg.startswith("joinchat/"): invite_hash = arg[len("joinchat/"):] try: @@ -110,7 +110,7 @@ async def _join(evt: CommandEvent, arg: str): @command_handler(help_section=SECTION_CREATING_PORTALS, help_args="<_link_>", help_text="Join a chat with an invite link.") -async def join(evt: CommandEvent): +async def join(evt: CommandEvent) -> None: if len(evt.args) == 0: return await evt.reply("**Usage:** `$cmdprefix+sp join `") @@ -137,7 +137,7 @@ async def join(evt: CommandEvent): @command_handler(help_section=SECTION_MISC, help_args="[`chats`|`contacts`|`me`]", help_text="Synchronize your chat portals, contacts and/or own info.") -async def sync(evt: CommandEvent): +async def sync(evt: CommandEvent) -> None: if len(evt.args) > 0: sync_only = evt.args[0] if sync_only not in ("chats", "contacts", "me"): diff --git a/mautrix_telegram/config.py b/mautrix_telegram/config.py index 159a61d5..c40b910b 100644 --- a/mautrix_telegram/config.py +++ b/mautrix_telegram/config.py @@ -25,7 +25,7 @@ yaml.indent(4) class DictWithRecursion: - def __init__(self, data: CommentedMap = None): + def __init__(self, data: CommentedMap = None) -> None: self._data = data or CommentedMap() # type: CommentedMap def _recursive_get(self, data: CommentedMap, key: str, default_value: Any) -> Any: @@ -46,7 +46,7 @@ class DictWithRecursion: def __contains__(self, key: str) -> bool: return self[key] is not None - def _recursive_set(self, data: CommentedMap, key: str, value: Any): + def _recursive_set(self, data: CommentedMap, key: str, value: Any) -> None: if '.' in key: key, next_key = key.split('.', 1) if key not in data: @@ -56,16 +56,16 @@ class DictWithRecursion: return data[key] = value - def set(self, key: str, value: Any, allow_recursion: bool = True): + def set(self, key: str, value: Any, allow_recursion: bool = True) -> None: if allow_recursion and '.' in key: self._recursive_set(self._data, key, value) return self._data[key] = value - def __setitem__(self, key: str, value: Any): + def __setitem__(self, key: str, value: Any) -> None: self.set(key, value) - def _recursive_del(self, data: CommentedMap, key: str): + def _recursive_del(self, data: CommentedMap, key: str) -> None: if '.' in key: key, next_key = key.split('.', 1) if key not in data: @@ -79,7 +79,7 @@ class DictWithRecursion: except KeyError: pass - def delete(self, key: str, allow_recursion: bool = True): + def delete(self, key: str, allow_recursion: bool = True) -> None: if allow_recursion and '.' in key: self._recursive_del(self._data, key) return @@ -89,19 +89,19 @@ class DictWithRecursion: except KeyError: pass - def __delitem__(self, key: str): + def __delitem__(self, key: str) -> None: self.delete(key) class Config(DictWithRecursion): - def __init__(self, path: str, registration_path: str, base_path: str): + def __init__(self, path: str, registration_path: str, base_path: str) -> None: super().__init__() self.path = path # type: str self.registration_path = registration_path # type: str self.base_path = base_path # type: str self._registration = None # type: dict - def load(self): + def load(self) -> None: with open(self.path, 'r') as stream: self._data = yaml.load(stream) @@ -113,7 +113,7 @@ class Config(DictWithRecursion): pass return None - def save(self): + def save(self) -> None: with open(self.path, 'w') as stream: yaml.dump(self._data, stream) if self._registration and self.registration_path: @@ -124,16 +124,16 @@ class Config(DictWithRecursion): def _new_token() -> str: return "".join(random.choice(string.ascii_lowercase + string.digits) for _ in range(64)) - def update(self): + def update(self) -> None: base = self.load_base() if not base: return - def copy(from_path, to_path=None): + def copy(from_path, to_path=None) -> None: if from_path in self: base[to_path or from_path] = self[from_path] - def copy_dict(from_path, to_path=None, override_existing_map=True): + def copy_dict(from_path, to_path=None, override_existing_map=True) -> None: if from_path in self: to_path = to_path or from_path if override_existing_map or to_path not in base: @@ -273,7 +273,7 @@ class Config(DictWithRecursion): return self._get_permissions("*") - def generate_registration(self): + def generate_registration(self) -> None: homeserver = self["homeserver.domain"] username_format = self.get("bridge.username_template", "telegram_{userid}") \ diff --git a/mautrix_telegram/context.py b/mautrix_telegram/context.py index de257477..ac48e239 100644 --- a/mautrix_telegram/context.py +++ b/mautrix_telegram/context.py @@ -32,7 +32,8 @@ if TYPE_CHECKING: class Context: def __init__(self, az: "AppService", db: "scoped_session", config: "Config", - loop: "asyncio.AbstractEventLoop", session_container: "AlchemySessionContainer"): + loop: "asyncio.AbstractEventLoop", session_container: "AlchemySessionContainer" + ) -> None: self.az = az # type: AppService self.db = db # type: scoped_session self.config = config # type: Config @@ -43,7 +44,7 @@ class Context: self.public_website = None # type: PublicBridgeWebsite self.provisioning_api = None # type: ProvisioningAPI - def __iter__(self): + def __iter__(self) -> None: yield self.az yield self.db yield self.config diff --git a/mautrix_telegram/db.py b/mautrix_telegram/db.py index 5a0baf70..13aa12a4 100644 --- a/mautrix_telegram/db.py +++ b/mautrix_telegram/db.py @@ -91,17 +91,17 @@ class RoomState(Base): _power_levels_json = None @property - def has_power_levels(self): + def has_power_levels(self) -> None: return bool(self._power_levels_text) @property - def power_levels(self): + def power_levels(self) -> None: if not self._power_levels_json and self._power_levels_text: self._power_levels_json = json.loads(self._power_levels_text) return self._power_levels_json or {} @power_levels.setter - def power_levels(self, val): + def power_levels(self, val) -> None: self._power_levels_json = val self._power_levels_text = json.dumps(val) @@ -116,7 +116,7 @@ class UserProfile(Base): displayname = Column(String, nullable=True) avatar_url = Column(String, nullable=True) - def dict(self): + def dict(self) -> None: return { "membership": self.membership, "displayname": self.displayname, @@ -171,7 +171,7 @@ class TelegramFile(Base): thumbnail = relationship("TelegramFile", uselist=False) -def init(db_session): +def init(db_session) -> None: Portal.query = db_session.query_property() Message.query = db_session.query_property() UserPortal.query = db_session.query_property() diff --git a/mautrix_telegram/formatter/from_telegram.py b/mautrix_telegram/formatter/from_telegram.py index 33f8a335..5afb1a58 100644 --- a/mautrix_telegram/formatter/from_telegram.py +++ b/mautrix_telegram/formatter/from_telegram.py @@ -328,6 +328,6 @@ def _parse_url(html: List[str], entity_text: str, url: str) -> bool: return False -def init_tg(context: "Context"): +def init_tg(context: "Context") -> None: global should_highlight_edits should_highlight_edits = htmldiff and context.config["bridge.highlight_edits"] diff --git a/mautrix_telegram/matrix.py b/mautrix_telegram/matrix.py index 4deda884..70bafb47 100644 --- a/mautrix_telegram/matrix.py +++ b/mautrix_telegram/matrix.py @@ -27,14 +27,14 @@ from . import user as u, portal as po, puppet as pu, commands as com class MatrixHandler: log = logging.getLogger("mau.mx") # type: logging.Logger - def __init__(self, context): + def __init__(self, context) -> None: self.az, self.db, self.config, _, self.tgbot = context self.commands = com.CommandProcessor(context) # type: com.CommandProcessor self.previously_typing = [] # type: List[str] self.az.matrix_event_handler(self.handle_event) - async def init_as_bot(self): + async def init_as_bot(self) -> None: displayname = self.config["appservice.bot_displayname"] if displayname: try: @@ -50,7 +50,7 @@ class MatrixHandler: except asyncio.TimeoutError: self.log.exception("TimeoutError when trying to set avatar") - async def handle_puppet_invite(self, room_id, puppet: pu.Puppet, inviter: u.User): + async def handle_puppet_invite(self, room_id, puppet: pu.Puppet, inviter: u.User) -> None: intent = puppet.default_mxid_intent self.log.debug(f"{inviter} invited puppet for {puppet.tgid} to {room_id}") if not await inviter.is_logged_in(): @@ -101,7 +101,7 @@ class MatrixHandler: await intent.send_notice(room_id, "This puppet will remain inactive until a " "Telegram chat is created for this room.") - async def accept_bot_invite(self, room_id: str, inviter: u.User): + async def accept_bot_invite(self, room_id: str, inviter: u.User) -> None: tries = 0 while tries < 5: try: @@ -126,7 +126,7 @@ class MatrixHandler: "bridge.permissions section in your config file.") await self.az.intent.leave_room(room_id) - async def handle_invite(self, room_id: str, user_id: str, inviter_mxid: str): + async def handle_invite(self, room_id: str, user_id: str, inviter_mxid: str) -> None: self.log.debug(f"{inviter_mxid} invited {user_id} to {room_id}") inviter = await u.User.get_by_mxid(inviter_mxid).ensure_started() if user_id == self.az.bot_mxid: @@ -150,7 +150,7 @@ class MatrixHandler: # The rest can probably be ignored - async def handle_join(self, room_id: str, user_id: str, event_id: str): + async def handle_join(self, room_id: str, user_id: str, event_id: str) -> None: user = await u.User.get_by_mxid(user_id).ensure_started() portal = po.Portal.get_by_mxid(room_id) @@ -171,7 +171,7 @@ class MatrixHandler: if await user.is_logged_in() or portal.has_bot: await portal.join_matrix(user, event_id) - async def handle_part(self, room_id: str, user_id, sender_mxid: str, event_id: str): + async def handle_part(self, room_id: str, user_id, sender_mxid: str, event_id: str) -> None: self.log.debug(f"{user_id} left {room_id}") sender = u.User.get_by_mxid(sender_mxid, create=False) @@ -202,7 +202,7 @@ class MatrixHandler: text = text[len(prefix) + 1:] return is_command, text - async def handle_message(self, room, sender, message, event_id): + async def handle_message(self, room, sender, message, event_id) -> None: is_command, text = self.is_command(message) sender = await u.User.get_by_mxid(sender).ensure_started() if not sender.relaybot_whitelisted: @@ -237,7 +237,7 @@ class MatrixHandler: is_portal=portal is not None) @staticmethod - async def handle_redaction(room_id: str, sender_mxid: str, event_id: str): + async def handle_redaction(room_id: str, sender_mxid: str, event_id: str) -> None: sender = await u.User.get_by_mxid(sender_mxid).ensure_started() if not sender.relaybot_whitelisted: return @@ -249,14 +249,15 @@ class MatrixHandler: await portal.handle_matrix_deletion(sender, event_id) @staticmethod - async def handle_power_levels(room_id: str, sender_mxid: str, new: dict, old: dict): + async def handle_power_levels(room_id: str, sender_mxid: str, new: dict, old: dict) -> None: portal = po.Portal.get_by_mxid(room_id) sender = await u.User.get_by_mxid(sender_mxid).ensure_started() if await sender.has_full_access(allow_bot=True) and portal: await portal.handle_matrix_power_levels(sender, new["users"], old["users"]) @staticmethod - async def handle_room_meta(evt_type: str, room_id: str, sender_mxid: str, content: dict): + async def handle_room_meta(evt_type: str, room_id: str, sender_mxid: str, + content: dict) -> None: portal = po.Portal.get_by_mxid(room_id) sender = await u.User.get_by_mxid(sender_mxid).ensure_started() if await sender.has_full_access(allow_bot=True) and portal: @@ -271,7 +272,7 @@ class MatrixHandler: @staticmethod async def handle_room_pin(room_id: str, sender_mxid: str, new_events: Set[str], - old_events: Set[str]): + old_events: Set[str]) -> None: portal = po.Portal.get_by_mxid(room_id) sender = await u.User.get_by_mxid(sender_mxid).ensure_started() if await sender.has_full_access(allow_bot=True) and portal: @@ -285,7 +286,7 @@ class MatrixHandler: @staticmethod async def handle_name_change(room_id: str, user_id: str, displayname: str, - prev_displayname: str, event_id: str): + prev_displayname: str, event_id: str) -> None: portal = po.Portal.get_by_mxid(room_id) if not portal or not portal.has_bot: return @@ -301,7 +302,7 @@ class MatrixHandler: for user_id in receipts.get("m.read", {})} @staticmethod - async def handle_read_receipts(room_id: str, receipts: Dict[str, str]): + async def handle_read_receipts(room_id: str, receipts: Dict[str, str]) -> None: portal = po.Portal.get_by_mxid(room_id) if not portal: return @@ -313,13 +314,13 @@ class MatrixHandler: await portal.mark_read(user, event_id) @staticmethod - async def handle_presence(user_id: str, presence: str): + async def handle_presence(user_id: str, presence: str) -> None: user = await u.User.get_by_mxid(user_id).ensure_started() if not await user.is_logged_in(): return await user.set_presence(presence == "online") - async def handle_typing(self, room_id: str, now_typing: List[str]): + async def handle_typing(self, room_id: str, now_typing: List[str]) -> None: portal = po.Portal.get_by_mxid(room_id) if not portal: return @@ -338,20 +339,20 @@ class MatrixHandler: self.previously_typing = now_typing - def filter_matrix_event(self, event: dict): + def filter_matrix_event(self, event: dict) -> None: sender = event.get("sender", None) if not sender: return False return (sender == self.az.bot_mxid or pu.Puppet.get_id_from_mxid(sender) is not None) - async def try_handle_event(self, evt: dict): + async def try_handle_event(self, evt: dict) -> None: try: await self.handle_event(evt) except Exception: self.log.exception("Error handling manually received Matrix event") - async def handle_event(self, evt: dict): + async def handle_event(self, evt: dict) -> None: if self.filter_matrix_event(evt): return self.log.debug("Received event: %s", evt) diff --git a/mautrix_telegram/portal.py b/mautrix_telegram/portal.py index 7b90f531..37e01406 100644 --- a/mautrix_telegram/portal.py +++ b/mautrix_telegram/portal.py @@ -86,7 +86,7 @@ class Portal: mxid: Optional[str] = None, username: Optional[str] = None, megagroup: Optional[bool] = False, title: Optional[str] = None, about: Optional[str] = None, photo_id: Optional[str] = None, - db_instance: DBPortal = None): + db_instance: DBPortal = None) -> None: self.mxid = mxid # type: str self.tgid = tgid # type: int self.tg_receiver = tg_receiver or tgid # type: int @@ -238,7 +238,7 @@ class Portal: # endregion # region Matrix room info updating - async def invite_to_matrix(self, users: InviteList): + async def invite_to_matrix(self, users: InviteList) -> None: if isinstance(users, str): await self.main_intent.invite(self.mxid, users, check_cache=True) elif isinstance(users, list): @@ -250,7 +250,7 @@ class Portal: async def update_matrix_room(self, user: "AbstractUser", entity: TypeChat, direct: bool, puppet: p.Puppet = None, levels: dict = None, users: List[User] = None, - participants: List[TypeParticipant] = None): + participants: List[TypeParticipant] = None) -> None: if not direct: await self.update_info(user, entity) if not users or not participants: @@ -383,7 +383,7 @@ class Portal: return None return self.alias_template.format(groupname=username) - def add_bot_chat(self, bot: User): + def add_bot_chat(self, bot: User) -> None: if self.bot and bot.id == self.bot.tgid: self.bot.add_chat(self.tgid, self.peer_type) return @@ -392,7 +392,7 @@ class Portal: if user and user.is_bot: user.register_portal(self) - async def sync_telegram_users(self, source: "AbstractUser", users: List[User]): + async def sync_telegram_users(self, source: "AbstractUser", users: List[User]) -> None: allowed_tgids = set() for entity in users: puppet = p.Puppet.get(entity.id) @@ -434,7 +434,8 @@ class Portal: "You had left this Telegram chat.") continue - async def add_telegram_user(self, user_id: int, source: Optional["AbstractUser"] = None): + async def add_telegram_user(self, user_id: int, source: Optional["AbstractUser"] = None + ) -> None: puppet = p.Puppet.get(user_id) if source: entity = await source.client.get_entity(PeerUser(user_id)) @@ -446,7 +447,7 @@ class Portal: user.register_portal(self) await self.invite_to_matrix(user.mxid) - async def delete_telegram_user(self, user_id: int, sender: p.Puppet): + async def delete_telegram_user(self, user_id: int, sender: p.Puppet) -> None: puppet = p.Puppet.get(user_id) user = u.User.get_by_tgid(user_id) kick_message = (f"Kicked by {sender.displayname}" @@ -460,7 +461,7 @@ class Portal: user.unregister_portal(self) await self.main_intent.kick(self.mxid, user.mxid, kick_message) - async def update_info(self, user: "AbstractUser", entity: TypeChat = None): + async def update_info(self, user: "AbstractUser", entity: TypeChat = None) -> None: if self.peer_type == "user": self.log.warning(f"Called update_info() for direct chat portal {self.tgid_log}") return @@ -524,7 +525,7 @@ class Portal: return max(photo.sizes, key=(lambda photo2: ( len(photo2.bytes) if isinstance(photo2, PhotoCachedSize) else photo2.size))) - async def remove_avatar(self, _: "AbstractUser", save: bool = False): + async def remove_avatar(self, _: "AbstractUser", save: bool = False) -> None: await self.main_intent.set_room_avatar(self.mxid, None) self.photo_id = None if save: @@ -622,7 +623,7 @@ class Portal: @staticmethod async def cleanup_room(intent: IntentAPI, room_id: str, message: str = "Portal deleted", - puppets_only: bool = False): + puppets_only: bool = False) -> None: try: members = await intent.get_room_members(room_id) except MatrixRequestError: @@ -639,11 +640,11 @@ class Portal: pass await intent.leave_room(room_id) - async def unbridge(self): + async def unbridge(self) -> None: await self.cleanup_room(self.main_intent, self.mxid, "Room unbridged", puppets_only=True) self.delete() - async def cleanup_and_delete(self): + async def cleanup_and_delete(self) -> None: await self.cleanup_room(self.main_intent, self.mxid) self.delete() @@ -682,7 +683,7 @@ class Portal: } async def name_change_matrix(self, user: u.User, displayname: str, prev_displayname: str, - event_id: str): + event_id: str) -> None: async with self.require_send_lock(self.bot.tgid): message = await self._get_state_change_message( "name_change", user, @@ -699,11 +700,12 @@ class Portal: return (await self.main_intent.get_displayname(self.mxid, user.mxid) or user.mxid_localpart) - def set_typing(self, user: u.User, typing: bool = True, action=SendMessageTypingAction): + def set_typing(self, user: u.User, typing: bool = True, + action=SendMessageTypingAction) -> None: return user.client(SetTypingRequest( self.peer, action() if typing else SendMessageCancelAction())) - async def mark_read(self, user: u.User, event_id: str): + async def mark_read(self, user: u.User, event_id: str) -> None: if user.is_bot: return space = self.tgid if self.peer_type == "channel" else user.tgid @@ -718,7 +720,7 @@ class Portal: else: await user.client(ReadMessageHistoryRequest(peer=self.peer, max_id=message.tgid)) - async def leave_matrix(self, user: u.User, source: u.User, event_id: str): + async def leave_matrix(self, user: u.User, source: u.User, event_id: str) -> None: if await user.needs_relaybot(self): async with self.require_send_lock(self.bot.tgid): message = await self._get_state_change_message("leave", user) @@ -754,7 +756,7 @@ class Portal: channel = await self.get_input_entity(user) await user.client(LeaveChannelRequest(channel=channel)) - async def join_matrix(self, user: u.User, event_id: str): + async def join_matrix(self, user: u.User, event_id: str) -> None: if await user.needs_relaybot(self): async with self.require_send_lock(self.bot.tgid): message = await self._get_state_change_message("join", user) @@ -773,7 +775,7 @@ class Portal: # We'll just assume the user is already in the chat. pass - async def _apply_msg_format(self, sender: u.User, msgtype: str, message: dict): + async def _apply_msg_format(self, sender: u.User, msgtype: str, message: dict) -> None: if "formatted_body" not in message: message["format"] = "org.matrix.custom.html" message["formatted_body"] = escape_html(message.get("body", "")) @@ -788,7 +790,8 @@ class Portal: message=body) message["formatted_body"] = Template(tpl).safe_substitute(tpl_args) - async def _pre_process_matrix_message(self, sender: u.User, use_relaybot: bool, message: dict): + async def _pre_process_matrix_message(self, sender: u.User, use_relaybot: bool, + message: dict) -> None: msgtype = message.get("msgtype", "m.text") if msgtype == "m.emote": await self._apply_msg_format(sender, msgtype, message) @@ -825,7 +828,7 @@ class Portal: return None async def _handle_matrix_text(self, sender_id: int, event_id: str, space: int, - client: "MautrixTelegramClient", message: dict, reply_to: int): + client: "MautrixTelegramClient", message: dict, reply_to: int) -> None: lock = self.require_send_lock(sender_id) async with lock: response = await client.send_message(self.peer, message, reply_to=reply_to, @@ -833,7 +836,7 @@ class Portal: self._add_telegram_message_to_db(event_id, space, response) async def _handle_matrix_file(self, msgtype: str, sender_id: int, event_id: str, space: int, - client: "MautrixTelegramClient", message: dict, reply_to: int): + client: "MautrixTelegramClient", message: dict, reply_to: int) -> None: file = await self.main_intent.download_file(message["url"]) info = message.get("info", {}) @@ -868,7 +871,7 @@ class Portal: async def _handle_matrix_location(self, sender_id: int, event_id: str, space: int, client: "MautrixTelegramClient", message: dict, - reply_to: int): + reply_to: int) -> None: try: lat, long = message["geo_uri"][len("geo:"):].split(",") lat, long = float(lat), float(long) @@ -884,7 +887,8 @@ class Portal: caption=message, entities=entities) self._add_telegram_message_to_db(event_id, space, response) - def _add_telegram_message_to_db(self, event_id: str, space: int, response: TypeMessage): + def _add_telegram_message_to_db(self, event_id: str, space: int, + response: TypeMessage) -> None: self.log.debug("Handled Matrix message: %s", response) self.is_duplicate(response, (event_id, space)) self.db.add(DBMessage( @@ -894,7 +898,7 @@ class Portal: mxid=event_id)) self.db.commit() - async def handle_matrix_message(self, sender: u.User, message: dict, event_id: str): + async def handle_matrix_message(self, sender: u.User, message: dict, event_id: str) -> None: puppet = p.Puppet.get_by_custom_mxid(sender.mxid) if puppet and message.get("net.maunium.telegram.puppet", False): self.log.debug("Ignoring puppet-sent message by confirmed puppet user %s", sender.mxid) @@ -922,7 +926,7 @@ class Portal: else: self.log.debug(f"Unhandled Matrix event: {message}") - async def handle_matrix_pin(self, sender: u.User, pinned_message: Optional[str]): + async def handle_matrix_pin(self, sender: u.User, pinned_message: Optional[str]) -> None: if self.peer_type != "channel": return try: @@ -936,7 +940,7 @@ class Portal: except ChatNotModifiedError: pass - async def handle_matrix_deletion(self, deleter: u.User, event_id: str): + async def handle_matrix_deletion(self, deleter: u.User, event_id: str) -> None: deleter = deleter if not await deleter.needs_relaybot(self) else self.bot space = self.tgid if self.peer_type == "channel" else deleter.tgid message = DBMessage.query.filter(DBMessage.mxid == event_id, @@ -946,7 +950,7 @@ class Portal: return await deleter.client.delete_messages(self.peer, [message.tgid]) - async def _update_telegram_power_level(self, sender: u.User, user_id: int, level: int): + async def _update_telegram_power_level(self, sender: u.User, user_id: int, level: int) -> None: if self.peer_type == "chat": await sender.client(EditChatAdminRequest( chat_id=self.tgid, user_id=user_id, is_admin=level >= 50)) @@ -963,7 +967,7 @@ class Portal: user_id=user_id, admin_rights=rights)) async def handle_matrix_power_levels(self, sender: u.User, new_users: Dict[str, int], - old_users: Dict[str, int]): + old_users: Dict[str, int]) -> None: # TODO handle all power level changes and bridge exact admin rights to supergroups/channels for user, level in new_users.items(): if not user or user == self.main_intent.mxid or user == sender.mxid: @@ -979,7 +983,7 @@ class Portal: if user not in old_users or level != old_users[user]: await self._update_telegram_power_level(sender, user_id, level) - async def handle_matrix_about(self, sender: u.User, about: str): + async def handle_matrix_about(self, sender: u.User, about: str) -> None: if self.peer_type not in {"channel"}: return channel = await self.get_input_entity(sender) @@ -987,7 +991,7 @@ class Portal: self.about = about self.save() - async def handle_matrix_title(self, sender: u.User, title: str): + async def handle_matrix_title(self, sender: u.User, title: str) -> None: if self.peer_type not in {"chat", "channel"}: return @@ -1000,7 +1004,7 @@ class Portal: self.title = title self.save() - async def handle_matrix_avatar(self, sender: u.User, url: str): + async def handle_matrix_avatar(self, sender: u.User, url: str) -> None: if self.peer_type not in {"chat", "channel"}: # Invalid peer type return @@ -1027,7 +1031,7 @@ class Portal: self.save() break - def _register_outgoing_actions_for_dedup(self, response: TypeUpdates): + def _register_outgoing_actions_for_dedup(self, response: TypeUpdates) -> None: for update in response.updates: check_dedup = (isinstance(update, (UpdateNewMessage, UpdateNewChannelMessage)) and isinstance(update.message, MessageService)) @@ -1051,7 +1055,7 @@ class Portal: user_tgids.add(puppet_id) return list(user_tgids) - async def upgrade_telegram_chat(self, source: u.User): + async def upgrade_telegram_chat(self, source: u.User) -> None: if self.peer_type != "chat": raise ValueError("Only normal group chats are upgradable to supergroups.") @@ -1067,7 +1071,7 @@ class Portal: self.migrate_and_save(entity.id) await self.update_info(source, entity) - async def set_telegram_username(self, source: u.User, username: str): + async def set_telegram_username(self, source: u.User, username: str) -> None: if self.peer_type != "channel": raise ValueError("Only channels and supergroups have usernames.") await source.client( @@ -1075,7 +1079,7 @@ class Portal: if await self.update_username(username): self.save() - async def create_telegram_chat(self, source: u.User, supergroup: bool = False): + async def create_telegram_chat(self, source: u.User, supergroup: bool = False) -> None: if not self.mxid: raise ValueError("Can't create Telegram chat for portal without Matrix room.") elif self.tgid: @@ -1116,7 +1120,8 @@ class Portal: await self.main_intent.set_power_levels(self.mxid, levels) await self.handle_matrix_power_levels(source, levels["users"], {}) - async def invite_telegram(self, source: u.User, puppet: Union[p.Puppet, "AbstractUser"]): + async def invite_telegram(self, source: u.User, + puppet: Union[p.Puppet, "AbstractUser"]) -> None: if self.peer_type == "chat": await source.client( AddChatUserRequest(chat_id=self.tgid, user_id=puppet.tgid, fwd_limit=0)) @@ -1129,7 +1134,7 @@ class Portal: # region Telegram event handling async def handle_telegram_typing(self, user: p.Puppet, - _: Union[UpdateUserTyping, UpdateChatUserTyping]): + _: Union[UpdateUserTyping, UpdateChatUserTyping]) -> None: if self.mxid: await user.intent.set_typing(self.mxid, is_typing=True) @@ -1139,7 +1144,7 @@ class Portal: return None async def handle_telegram_photo(self, source: "AbstractUser", intent: IntentAPI, evt: Message, - relates_to=None): + relates_to=None) -> None: largest_size = self._get_largest_photo_size(evt.media.photo) file = await util.transfer_file_to_matrix(self.db, source.client, intent, largest_size.location) @@ -1300,7 +1305,8 @@ class Portal: msgtype=msgtype, timestamp=evt.date, external_url=self.get_external_url(evt)) - async def handle_telegram_edit(self, source: "AbstractUser", sender: p.Puppet, evt: Message): + async def handle_telegram_edit(self, source: "AbstractUser", sender: p.Puppet, + evt: Message) -> None: if not self.mxid: return elif not config["bridge.edits_as_replies"]: @@ -1349,7 +1355,8 @@ class Portal: .update({"mxid": mxid}) self.db.commit() - async def handle_telegram_message(self, source: "AbstractUser", sender: p.Puppet, evt: Message): + async def handle_telegram_message(self, source: "AbstractUser", sender: p.Puppet, + evt: Message) -> None: if not self.mxid: await self.create_matrix_room(source, invites=[source.mxid], update_if_exists=False) @@ -1461,7 +1468,7 @@ class Portal: return True async def handle_telegram_action(self, source: "AbstractUser", sender: p.Puppet, - update: MessageService): + update: MessageService) -> None: action = update.action should_ignore = ((not self.mxid and not await self._create_room_on_action(source, action)) or self.is_duplicate_action(update)) @@ -1491,7 +1498,7 @@ class Portal: else: self.log.debug("Unhandled Telegram action in %s: %s", self.title, action) - async def set_telegram_admin(self, user_id: int): + async def set_telegram_admin(self, user_id: int) -> None: puppet = p.Puppet.get(user_id) user = await u.User.get_by_tgid(user_id) @@ -1502,12 +1509,12 @@ class Portal: levels["users"][puppet.mxid] = 50 await self.main_intent.set_power_levels(self.mxid, levels) - async def receive_telegram_pin_sender(self, sender: p.Puppet): + async def receive_telegram_pin_sender(self, sender: p.Puppet) -> None: self._temp_pinned_message_sender = sender if self._temp_pinned_message_id: await self.update_telegram_pin() - async def update_telegram_pin(self): + async def update_telegram_pin(self) -> None: intent = (self._temp_pinned_message_sender.intent if self._temp_pinned_message_sender else self.main_intent) msg_id = self._temp_pinned_message_id @@ -1520,7 +1527,7 @@ class Portal: else: await intent.set_pinned_messages(self.mxid, []) - async def receive_telegram_pin_id(self, msg_id: int): + async def receive_telegram_pin_id(self, msg_id: int) -> None: if msg_id == 0: return await self.update_telegram_pin() self._temp_pinned_message_id = msg_id @@ -1596,13 +1603,13 @@ class Portal: return changed async def update_telegram_participants(self, participants: List[TypeParticipant], - levels: dict = None): + levels: dict = None) -> None: if not levels: levels = await self.main_intent.get_power_levels(self.mxid) if self._participants_to_power_levels(participants, levels): await self.main_intent.set_power_levels(self.mxid, levels) - async def set_telegram_admins_enabled(self, enabled: bool): + async def set_telegram_admins_enabled(self, enabled: bool) -> None: level = 50 if enabled else 10 levels = await self.main_intent.get_power_levels(self.mxid) levels["invite"] = level @@ -1624,7 +1631,7 @@ class Portal: mxid=self.mxid, username=self.username, megagroup=self.megagroup, title=self.title, about=self.about, photo_id=self.photo_id) - def migrate_and_save(self, new_id: int): + def migrate_and_save(self, new_id: int) -> None: existing = DBPortal.query.get(self.tgid_full) if existing: self.db.delete(existing) @@ -1637,7 +1644,7 @@ class Portal: self.by_tgid[self.tgid_full] = self self.save() - def save(self): + def save(self) -> None: self.db_instance.mxid = self.mxid self.db_instance.username = self.username self.db_instance.title = self.title @@ -1645,7 +1652,7 @@ class Portal: self.db_instance.photo_id = self.photo_id self.db.commit() - def delete(self): + def delete(self) -> None: try: del self.by_tgid[self.tgid_full] except KeyError: @@ -1758,7 +1765,7 @@ class Portal: # endregion -def init(context: Context): +def init(context: Context) -> None: global config Portal.az, Portal.db, config, Portal.loop, Portal.bot = context Portal.bridge_notices = config["bridge.bridge_notices"] diff --git a/mautrix_telegram/puppet.py b/mautrix_telegram/puppet.py index f5642bc2..4e7a34d6 100644 --- a/mautrix_telegram/puppet.py +++ b/mautrix_telegram/puppet.py @@ -50,7 +50,7 @@ class Puppet: def __init__(self, id=None, access_token=None, custom_mxid=None, username=None, displayname=None, displayname_source=None, photo_id=None, is_bot=None, - is_registered=False, db_instance=None): + is_registered=False, db_instance=None) -> None: self.id = id self.access_token = access_token self.custom_mxid = custom_mxid @@ -75,20 +75,20 @@ class Puppet: self.by_custom_mxid[self.custom_mxid] = self @property - def tgid(self): + def tgid(self) -> None: return self.id @staticmethod - async def is_logged_in(): + async def is_logged_in() -> None: return True # region Custom puppet management - def refresh_intents(self): + def refresh_intents(self) -> None: self.is_real_user = self.custom_mxid and self.access_token self.intent = (self.az.intent.user(self.custom_mxid, self.access_token) if self.is_real_user else self.default_mxid_intent) - async def switch_mxid(self, access_token, mxid): + async def switch_mxid(self, access_token, mxid) -> None: prev_mxid = self.custom_mxid self.custom_mxid = mxid self.access_token = access_token @@ -109,7 +109,7 @@ class Puppet: self.save() return 0 - async def init_custom_mxid(self): + async def init_custom_mxid(self) -> None: if not self.is_real_user: return 0 @@ -125,7 +125,7 @@ class Puppet: asyncio.ensure_future(self.sync(), loop=self.loop) return 0 - async def leave_rooms_with_default_user(self): + async def leave_rooms_with_default_user(self) -> None: for room_id in await self.default_mxid_intent.get_joined_rooms(): try: await self.default_mxid_intent.leave_room(room_id) @@ -159,7 +159,7 @@ class Puppet: }, }) - def filter_events(self, events): + def filter_events(self, events) -> None: new_events = [] for event in events: evt_type = event.get("type", None) @@ -186,7 +186,7 @@ class Puppet: new_events.append(event) return new_events - def handle_sync(self, presence, ephemeral): + def handle_sync(self, presence, ephemeral) -> None: presence = [self.mx.try_handle_event(event) for event in presence] for room_id, events in ephemeral.items(): @@ -201,13 +201,13 @@ class Puppet: coro = asyncio.gather(*events, loop=self.loop) asyncio.ensure_future(coro, loop=self.loop) - async def sync(self): + async def sync(self) -> None: try: await self._sync() except Exception: self.log.exception("Fatal error syncing") - async def _sync(self): + async def _sync(self) -> None: if not self.is_real_user: self.log.warning("Called sync() for non-custom puppet.") return @@ -241,25 +241,25 @@ class Puppet: # region DB conversion @property - def db_instance(self): + def db_instance(self) -> None: if not self._db_instance: self._db_instance = self.new_db_instance() return self._db_instance - def new_db_instance(self): + def new_db_instance(self) -> None: return DBPuppet(id=self.id, access_token=self.access_token, custom_mxid=self.custom_mxid, username=self.username, displayname=self.displayname, displayname_source=self.displayname_source, photo_id=self.photo_id, is_bot=self.is_bot, matrix_registered=self.is_registered) @classmethod - def from_db(cls, db_puppet): + def from_db(cls, db_puppet) -> None: return Puppet(db_puppet.id, db_puppet.access_token, db_puppet.custom_mxid, db_puppet.username, db_puppet.displayname, db_puppet.displayname_source, db_puppet.photo_id, db_puppet.is_bot, db_puppet.matrix_registered, db_instance=db_puppet) - def save(self): + def save(self) -> None: self.db_instance.access_token = self.access_token self.db_instance.custom_mxid = self.custom_mxid self.db_instance.username = self.username @@ -272,7 +272,7 @@ class Puppet: # endregion # region Info updating - def similarity(self, query): + def similarity(self, query) -> None: username_similarity = (SequenceMatcher(None, self.username, query).ratio() if self.username else 0) displayname_similarity = (SequenceMatcher(None, self.displayname, query).ratio() @@ -281,7 +281,7 @@ class Puppet: return round(similarity * 1000) / 10 @staticmethod - def get_displayname(info, enable_format=True): + def get_displayname(info, enable_format=True) -> None: data = { "phone number": info.phone if hasattr(info, "phone") else None, "username": info.username, @@ -308,7 +308,7 @@ class Puppet: return config.get("bridge.displayname_template", "{displayname} (Telegram)").format( displayname=name) - async def update_info(self, source, info): + async def update_info(self, source, info) -> None: changed = False if self.username != info.username: self.username = info.username @@ -323,7 +323,7 @@ class Puppet: if changed: self.save() - async def update_displayname(self, source, info): + async def update_displayname(self, source, info) -> None: ignore_source = (not source.is_relaybot and self.displayname_source is not None and self.displayname_source != source.tgid) @@ -340,7 +340,7 @@ class Puppet: self.displayname_source = source.tgid return True - async def update_avatar(self, source, photo): + async def update_avatar(self, source, photo) -> None: photo_id = f"{photo.volume_id}-{photo.local_id}" if self.photo_id != photo_id: file = await util.transfer_file_to_matrix(self.db, source.client, @@ -379,7 +379,7 @@ class Puppet: return cls.get(tgid, create) if tgid else None @classmethod - def get_by_custom_mxid(cls, mxid): + def get_by_custom_mxid(cls, mxid) -> None: if not mxid: raise ValueError("Matrix ID can't be empty") @@ -396,21 +396,21 @@ class Puppet: return None @classmethod - def get_all_with_custom_mxid(cls): + def get_all_with_custom_mxid(cls) -> None: return [cls.by_custom_mxid[puppet.mxid] if puppet.custom_mxid in cls.by_custom_mxid else cls.from_db(puppet) for puppet in DBPuppet.query.filter(DBPuppet.custom_mxid is not None).all()] @classmethod - def get_id_from_mxid(cls, mxid): + def get_id_from_mxid(cls, mxid) -> None: match = cls.mxid_regex.match(mxid) if match: return int(match.group(1)) return None @classmethod - def get_mxid_from_id(cls, tgid): + def get_mxid_from_id(cls, tgid) -> None: return f"@{cls.username_template.format(userid=tgid)}:{cls.hs_domain}" @classmethod diff --git a/mautrix_telegram/sqlstatestore.py b/mautrix_telegram/sqlstatestore.py index 68e9fd9d..c60af89c 100644 --- a/mautrix_telegram/sqlstatestore.py +++ b/mautrix_telegram/sqlstatestore.py @@ -25,7 +25,7 @@ from .db import RoomState, UserProfile class SQLStateStore(StateStore): - def __init__(self, db): + def __init__(self, db) -> None: super().__init__() self.db = db # type: orm.Session self.profile_cache = {} # type: Dict[Tuple[str, str], UserProfile] @@ -37,13 +37,13 @@ class SQLStateStore(StateStore): return puppet.is_registered if puppet else False @staticmethod - def registered(user: str): + def registered(user: str) -> None: puppet = pu.Puppet.get_by_mxid(user) if puppet: puppet.is_registered = True puppet.save() - def update_state(self, event: dict): + def update_state(self, event: dict) -> None: event_type = event["type"] if event_type == "m.room.power_levels": self.set_power_levels(event["room_id"], event["content"]) @@ -70,14 +70,14 @@ class SQLStateStore(StateStore): def get_member(self, room: str, user: str) -> dict: return self._get_user_profile(room, user).dict() - def set_member(self, room: str, user: str, member: dict): + def set_member(self, room: str, user: str, member: dict) -> None: profile = self._get_user_profile(room, user) profile.membership = member.get("membership", profile.membership or "leave") profile.displayname = member.get("displayname", profile.displayname) profile.avatar_url = member.get("avatar_url", profile.avatar_url) self.db.commit() - def set_membership(self, room: str, user: str, membership: str): + def set_membership(self, room: str, user: str, membership: str) -> None: self.set_member(room, user, { "membership": membership, }) @@ -102,7 +102,7 @@ class SQLStateStore(StateStore): def get_power_levels(self, room: str) -> dict: return self._get_room_state(room).power_levels - def set_power_level(self, room: str, user: str, level: int): + def set_power_level(self, room: str, user: str, level: int) -> None: room_state = self._get_room_state(room) power_levels = room_state.power_levels if not power_levels: @@ -114,7 +114,7 @@ class SQLStateStore(StateStore): room_state.power_levels = power_levels self.db.commit() - def set_power_levels(self, room: str, content: dict): + def set_power_levels(self, room: str, content: dict) -> None: state = self._get_room_state(room) state.power_levels = content self.db.commit() diff --git a/mautrix_telegram/user.py b/mautrix_telegram/user.py index c8a28131..b100fba0 100644 --- a/mautrix_telegram/user.py +++ b/mautrix_telegram/user.py @@ -47,7 +47,7 @@ class User(AbstractUser): def __init__(self, mxid: str, tgid: Optional[int] = None, username: Optional[str] = None, db_contacts: Optional[List[DBContact]] = None, saved_contacts: int = 0, is_bot: bool = False, db_portals: Optional[List[DBPortal]] = None, - db_instance: Optional[DBUser] = None): + db_instance: Optional[DBUser] = None) -> None: super().__init__() self.mxid = mxid # type: str self.tgid = tgid # type: int @@ -93,7 +93,7 @@ class User(AbstractUser): for puppet in self.contacts] @db_contacts.setter - def db_contacts(self, contacts: List[DBContact]): + def db_contacts(self, contacts: List[DBContact]) -> None: self.contacts = [pu.Puppet.get(entry.contact) for entry in contacts] if contacts else [] @property @@ -101,7 +101,7 @@ class User(AbstractUser): return [portal.db_instance for portal in self.portals.values() if not portal.deleted] @db_portals.setter - def db_portals(self, portals: List[DBPortal]): + def db_portals(self, portals: List[DBPortal]) -> None: self.portals = {(portal.tgid, portal.tg_receiver): po.Portal.get_by_tgid(portal.tgid, portal.tg_receiver) for portal in portals} if portals else {} @@ -119,7 +119,7 @@ class User(AbstractUser): contacts=self.db_contacts, saved_contacts=self.saved_contacts or 0, portals=self.db_portals) - def save(self): + def save(self) -> None: self.db_instance.tgid = self.tgid self.db_instance.username = self.username self.db_instance.contacts = self.db_contacts @@ -127,7 +127,7 @@ class User(AbstractUser): self.db_instance.portals = self.db_portals self.db.commit() - def delete(self): + def delete(self) -> None: try: del self.by_mxid[self.mxid] del self.by_tgid[self.tgid] @@ -156,7 +156,7 @@ class User(AbstractUser): self.client.session.delete() return self - async def post_login(self, info: TLUser = None): + async def post_login(self, info: TLUser = None) -> None: try: await self.update_info(info) if not self.is_bot: @@ -167,7 +167,7 @@ class User(AbstractUser): except Exception: self.log.exception("Failed to run post-login functions for %s", self.mxid) - async def update(self, update: TypeUpdate): + async def update(self, update: TypeUpdate) -> None: if not self.is_bot: return @@ -193,12 +193,12 @@ class User(AbstractUser): def ensure_started(self, even_if_no_session: bool = False) -> "Awaitable[User]": return super().ensure_started(even_if_no_session) - def set_presence(self, online: bool = True): + def set_presence(self, online: bool = True) -> None: if self.is_bot: return return self.client(UpdateStatusRequest(offline=not online)) - async def update_info(self, info: TLUser = None): + async def update_info(self, info: TLUser = None) -> None: info = info or await self.client.get_me() changed = False if self.is_bot != info.bot: @@ -213,7 +213,7 @@ class User(AbstractUser): if changed: self.save() - async def log_out(self): + async def log_out(self) -> None: puppet = pu.Puppet.get(self.tgid) if puppet.is_real_user: await puppet.switch_mxid(None, None) @@ -272,7 +272,7 @@ class User(AbstractUser): return await self._search_remote(query), True - async def sync_dialogs(self, synchronous_create: bool = False): + async def sync_dialogs(self, synchronous_create: bool = False) -> None: creators = [] for entity in await self.get_dialogs(limit=30): portal = po.Portal.get_by_entity(entity) @@ -283,7 +283,7 @@ class User(AbstractUser): self.save() await asyncio.gather(*creators, loop=self.loop) - def register_portal(self, portal: po.Portal): + def register_portal(self, portal: po.Portal) -> None: try: if self.portals[portal.tgid_full] == portal: return @@ -292,7 +292,7 @@ class User(AbstractUser): self.portals[portal.tgid_full] = portal self.save() - def unregister_portal(self, portal: po.Portal): + def unregister_portal(self, portal: po.Portal) -> None: try: del self.portals[portal.tgid_full] self.save() @@ -309,7 +309,7 @@ class User(AbstractUser): acc = (acc * 20261 + id) & 0xffffffff return acc & 0x7fffffff - async def sync_contacts(self): + async def sync_contacts(self) -> None: response = await self.client(GetContactsRequest(hash=self._hash_contacts())) if isinstance(response, ContactsNotModified): return diff --git a/mautrix_telegram/util/format_duration.py b/mautrix_telegram/util/format_duration.py index 9402b83e..076eb720 100644 --- a/mautrix_telegram/util/format_duration.py +++ b/mautrix_telegram/util/format_duration.py @@ -17,10 +17,10 @@ def format_duration(seconds: int) -> str: - def pluralize(count, singular): + def pluralize(count, singular) -> None: return singular if count == 1 else singular + "s" - def include(count, word): + def include(count, word) -> None: return f"{count} {pluralize(count, word)}" if count > 0 else "" minutes, seconds = divmod(seconds, 60)