diff --git a/mautrix_telegram/__main__.py b/mautrix_telegram/__main__.py index 9037736d..bc26bfc3 100644 --- a/mautrix_telegram/__main__.py +++ b/mautrix_telegram/__main__.py @@ -130,5 +130,8 @@ class TelegramBridge(Bridge): async def get_double_puppet(self, user_id: UserID) -> Puppet: return await Puppet.get_by_custom_mxid(user_id) + def is_bridge_ghost(self, user_id: UserID) -> bool: + return bool(Puppet.get_id_from_mxid(user_id)) + TelegramBridge().run() diff --git a/mautrix_telegram/config.py b/mautrix_telegram/config.py index 108dceb2..a66819bf 100644 --- a/mautrix_telegram/config.py +++ b/mautrix_telegram/config.py @@ -48,6 +48,8 @@ class Config(BaseBridgeConfig): super().do_update(helper) copy, copy_dict, base = helper + copy("homeserver.asmux") + if "appservice.protocol" in self and "appservice.address" not in self: protocol, hostname, port = (self["appservice.protocol"], self["appservice.hostname"], self["appservice.port"]) @@ -102,6 +104,7 @@ class Config(BaseBridgeConfig): copy("bridge.plaintext_highlights") copy("bridge.public_portals") copy("bridge.sync_with_custom_puppets") + copy("bridge.sync_direct_chat_list") copy("bridge.login_shared_secret") copy("bridge.telegram_link_preview") copy("bridge.inline_images") diff --git a/mautrix_telegram/db/portal.py b/mautrix_telegram/db/portal.py index fa0fd9d1..344509e6 100644 --- a/mautrix_telegram/db/portal.py +++ b/mautrix_telegram/db/portal.py @@ -49,6 +49,10 @@ class Portal(Base): def get_by_tgid(cls, tgid: TelegramID, tg_receiver: TelegramID) -> Optional['Portal']: return cls._select_one_or_none(cls.c.tgid == tgid, cls.c.tg_receiver == tg_receiver) + @classmethod + def find_private_chats(cls, tg_receiver: TelegramID) -> Iterable['Portal']: + yield from cls._select_all(cls.c.tg_receiver == tg_receiver, cls.c.peer_type == "user") + @classmethod def get_by_mxid(cls, mxid: RoomID) -> Optional['Portal']: return cls._select_one_or_none(cls.c.mxid == mxid) diff --git a/mautrix_telegram/example-config.yaml b/mautrix_telegram/example-config.yaml index 578b853b..c9029639 100644 --- a/mautrix_telegram/example-config.yaml +++ b/mautrix_telegram/example-config.yaml @@ -7,6 +7,7 @@ homeserver: # Whether or not to verify the SSL certificate of the homeserver. # Only applies if address starts with https:// verify_ssl: true + asmux: false # Application service host/registration related details # Changing these values requires regeneration of the registration. @@ -163,9 +164,13 @@ bridge: plaintext_highlights: false # Whether or not to make portals of publicly joinable channels/supergroups publicly joinable on Matrix. public_portals: true - # Whether or not to use /sync to get presence, read receipts and typing notifications when using - # your own Matrix account as the Matrix puppet for your Telegram account. + # Whether or not to use /sync to get presence, read receipts and typing notifications + # when double puppeting is enabled sync_with_custom_puppets: true + # Whether or not to update the m.direct account data event when double puppeting is enabled. + # Note that updating the m.direct event is not atomic (except with mautrix-asmux) + # and is therefore prone to race conditions. + sync_direct_chat_list: false # Shared secret for https://github.com/devture/matrix-synapse-shared-secret-auth # # If set, custom puppets will be enabled automatically for local users diff --git a/mautrix_telegram/portal/metadata.py b/mautrix_telegram/portal/metadata.py index ff31e30e..3432ef29 100644 --- a/mautrix_telegram/portal/metadata.py +++ b/mautrix_telegram/portal/metadata.py @@ -220,7 +220,9 @@ class PortalMetadata(BasePortal, ABC): puppet = await p.Puppet.get_by_custom_mxid(user.mxid) if puppet: try: - await puppet.intent.ensure_joined(self.mxid) + did_join = await puppet.intent.ensure_joined(self.mxid) + if isinstance(user, u.User) and did_join and self.peer_type == "user": + await user.update_direct_chats({self.main_intent.mxid: [self.mxid]}) except Exception: self.log.exception("Failed to ensure %s is joined to portal", user.mxid) diff --git a/mautrix_telegram/user.py b/mautrix_telegram/user.py index 73a42fe5..90289eed 100644 --- a/mautrix_telegram/user.py +++ b/mautrix_telegram/user.py @@ -28,12 +28,12 @@ from telethon.tl.functions.account import UpdateStatusRequest from mautrix.client import Client from mautrix.errors import MatrixRequestError -from mautrix.types import UserID +from mautrix.types import UserID, RoomID from mautrix.bridge import BaseUser from mautrix.util.logging import TraceLogger from .types import TelegramID -from .db import User as DBUser +from .db import User as DBUser, Portal as DBPortal from .abstract_user import AbstractUser from . import portal as po, puppet as pu @@ -79,6 +79,7 @@ class User(AbstractUser, BaseUser): self.db_portals = db_portals or [] self._db_instance = db_instance self._ensure_started_lock = asyncio.Lock() + self.dm_update_lock = asyncio.Lock() self.command_status = None @@ -340,6 +341,13 @@ class User(AbstractUser, BaseUser): except Exception: self.log.exception(f"Error while {action}") + async def get_direct_chats(self) -> Dict[UserID, List[RoomID]]: + return { + pu.Puppet.get_mxid_from_id(portal.tgid): [portal.mxid] + for portal in DBPortal.find_private_chats(self.tgid) + if portal.mxid + } + async def sync_dialogs(self) -> None: if self.is_bot: return @@ -378,6 +386,7 @@ class User(AbstractUser, BaseUser): index += 1 await self.save(portals=True) await asyncio.gather(*creators) + await self.update_direct_chats() self.log.debug("Dialog syncing complete") async def register_portal(self, portal: po.Portal) -> None: @@ -482,6 +491,7 @@ class User(AbstractUser, BaseUser): def init(context: 'Context') -> Iterable[Awaitable['User']]: global config config = context.config + User.bridge = context.bridge return (User.from_db(db_user).try_ensure_started() for db_user in DBUser.all_with_tgid()) diff --git a/requirements.txt b/requirements.txt index b1bbd1f3..fbba7103 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,6 @@ ruamel.yaml>=0.15.35,<0.17 python-magic>=0.4,<0.5 commonmark>=0.8,<0.10 aiohttp>=3,<4 -mautrix==0.7.0rc1 +mautrix==0.7.0rc2 telethon>=1.16,<1.17 telethon-session-sqlalchemy>=0.2.14,<0.3