diff --git a/mautrix_telegram/abstract_user.py b/mautrix_telegram/abstract_user.py index c1da7f14..8d827002 100644 --- a/mautrix_telegram/abstract_user.py +++ b/mautrix_telegram/abstract_user.py @@ -38,6 +38,7 @@ from telethon.tl.types import ( PeerChannel, PeerChat, PeerUser, + PhoneCallRequested, TypeUpdate, UpdateChannel, UpdateChannelUserTyping, @@ -54,6 +55,7 @@ from telethon.tl.types import ( UpdateNewChannelMessage, UpdateNewMessage, UpdateNotifySettings, + UpdatePhoneCall, UpdatePinnedChannelMessages, UpdatePinnedDialogs, UpdatePinnedMessages, @@ -343,6 +345,8 @@ class AbstractUser(ABC): await self.delete_message(update) elif isinstance(update, UpdateDeleteChannelMessages): await self.delete_channel_message(update) + elif isinstance(update, UpdatePhoneCall): + await self.update_phone_call(update) elif isinstance(update, UpdateMessageReactions): await self.update_reactions(update) elif isinstance(update, (UpdateChatUserTyping, UpdateChannelUserTyping, UpdateUserTyping)): @@ -617,6 +621,19 @@ class AbstractUser(ABC): return await portal.handle_telegram_reactions(self, TelegramID(update.msg_id), update.reactions) + async def update_phone_call(self, update: UpdatePhoneCall) -> None: + self.log.debug("Phone call update %s", update) + if not isinstance(update.phone_call, PhoneCallRequested): + return + tgid = TelegramID(update.phone_call.participant_id) + if tgid == self.tgid: + tgid = update.phone_call.admin_id + portal = await po.Portal.get_by_tgid(tgid, tg_receiver=self.tgid, peer_type="user") + if not portal or not portal.mxid or not portal.allow_bridging: + return + sender = await pu.Puppet.get_by_tgid(TelegramID(update.phone_call.admin_id)) + await portal.handle_telegram_direct_call(self, sender, update) + async def update_channel(self, update: UpdateChannel) -> None: portal = await po.Portal.get_by_tgid(TelegramID(update.channel_id)) if not portal: diff --git a/mautrix_telegram/portal.py b/mautrix_telegram/portal.py index d47a7c10..2e6b27c0 100644 --- a/mautrix_telegram/portal.py +++ b/mautrix_telegram/portal.py @@ -95,6 +95,9 @@ from telethon.tl.types import ( MessageActionChatMigrateTo, MessageActionContactSignUp, MessageActionGameScore, + MessageActionGiftPremium, + MessageActionGroupCall, + MessageActionPhoneCall, MessageMediaGame, MessageMediaGeo, MessagePeerReaction, @@ -102,6 +105,10 @@ from telethon.tl.types import ( PeerChannel, PeerChat, PeerUser, + PhoneCallDiscardReasonBusy, + PhoneCallDiscardReasonDisconnect, + PhoneCallDiscardReasonMissed, + PhoneCallRequested, Photo, PhotoEmpty, ReactionCount, @@ -126,6 +133,7 @@ from telethon.tl.types import ( UpdateChatUserTyping, UpdateMessageReactions, UpdateNewMessage, + UpdatePhoneCall, UpdateUserTyping, User, UserFull, @@ -172,6 +180,7 @@ from mautrix.types import ( VideoInfo, ) from mautrix.util import background_task, magic, variation_selector +from mautrix.util.format_duration import format_duration from mautrix.util.message_send_checkpoint import MessageSendCheckpointStatus from mautrix.util.simple_lock import SimpleLock from mautrix.util.simple_template import SimpleTemplate @@ -3476,6 +3485,16 @@ class Portal(DBPortal, BasePortal): return False return True + async def handle_telegram_direct_call( + self, source: au.AbstractUser, sender: p.Puppet, update: UpdatePhoneCall + ) -> None: + if isinstance(update.phone_call, PhoneCallRequested): + call_type = "video call" if update.phone_call.video else "call" + await self._send_message( + sender.intent_for(self), + TextMessageEventContent(msgtype=MessageType.EMOTE, body=f"started a {call_type}"), + ) + async def handle_telegram_action( self, source: au.AbstractUser, sender: p.Puppet | None, update: MessageService ) -> None: @@ -3503,11 +3522,53 @@ class Portal(DBPortal, BasePortal): await self.delete_telegram_user(TelegramID(action.user_id), sender) elif isinstance(action, MessageActionChatMigrateTo): await self._migrate_and_save_telegram(TelegramID(action.channel_id)) - # TODO encrypt - await sender.intent_for(self).send_emote( - self.mxid, "upgraded this group to a supergroup." + await self._send_message( + sender.intent_for(self), + TextMessageEventContent( + msgtype=MessageType.EMOTE, + body="upgraded this group to a supergroup", + ), ) await self.update_bridge_info() + elif isinstance(action, MessageActionPhoneCall): + call_type = "Video call" if action.video else "Call" + end_reason = "ended" + if isinstance(action.reason, PhoneCallDiscardReasonMissed): + end_reason = "cancelled" if sender.tgid == source.tgid else "missed" + elif isinstance(action.reason, PhoneCallDiscardReasonBusy): + end_reason = "rejected" + elif isinstance(action.reason, PhoneCallDiscardReasonDisconnect): + end_reason = "disconnected" + body = f"{call_type} {end_reason}" + if action.duration: + body += f" ({format_duration(action.duration)}" + await self._send_message( + sender.intent_for(self), + TextMessageEventContent(msgtype=MessageType.NOTICE, body=body), + ) + elif isinstance(action, MessageActionGroupCall): + await self._send_message( + sender.intent_for(self), + TextMessageEventContent( + msgtype=MessageType.EMOTE, + body=( + "started a video chat" + if action.duration is None + else f"ended the video chat ({format_duration(action.duration)})" + ), + ), + ) + elif isinstance(action, MessageActionGiftPremium): + await self._send_message( + sender.intent_for(self), + TextMessageEventContent( + msgtype=MessageType.EMOTE, + body=( + f"gifted Telegram Premium for {action.months} " + f"({action.amount / 100} {action.currency})" + ), + ), + ) elif isinstance(action, MessageActionGameScore): # TODO handle game score pass