diff --git a/mautrix_telegram/portal.py b/mautrix_telegram/portal.py index 9bd75979..1d5877c5 100644 --- a/mautrix_telegram/portal.py +++ b/mautrix_telegram/portal.py @@ -23,6 +23,7 @@ from sqlite3 import IntegrityError from string import Template import asyncio import base64 +import itertools import random import time @@ -36,6 +37,7 @@ from telethon.errors import ( ReactionInvalidError, RPCError, ) +from telethon.tl.custom import Dialog from telethon.tl.functions.channels import ( CreateChannelRequest, EditPhotoRequest, @@ -54,6 +56,7 @@ from telethon.tl.functions.messages import ( ExportChatInviteRequest, GetMessageReactionsListRequest, GetMessagesReactionsRequest, + GetPeerDialogsRequest, MigrateChatRequest, SendReactionRequest, SetTypingRequest, @@ -66,6 +69,7 @@ from telethon.tl.types import ( ChannelFull, Chat, ChatBannedRights, + ChatEmpty, ChatFull, ChatPhoto, ChatPhotoEmpty, @@ -76,6 +80,7 @@ from telethon.tl.types import ( GeoPoint, InputChannel, InputChatUploadedPhoto, + InputDialogPeer, InputMediaUploadedDocument, InputMediaUploadedPhoto, InputPeerChannel, @@ -136,11 +141,13 @@ from telethon.tl.types import ( UpdatePhoneCall, UpdateUserTyping, User, + UserEmpty, UserFull, UserProfilePhoto, UserProfilePhotoEmpty, ) -from telethon.utils import encode_waveform +from telethon.tl.types.messages import PeerDialogs +from telethon.utils import encode_waveform, get_peer_id import attr from mautrix.appservice import DOUBLE_PUPPET_SOURCE_KEY, IntentAPI @@ -725,6 +732,7 @@ class Portal(DBPortal, BasePortal): entity: TypeChat | User = None, invites: InviteList = None, update_if_exists: bool = True, + from_dialog_sync: bool = False, client: MautrixTelegramClient | None = None, ) -> RoomID | None: if self.mxid: @@ -741,7 +749,9 @@ class Portal(DBPortal, BasePortal): return self.mxid async with self._room_create_lock: try: - return await self._create_matrix_room(user, entity, invites, client=client) + return await self._create_matrix_room( + user, entity, invites, client=client, from_dialog_sync=from_dialog_sync + ) except Exception: self.log.exception("Fatal error creating Matrix room") @@ -796,6 +806,7 @@ class Portal(DBPortal, BasePortal): user: au.AbstractUser, entity: TypeChat | User, invites: InviteList, + from_dialog_sync: bool, client: MautrixTelegramClient | None = None, ) -> RoomID | None: if self.mxid: @@ -807,6 +818,31 @@ class Portal(DBPortal, BasePortal): invites = invites or [] + dialog = None + if not from_dialog_sync and not user.is_bot: + self.log.debug("Fetching dialog info for new portal") + dialogs: PeerDialogs = await user.client( + GetPeerDialogsRequest(peers=[InputDialogPeer(await self.get_input_entity(user))]) + ) + if dialogs.chats and dialogs.chats[0].id == self.tgid: + entity = dialogs.chats[0] + self.log.debug("Got entity info from get dialogs request") + elif self.is_direct and dialogs.users: + for user in dialogs.users: + if user.id == self.tgid: + entity = user + self.log.debug("Got user entity info from get dialogs request") + break + if dialogs.dialogs: + entities = { + get_peer_id(x): x + for x in itertools.chain(dialogs.users, dialogs.chats) + if not isinstance(x, (UserEmpty, ChatEmpty)) + } + msg = dialogs.messages[0] if len(dialogs.messages) == 1 else None + dialog = Dialog(user.client, dialogs.dialogs[0], entities, msg) + self.log.debug("Got dialog info for new portal: %s", dialog) + if not entity: entity = await self.get_entity(user, client) self.log.trace("Fetched data: %s", entity) @@ -959,6 +995,10 @@ class Portal(DBPortal, BasePortal): self.log.debug(f"Matrix room created: {self.mxid}") await self.az.state_store.set_power_levels(self.mxid, power_levels) await user.register_portal(self) + if dialog and isinstance(user, u.User): + await user.post_sync_dialog( + self, puppet=None, was_created=True, **user.dialog_to_sync_args(dialog) + ) if not autojoin_invites or not self.is_direct: await self.invite_to_matrix(invites) diff --git a/mautrix_telegram/user.py b/mautrix_telegram/user.py index 4a911ce2..764803d9 100644 --- a/mautrix_telegram/user.py +++ b/mautrix_telegram/user.py @@ -490,13 +490,16 @@ class User(DBUser, AbstractUser, BaseUser): self.log.info(f"Creating portal for {portal.tgid_log} as part of backfill loop") try: await portal.create_matrix_room( - self, client=client, update_if_exists=False, invites=[self.mxid] + self, + client=client, + update_if_exists=False, + invites=[self.mxid], + from_dialog_sync=True, ) except Exception: self.log.exception(f"Error while creating {portal.tgid_log}") else: - puppet = await pu.Puppet.get_by_custom_mxid(self.mxid) - await self._post_sync_dialog(portal, puppet, was_created=True, **post_sync_args) + await self.post_sync_dialog(portal, puppet=None, was_created=True, **post_sync_args) async def update(self, update: TypeUpdate) -> bool: if not self.is_bot: @@ -746,12 +749,12 @@ class User(DBUser, AbstractUser, BaseUser): ) await self._mute_room(puppet, portal, update.notify_settings.mute_until.timestamp()) - async def _sync_dialog( - self, portal: po.Portal, dialog: Dialog, should_create: bool, puppet: pu.Puppet | None - ) -> None: - was_created = False - post_sync_args = { - "last_message_ts": cast(datetime, dialog.date).timestamp(), + @staticmethod + def dialog_to_sync_args(dialog: Dialog) -> dict: + return { + "last_message_ts": ( + cast(datetime, dialog.date).timestamp() if dialog.date else time.time() + ), "unread_count": dialog.unread_count, "max_read_id": dialog.dialog.read_inbox_max_id, "mute_until": ( @@ -762,6 +765,12 @@ class User(DBUser, AbstractUser, BaseUser): "pinned": dialog.pinned, "archived": dialog.archived, } + + async def _sync_dialog( + self, portal: po.Portal, dialog: Dialog, should_create: bool, puppet: pu.Puppet | None + ) -> None: + was_created = False + post_sync_args = self.dialog_to_sync_args(dialog) if portal.mxid: self.log.debug(f"Backfilling and updating {portal.tgid_log} (dialog sync)") try: @@ -775,7 +784,9 @@ class User(DBUser, AbstractUser, BaseUser): elif should_create: self.log.debug(f"Creating portal for {portal.tgid_log} immediately (dialog sync)") try: - await portal.create_matrix_room(self, dialog.entity, invites=[self.mxid]) + await portal.create_matrix_room( + self, dialog.entity, invites=[self.mxid], from_dialog_sync=True + ) was_created = True except Exception: self.log.exception(f"Error while creating {portal.tgid_log}") @@ -788,7 +799,7 @@ class User(DBUser, AbstractUser, BaseUser): extra_data=post_sync_args, ) if portal.mxid and puppet and puppet.is_real_user: - await self._post_sync_dialog( + await self.post_sync_dialog( portal=portal, puppet=puppet, was_created=was_created, @@ -796,10 +807,10 @@ class User(DBUser, AbstractUser, BaseUser): ) self.log.debug(f"_sync_dialog finished for {portal.tgid_log}") - async def _post_sync_dialog( + async def post_sync_dialog( self, portal: po.Portal, - puppet: pu.Puppet, + puppet: pu.Puppet | None, was_created: bool, max_read_id: int, last_message_ts: float, @@ -808,6 +819,10 @@ class User(DBUser, AbstractUser, BaseUser): pinned: bool, archived: bool, ) -> None: + if puppet is None: + puppet = await pu.Puppet.get_by_custom_mxid(self.mxid) + if not puppet or not puppet.is_real_user: + return self.log.debug( f"Running dialog post-sync for {portal.tgid_log} with args " f"{was_created=}, {max_read_id=}, {last_message_ts=}, {unread_count=}, "