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