diff --git a/mautrix_telegram/formatter/from_telegram.py b/mautrix_telegram/formatter/from_telegram.py index 590eaf5f..998a4117 100644 --- a/mautrix_telegram/formatter/from_telegram.py +++ b/mautrix_telegram/formatter/from_telegram.py @@ -53,6 +53,7 @@ from mautrix.types import Format, MessageType, TextMessageEventContent from .. import abstract_user as au, portal as po, puppet as pu, user as u from ..db import Message as DBMessage, TelegramFile as DBTelegramFile +from ..tgclient import MautrixTelegramClient from ..types import TelegramID from ..util.file_transfer import UnicodeCustomEmoji, transfer_custom_emojis_to_matrix @@ -60,7 +61,7 @@ log: logging.Logger = logging.getLogger("mau.fmt.tg") async def _add_forward_header( - source: au.AbstractUser, content: TextMessageEventContent, fwd_from: MessageFwdHeader + client: MautrixTelegramClient, content: TextMessageEventContent, fwd_from: MessageFwdHeader ) -> None: fwd_from_html, fwd_from_text = None, None if isinstance(fwd_from.from_id, PeerUser): @@ -81,7 +82,7 @@ async def _add_forward_header( if not fwd_from_text: try: - user = await source.client.get_entity(fwd_from.from_id) + user = await client.get_entity(fwd_from.from_id) if user: fwd_from_text, _ = pu.Puppet.get_displayname(user, False) fwd_from_html = f"{escape(fwd_from_text)}" @@ -104,7 +105,7 @@ async def _add_forward_header( fwd_from_html = f"channel {escape(fwd_from_text)}" else: try: - channel = await source.client.get_entity(fwd_from.from_id) + channel = await client.get_entity(fwd_from.from_id) if channel: fwd_from_text = f"channel {channel.title}" fwd_from_html = f"channel {escape(channel.title)}" @@ -135,12 +136,14 @@ class ReuploadedCustomEmoji(MessageEntityCustomEmoji): async def _convert_custom_emoji( - source: au.AbstractUser, entities: list[TypeMessageEntity] + source: au.AbstractUser, + entities: list[TypeMessageEntity], + client: MautrixTelegramClient | None = None, ) -> None: emoji_ids = [ entity.document_id for entity in entities if isinstance(entity, MessageEntityCustomEmoji) ] - custom_emojis = await transfer_custom_emojis_to_matrix(source, emoji_ids) + custom_emojis = await transfer_custom_emojis_to_matrix(source, emoji_ids, client=client) if len(custom_emojis) > 0: for i, entity in enumerate(entities): if isinstance(entity, MessageEntityCustomEmoji): @@ -150,17 +153,20 @@ async def _convert_custom_emoji( async def telegram_to_matrix( evt: Message | SponsoredMessage, source: au.AbstractUser, + client: MautrixTelegramClient | None = None, override_text: str = None, override_entities: list[TypeMessageEntity] = None, require_html: bool = False, ) -> TextMessageEventContent: + if not client: + client = source.client content = TextMessageEventContent( msgtype=MessageType.TEXT, body=override_text or evt.message, ) entities = override_entities or evt.entities if entities: - await _convert_custom_emoji(source, entities) + await _convert_custom_emoji(source, entities, client=client) content.format = Format.HTML html = await _telegram_entities_to_matrix_catch(add_surrogate(content.body), entities) content.formatted_body = del_surrogate(html) @@ -169,7 +175,7 @@ async def telegram_to_matrix( content.ensure_has_html() if getattr(evt, "fwd_from", None): - await _add_forward_header(source, content, evt.fwd_from) + await _add_forward_header(client, content, evt.fwd_from) if isinstance(evt, Message) and evt.post and evt.post_author: content.ensure_has_html() diff --git a/mautrix_telegram/portal.py b/mautrix_telegram/portal.py index 75fe05a5..577fd1bd 100644 --- a/mautrix_telegram/portal.py +++ b/mautrix_telegram/portal.py @@ -2785,6 +2785,7 @@ class Portal(DBPortal, BasePortal): async def _convert_batch_msg( self, source: u.User, + client: MautrixTelegramClient, msg: Message, add_member: Callable[[IntentAPI, str, ContentURI], Awaitable[None]], ) -> tuple[putil.ConvertedMessage, IntentAPI]: @@ -2800,8 +2801,8 @@ class Portal(DBPortal, BasePortal): if sender: intent = sender.intent_for(self) if not sender.displayname: - entity = await source.client.get_entity(sender.peer) - await sender.update_info(source, entity) + entity = await client.get_entity(sender.peer) + await sender.update_info(source, entity, client_override=client) else: intent = self.main_intent if intent.api.is_real_user and not self._can_double_puppet_backfill(intent.mxid): @@ -2809,7 +2810,7 @@ class Portal(DBPortal, BasePortal): if sender: await add_member(intent, sender.displayname, sender.avatar_url) is_bot = sender.is_bot if sender else False - converted = await self._msg_conv.convert(source, intent, is_bot, msg) + converted = await self._msg_conv.convert(source, intent, is_bot, msg, client=client) return converted, intent async def _wrap_batch_msg( @@ -2902,7 +2903,7 @@ class Portal(DBPortal, BasePortal): first_id = msg.id before_first_msg_timestamp = int(msg.date.timestamp() * 1000) - 1 - converted, intent = await self._convert_batch_msg(source, msg, add_member) + converted, intent = await self._convert_batch_msg(source, client, msg, add_member) if converted is None: continue events.append(await self._wrap_batch_msg(intent, msg, converted)) diff --git a/mautrix_telegram/portal_util/message_convert.py b/mautrix_telegram/portal_util/message_convert.py index 84f3f707..c59e649a 100644 --- a/mautrix_telegram/portal_util/message_convert.py +++ b/mautrix_telegram/portal_util/message_convert.py @@ -78,6 +78,7 @@ from mautrix.util.logging import TraceLogger from .. import abstract_user as au, formatter, matrix as m, portal as po, puppet as pu, util from ..config import Config from ..db import Message as DBMessage, TelegramFile as DBTelegramFile +from ..tgclient import MautrixTelegramClient from ..types import TelegramID from ..util import sane_mimetypes @@ -149,12 +150,15 @@ class TelegramMessageConverter: is_bot: bool, evt: Message, no_reply_fallback: bool = False, + client: MautrixTelegramClient | None = None, ) -> ConvertedMessage | None: + if not client: + client = source.client if hasattr(evt, "media") and isinstance(evt.media, self._allowed_media): convert_media = self._media_converters[type(evt.media)] - converted = await convert_media(source=source, intent=intent, evt=evt) + converted = await convert_media(source=source, intent=intent, evt=evt, client=client) elif evt.message: - converted = await self._convert_text(source, intent, is_bot, evt) + converted = await self._convert_text(source, intent, is_bot, evt, client) else: self.log.debug("Unhandled Telegram message %d", evt.id) return @@ -326,9 +330,14 @@ class TelegramMessageConverter: return beeper_link_preview async def _convert_text( - self, source: au.AbstractUser, intent: IntentAPI, is_bot: bool, evt: Message + self, + source: au.AbstractUser, + intent: IntentAPI, + is_bot: bool, + evt: Message, + client: MautrixTelegramClient, ) -> ConvertedMessage: - content = await formatter.telegram_to_matrix(evt, source) + content = await formatter.telegram_to_matrix(evt, source, client) if is_bot and self.portal.get_config("bot_messages_as_notices"): content.msgtype = MessageType.NOTICE @@ -344,7 +353,11 @@ class TelegramMessageConverter: return ConvertedMessage(content=content) async def _convert_photo( - self, source: au.AbstractUser, intent: IntentAPI, evt: Message + self, + source: au.AbstractUser, + intent: IntentAPI, + evt: Message, + client: MautrixTelegramClient, ) -> ConvertedMessage | None: media: MessageMediaPhoto = evt.media if media.photo is None and media.ttl_seconds: @@ -362,7 +375,7 @@ class TelegramMessageConverter: ) ) file = await util.transfer_file_to_matrix( - source.client, + client, intent, loc, encrypt=self.portal.encrypted, @@ -388,7 +401,9 @@ class TelegramMessageConverter: content.file = file.decryption_info else: content.url = file.mxc - caption_content = await formatter.telegram_to_matrix(evt, source) if evt.message else None + caption_content = ( + await formatter.telegram_to_matrix(evt, source, client) if evt.message else None + ) return ConvertedMessage( content=content, caption=caption_content, @@ -396,7 +411,11 @@ class TelegramMessageConverter: ) async def _convert_document( - self, source: au.AbstractUser, intent: IntentAPI, evt: Message + self, + source: au.AbstractUser, + intent: IntentAPI, + evt: Message, + client: MautrixTelegramClient, ) -> ConvertedMessage | None: document = evt.media.document @@ -419,7 +438,7 @@ class TelegramMessageConverter: parallel_id = source.tgid if self.config["bridge.parallel_file_transfer"] else None tgs_convert = self.config["bridge.animated_sticker"] file = await util.transfer_file_to_matrix( - source.client, + client, intent, document, thumb_loc, @@ -486,7 +505,9 @@ class TelegramMessageConverter: else: content.url = file.mxc - caption_content = await formatter.telegram_to_matrix(evt, source) if evt.message else None + caption_content = ( + await formatter.telegram_to_matrix(evt, source, client) if evt.message else None + ) return ConvertedMessage( type=event_type, @@ -527,13 +548,17 @@ class TelegramMessageConverter: return ConvertedMessage(content=content) @staticmethod - async def _convert_unsupported(source: au.AbstractUser, evt: Message, **_) -> ConvertedMessage: + async def _convert_unsupported( + source: au.AbstractUser, evt: Message, client: MautrixTelegramClient, **_ + ) -> ConvertedMessage: override_text = ( "This message is not supported on your version of Mautrix-Telegram. " "Please check https://github.com/mautrix/telegram or ask your " "bridge administrator about possible updates." ) - content = await formatter.telegram_to_matrix(evt, source, override_text=override_text) + content = await formatter.telegram_to_matrix( + evt, source, client, override_text=override_text + ) content.msgtype = MessageType.NOTICE content["fi.mau.telegram.unsupported"] = True return ConvertedMessage(content=content) @@ -589,7 +614,9 @@ class TelegramMessageConverter: content["fi.mau.telegram.dice"] = {"emoticon": roll.emoticon, "value": roll.value} return ConvertedMessage(content=content) - async def _convert_game(self, source: au.AbstractUser, evt: Message, **_) -> ConvertedMessage: + async def _convert_game( + self, source: au.AbstractUser, evt: Message, client: MautrixTelegramClient, **_ + ) -> ConvertedMessage: game: Game = evt.media.game play_id = self._encode_msgid(source, evt) command = f"{self.command_prefix} play {play_id}" @@ -599,7 +626,7 @@ class TelegramMessageConverter: ] content = await formatter.telegram_to_matrix( - evt, source, override_text=override_text, override_entities=override_entities + evt, source, client, override_text=override_text, override_entities=override_entities ) content.msgtype = MessageType.NOTICE content["fi.mau.telegram.game"] = play_id @@ -607,7 +634,9 @@ class TelegramMessageConverter: return ConvertedMessage(content=content) @staticmethod - async def _convert_contact(source: au.AbstractUser, evt: Message, **_) -> ConvertedMessage: + async def _convert_contact( + source: au.AbstractUser, evt: Message, client: MautrixTelegramClient, **_ + ) -> ConvertedMessage: contact: MessageMediaContact = evt.media name = " ".join(x for x in [contact.first_name, contact.last_name] if x) formatted_phone = f"+{contact.phone_number}" @@ -633,8 +662,8 @@ class TelegramMessageConverter: puppet = await pu.Puppet.get_by_tgid(TelegramID(contact.user_id)) if not puppet.displayname: try: - entity = await source.client.get_entity(PeerUser(contact.user_id)) - await puppet.update_info(source, entity) + entity = await client.get_entity(PeerUser(contact.user_id)) + await puppet.update_info(source, entity, client_override=client) except Exception as e: source.log.warning(f"Failed to sync puppet info of received contact: {e}") else: diff --git a/mautrix_telegram/puppet.py b/mautrix_telegram/puppet.py index 47883fbb..51cded0a 100644 --- a/mautrix_telegram/puppet.py +++ b/mautrix_telegram/puppet.py @@ -29,8 +29,6 @@ from telethon.tl.types import ( PeerChat, PeerUser, TypeChatPhoto, - TypeInputPeer, - TypeInputUser, TypePeer, TypeUserProfilePhoto, UpdateUserName, @@ -48,6 +46,7 @@ from mautrix.util.simple_template import SimpleTemplate from . import abstract_user as au, portal as p, util from .config import Config from .db import Puppet as DBPuppet +from .tgclient import MautrixTelegramClient from .types import TelegramID if TYPE_CHECKING: @@ -147,9 +146,6 @@ class Puppet(DBPuppet, BasePuppet): def plain_displayname(self) -> str: return self.displayname_template.parse(self.displayname) or self.displayname - def get_input_entity(self, user: au.AbstractUser) -> Awaitable[TypeInputPeer | TypeInputUser]: - return user.client.get_input_entity(self.peer) - def intent_for(self, portal: p.Portal) -> IntentAPI: if portal.tgid == self.tgid: return self.default_mxid_intent @@ -255,7 +251,12 @@ class Puppet(DBPuppet, BasePuppet): except Exception: source.log.exception(f"Failed to update info of {self.tgid}") - async def update_info(self, source: au.AbstractUser, info: User | Channel) -> None: + async def update_info( + self, + source: au.AbstractUser, + info: User | Channel, + client_override: MautrixTelegramClient | None = None, + ) -> None: is_bot = False if isinstance(info, Channel) else info.bot is_premium = False if isinstance(info, Channel) else info.premium is_channel = isinstance(info, Channel) @@ -277,8 +278,14 @@ class Puppet(DBPuppet, BasePuppet): if not self.disable_updates: try: - changed = await self.update_displayname(source, info) or changed - changed = await self.update_avatar(source, info.photo) or changed + changed = ( + await self.update_displayname(source, info, client_override=client_override) + or changed + ) + changed = ( + await self.update_avatar(source, info.photo, client_override=client_override) + or changed + ) except Exception: self.log.exception(f"Failed to update info from source {source.tgid}") @@ -293,7 +300,10 @@ class Puppet(DBPuppet, BasePuppet): await portal.update_info_from_puppet(self) async def update_displayname( - self, source: au.AbstractUser, info: User | Channel | UpdateUserName + self, + source: au.AbstractUser, + info: User | Channel | UpdateUserName, + client_override: MautrixTelegramClient | None = None, ) -> bool: if self.disable_updates: return False @@ -320,7 +330,7 @@ class Puppet(DBPuppet, BasePuppet): return False if isinstance(info, UpdateUserName): - info = await source.client.get_entity(self.peer) + info = await (client_override or source.client).get_entity(self.peer) if isinstance(info, Channel) or not info.contact: self.displayname_contact = False elif not self.displayname_contact: @@ -357,7 +367,10 @@ class Puppet(DBPuppet, BasePuppet): return False async def update_avatar( - self, source: au.AbstractUser, photo: TypeUserProfilePhoto | TypeChatPhoto + self, + source: au.AbstractUser, + photo: TypeUserProfilePhoto | TypeChatPhoto, + client_override: MautrixTelegramClient | None = None, ) -> bool: if self.disable_updates: return False @@ -376,11 +389,14 @@ class Puppet(DBPuppet, BasePuppet): self.photo_id = "" self.avatar_url = None elif self.photo_id != photo_id or not self.avatar_url: + client = client_override or source.client file = await util.transfer_file_to_matrix( - client=source.client, + client=client, intent=self.default_mxid_intent, location=InputPeerPhotoFileLocation( - peer=await self.get_input_entity(source), photo_id=photo.photo_id, big=True + peer=await client.get_input_entity(self.peer), + photo_id=photo.photo_id, + big=True, ), async_upload=self.config["homeserver.async_media"], ) diff --git a/mautrix_telegram/util/file_transfer.py b/mautrix_telegram/util/file_transfer.py index 6bf47662..2fa885da 100644 --- a/mautrix_telegram/util/file_transfer.py +++ b/mautrix_telegram/util/file_transfer.py @@ -229,8 +229,10 @@ class UnicodeCustomEmoji(NamedTuple): async def transfer_custom_emojis_to_matrix( - source: au.AbstractUser, emoji_ids: list[int] + source: au.AbstractUser, emoji_ids: list[int], client: MautrixTelegramClient | None = None ) -> dict[int, DBTelegramFile | UnicodeCustomEmoji]: + if not client: + client = source.client emoji_ids = set(emoji_ids) existing_unicode = {} for emoji_id in emoji_ids: @@ -249,7 +251,7 @@ async def transfer_custom_emojis_to_matrix( if not_existing_ids: log.debug(f"Transferring custom emojis through {source.mxid}: {not_existing_ids}") - documents: list[Document] = await source.client( + documents: list[Document] = await client( GetCustomEmojiDocumentsRequest(document_id=not_existing_ids) ) @@ -261,7 +263,7 @@ async def transfer_custom_emojis_to_matrix( async def transfer(document: Document) -> None: async with transfer_sema: file_map[document.id] = await transfer_file_to_matrix( - source.client, + client, source.bridge.az.intent, document, is_sticker=True,