diff --git a/mautrix_telegram/abstract_user.py b/mautrix_telegram/abstract_user.py index 772ceb46..758bcb2e 100644 --- a/mautrix_telegram/abstract_user.py +++ b/mautrix_telegram/abstract_user.py @@ -32,7 +32,7 @@ from telethon.tl.types import ( UpdateShortChatMessage, UpdateShortMessage, UpdateUserName, UpdateUserPhoto, UpdateUserStatus, UpdateUserTyping, User, UserStatusOffline, UserStatusOnline, UpdateReadHistoryInbox, UpdateReadChannelInbox, MessageEmpty, UpdateFolderPeers, UpdatePinnedDialogs, - UpdateNotifySettings) + UpdateNotifySettings, UpdateChannelUserTyping) from mautrix.types import UserID, PresenceState from mautrix.errors import MatrixError @@ -58,6 +58,7 @@ MAX_DELETIONS: int = 10 UpdateMessage = Union[UpdateShortChatMessage, UpdateShortMessage, UpdateNewChannelMessage, UpdateNewMessage, UpdateEditMessage, UpdateEditChannelMessage] UpdateMessageContent = Union[UpdateShortMessage, UpdateShortChatMessage, Message, MessageService] +UpdateTyping = Union[UpdateUserTyping, UpdateChatUserTyping, UpdateChannelUserTyping] UPDATE_TIME = Histogram("bridge_telegram_update", "Time spent processing Telegram updates", ("update_type",)) @@ -244,7 +245,7 @@ class AbstractUser(ABC): await self.delete_message(update) elif isinstance(update, UpdateDeleteChannelMessages): await self.delete_channel_message(update) - elif isinstance(update, (UpdateChatUserTyping, UpdateUserTyping)): + elif isinstance(update, (UpdateChatUserTyping, UpdateChannelUserTyping, UpdateUserTyping)): await self.update_typing(update) elif isinstance(update, UpdateUserStatus): await self.update_status(update) @@ -345,16 +346,27 @@ class AbstractUser(ABC): await portal.set_telegram_admin(TelegramID(update.user_id)) - async def update_typing(self, update: Union[UpdateUserTyping, UpdateChatUserTyping]) -> None: + async def update_typing(self, update: UpdateTyping) -> None: + sender = None if isinstance(update, UpdateUserTyping): portal = po.Portal.get_by_tgid(TelegramID(update.user_id), self.tgid, "user") - else: + sender = pu.Puppet.get(TelegramID(update.user_id)) + elif isinstance(update, UpdateChannelUserTyping): + portal = po.Portal.get_by_tgid(TelegramID(update.channel_id)) + elif isinstance(update, UpdateChatUserTyping): portal = po.Portal.get_by_tgid(TelegramID(update.chat_id)) - - if not portal or not portal.mxid: + else: + return + + if isinstance(update, (UpdateChannelUserTyping, UpdateChatUserTyping)): + # Can typing notifications come from non-user peers? + if not update.from_id.user_id: + return + sender = pu.Puppet.get(TelegramID(update.from_id.user_id)) + + if not sender or not portal or not portal.mxid: return - sender = pu.Puppet.get(TelegramID(update.user_id)) await portal.handle_telegram_typing(sender, update) async def _handle_entity_updates(self, entities: Dict[int, Union[User, Chat, Channel]] diff --git a/mautrix_telegram/portal/telegram.py b/mautrix_telegram/portal/telegram.py index 65d81ca4..3504e46e 100644 --- a/mautrix_telegram/portal/telegram.py +++ b/mautrix_telegram/portal/telegram.py @@ -34,7 +34,8 @@ from telethon.tl.types import ( MessageMediaPhoto, MessageMediaDice, MessageMediaGame, MessageMediaUnsupported, PeerUser, PhotoCachedSize, TypeChannelParticipant, TypeChatParticipant, TypeDocumentAttribute, TypeMessageAction, TypePhotoSize, PhotoSize, UpdateChatUserTyping, UpdateUserTyping, - MessageEntityPre, ChatPhotoEmpty, DocumentAttributeImageSize, DocumentAttributeAnimated) + MessageEntityPre, ChatPhotoEmpty, DocumentAttributeImageSize, DocumentAttributeAnimated, + UpdateChannelUserTyping, SendMessageTypingAction) from mautrix.appservice import IntentAPI from mautrix.types import (EventID, UserID, ImageInfo, ThumbnailInfo, RelatesTo, MessageType, @@ -56,6 +57,7 @@ if TYPE_CHECKING: InviteList = Union[UserID, List[UserID]] TypeParticipant = Union[TypeChatParticipant, TypeChannelParticipant] +UpdateTyping = Union[UpdateUserTyping, UpdateChatUserTyping, UpdateChannelUserTyping] DocAttrs = NamedTuple("DocAttrs", name=Optional[str], mime_type=Optional[str], is_sticker=bool, sticker_alt=Optional[str], width=int, height=int, is_gif=bool) @@ -63,9 +65,10 @@ config: Optional['Config'] = None class PortalTelegram(BasePortal, ABC): - async def handle_telegram_typing(self, user: p.Puppet, - _: Union[UpdateUserTyping, UpdateChatUserTyping]) -> None: - await user.intent_for(self).set_typing(self.mxid, is_typing=True) + async def handle_telegram_typing(self, user: p.Puppet, update: UpdateTyping) -> None: + is_typing = isinstance(update.action, SendMessageTypingAction) + # Always use the default puppet here to avoid any problems with echoing + await user.default_mxid_intent.set_typing(self.mxid, is_typing=is_typing) def _get_external_url(self, evt: Message) -> Optional[str]: if self.peer_type == "channel" and self.username is not None: