Switch from SQLAlchemy to asyncpg/aiosqlite

This commit is contained in:
Tulir Asokan
2021-12-20 22:39:09 +02:00
parent f12f3fe007
commit 89ab29ea5f
61 changed files with 4681 additions and 4628 deletions
+29 -41
View File
@@ -1,5 +1,5 @@
# mautrix-telegram - A Matrix-Telegram puppeting bridge
# Copyright (C) 2019 Tulir Asokan
# Copyright (C) 2021 Tulir Asokan
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
@@ -33,10 +33,6 @@ from .db import BotChat
from .types import TelegramID
from . import puppet as pu, portal as po, user as u
if TYPE_CHECKING:
from .config import Config
config: Optional['Config'] = None
ReplyFunc = Callable[[str], Awaitable[Message]]
@@ -59,12 +55,12 @@ class Bot(AbstractUser):
self.puppet_whitelisted = True
self.whitelisted = True
self.relaybot_whitelisted = True
self.username = None
self.tg_username = None
self.is_relaybot = True
self.is_bot = True
self.chats = {}
self.tg_whitelist = []
self.whitelist_group_admins = (config["bridge.relaybot.whitelist_group_admins"]
self.whitelist_group_admins = (self.config["bridge.relaybot.whitelist_group_admins"]
or False)
self._me_info = None
self._me_mxid = None
@@ -76,7 +72,7 @@ class Bot(AbstractUser):
return self._me_info, self._me_mxid
async def init_permissions(self) -> None:
whitelist = config["bridge.relaybot.whitelist"] or []
whitelist = self.config["bridge.relaybot.whitelist"] or []
for user_id in whitelist:
if isinstance(user_id, str):
entity = await self.client.get_input_entity(user_id)
@@ -88,7 +84,7 @@ class Bot(AbstractUser):
self.tg_whitelist.append(user_id)
async def start(self, delete_unless_authenticated: bool = False) -> 'Bot':
self.chats = {chat.id: chat.type for chat in BotChat.all()}
self.chats = {chat.id: chat.type for chat in await BotChat.all()}
await super().start(delete_unless_authenticated)
if not await self.is_logged_in():
await self.client.sign_in(bot_token=self.token)
@@ -99,14 +95,14 @@ class Bot(AbstractUser):
await self.init_permissions()
info = await self.client.get_me()
self.tgid = TelegramID(info.id)
self.username = info.username
self.tg_username = info.username
self.mxid = pu.Puppet.get_mxid_from_id(self.tgid)
chat_ids = [chat_id for chat_id, chat_type in self.chats.items() if chat_type == "chat"]
response = await self.client(GetChatsRequest(chat_ids))
for chat in response.chats:
if isinstance(chat, ChatForbidden) or chat.left or chat.deactivated:
self.remove_chat(TelegramID(chat.id))
await self.remove_chat(TelegramID(chat.id))
channel_ids = [InputChannel(chat_id, 0)
for chat_id, chat_type in self.chats.items()
@@ -115,31 +111,31 @@ class Bot(AbstractUser):
try:
await self.client(GetChannelsRequest([channel_id]))
except (ChannelPrivateError, ChannelInvalidError):
self.remove_chat(TelegramID(channel_id.channel_id))
await self.remove_chat(TelegramID(channel_id.channel_id))
async def register_portal(self, portal: po.Portal) -> None:
self.add_chat(portal.tgid, portal.peer_type)
await self.add_chat(portal.tgid, portal.peer_type)
async def unregister_portal(self, tgid: int, tg_receiver: int) -> None:
self.remove_chat(tgid)
async def unregister_portal(self, tgid: TelegramID, tg_receiver: TelegramID) -> None:
await self.remove_chat(tgid)
def add_chat(self, chat_id: TelegramID, chat_type: str) -> None:
async def add_chat(self, chat_id: TelegramID, chat_type: str) -> None:
if chat_id not in self.chats:
self.chats[chat_id] = chat_type
BotChat(id=TelegramID(chat_id), type=chat_type).insert()
await BotChat(id=chat_id, type=chat_type).insert()
def remove_chat(self, chat_id: TelegramID) -> None:
async def remove_chat(self, chat_id: TelegramID) -> None:
try:
del self.chats[chat_id]
except KeyError:
pass
BotChat.delete_by_id(chat_id)
await BotChat.delete_by_id(chat_id)
async def _can_use_commands(self, chat: TypePeer, tgid: TelegramID) -> bool:
if tgid in self.tg_whitelist:
return True
user = u.User.get_by_tgid(tgid)
user = await u.User.get_by_tgid(tgid)
if user and user.is_admin:
self.tg_whitelist.append(user.tgid)
return True
@@ -157,13 +153,14 @@ class Bot(AbstractUser):
return False
async def check_can_use_commands(self, event: Message, reply: ReplyFunc) -> bool:
# FIXME event.from_id is not int
if not await self._can_use_commands(event.to_id, TelegramID(event.from_id)):
await reply("You do not have the permission to use that command.")
return False
return True
async def handle_command_portal(self, portal: po.Portal, reply: ReplyFunc) -> Message:
if not config["bridge.relaybot.authless_portals"]:
if not self.config["bridge.relaybot.authless_portals"]:
return await reply("This bridge doesn't allow portal creation from Telegram.")
if not portal.allow_bridging:
@@ -187,11 +184,11 @@ class Bot(AbstractUser):
"Create one with /portal first.")
if mxid_input[0] != '@' or mxid_input.find(':') < 2:
return await reply("That doesn't look like a Matrix ID.")
user = await u.User.get_by_mxid(mxid_input).ensure_started()
user = await u.User.get_and_start_by_mxid(mxid_input)
if not user.relaybot_whitelisted:
return await reply("That user is not whitelisted to use the bridge.")
elif await user.is_logged_in():
displayname = f"@{user.username}" if user.username else user.displayname
displayname = f"@{user.tg_username}" if user.tg_username else user.displayname
return await reply("That user seems to be logged in. "
f"Just invite [{displayname}](tg://user?id={user.tgid})")
else:
@@ -214,7 +211,7 @@ class Bot(AbstractUser):
def match_command(self, text: str, command: str) -> bool:
text = text.lower()
command = f"/{command.lower()}"
command_targeted = f"{command}@{self.username.lower()}"
command_targeted = f"{command}@{self.tg_username.lower()}"
is_plain_command = text == command or text == command_targeted
if is_plain_command:
@@ -233,7 +230,7 @@ class Bot(AbstractUser):
text = message.message
if self.match_command(text, "start"):
pcm = config["bridge.relaybot.private_chat.message"]
pcm = self.config["bridge.relaybot.private_chat.message"]
if pcm:
await reply(pcm)
return
@@ -243,7 +240,7 @@ class Bot(AbstractUser):
elif message.is_private:
return
portal = po.Portal.get_by_entity(message.to_id)
portal = await po.Portal.get_by_entity(message.to_id)
is_portal_cmd = self.match_command(text, "portal")
is_invite_cmd = self.match_command(text, "invite")
@@ -259,7 +256,7 @@ class Bot(AbstractUser):
mxid = ""
await self.handle_command_invite(portal, reply, mxid_input=UserID(mxid))
def handle_service_message(self, message: MessageService) -> None:
async def handle_service_message(self, message: MessageService) -> None:
to_peer = message.to_id
if isinstance(to_peer, PeerChannel):
to_id = TelegramID(to_peer.channel_id)
@@ -272,18 +269,18 @@ class Bot(AbstractUser):
action = message.action
if isinstance(action, MessageActionChatAddUser) and self.tgid in action.users:
self.add_chat(to_id, chat_type)
await self.add_chat(to_id, chat_type)
elif isinstance(action, MessageActionChatDeleteUser) and action.user_id == self.tgid:
self.remove_chat(to_id)
await self.remove_chat(to_id)
elif isinstance(action, MessageActionChatMigrateTo):
self.remove_chat(to_id)
self.add_chat(TelegramID(action.channel_id), "channel")
await self.remove_chat(to_id)
await self.add_chat(TelegramID(action.channel_id), "channel")
async def update(self, update) -> bool:
if not isinstance(update, (UpdateNewMessage, UpdateNewChannelMessage)):
return False
if isinstance(update.message, MessageService):
self.handle_service_message(update.message)
await self.handle_service_message(update.message)
return False
is_command = (isinstance(update.message, Message)
@@ -300,12 +297,3 @@ class Bot(AbstractUser):
@property
def name(self) -> str:
return "bot"
def init(cfg: 'Config') -> Optional[Bot]:
global config
config = cfg
token = config["telegram.bot_token"]
if token and not token.lower().startswith("disable"):
return Bot(token)
return None