From 565bb87470592e89127b2e152d1c0dd4946e2043 Mon Sep 17 00:00:00 2001 From: Malte E Date: Sun, 12 Jun 2022 21:46:11 +0200 Subject: [PATCH 1/2] implement handle_puppet_group_invite to auto-create groups --- .../commands/portal/create_chat.py | 1 - mautrix_telegram/matrix.py | 67 +++++++++++++++++-- mautrix_telegram/portal.py | 1 + mautrix_telegram/portal_util/power_levels.py | 6 +- 4 files changed, 68 insertions(+), 7 deletions(-) diff --git a/mautrix_telegram/commands/portal/create_chat.py b/mautrix_telegram/commands/portal/create_chat.py index f17e422b..512bd409 100644 --- a/mautrix_telegram/commands/portal/create_chat.py +++ b/mautrix_telegram/commands/portal/create_chat.py @@ -81,4 +81,3 @@ async def create(evt: CommandEvent) -> EventID: except ValueError as e: await portal.delete() return await evt.reply(e.args[0]) - return await evt.reply(f"Telegram chat created. ID: {portal.tgid}") diff --git a/mautrix_telegram/matrix.py b/mautrix_telegram/matrix.py index 8db3a31c..fce2a1d6 100644 --- a/mautrix_telegram/matrix.py +++ b/mautrix_telegram/matrix.py @@ -42,6 +42,8 @@ from mautrix.types import ( ) from . import commands as com, portal as po, puppet as pu, user as u +from .commands.portal.util import get_initial_state, user_has_power_level, warn_missing_power +from .types import TelegramID if TYPE_CHECKING: from .__main__ import TelegramBridge @@ -69,16 +71,71 @@ class MatrixHandler(BaseMatrixHandler): evt: StateEvent, members: list[UserID], ) -> None: - if self.az.bot_mxid not in members: + double_puppet = await pu.Puppet.get_by_custom_mxid(invited_by.mxid) + if not double_puppet or self.az.bot_mxid in members: + if self.az.bot_mxid not in members: + await puppet.default_mxid_intent.leave_room( + room_id, + reason="This ghost does not join multi-user rooms without the bridge bot.", + ) + else: + await puppet.default_mxid_intent.send_notice( + room_id, + "This ghost will remain inactive until a Telegram chat is created for this room.", + ) + return + + elif not await user_has_power_level( + evt.room_id, double_puppet.intent, invited_by, "bridge" + ): await puppet.default_mxid_intent.leave_room( - room_id, reason="This ghost does not join multi-user rooms without the bridge bot." + room_id, reason="You do not have the permissions to bridge this room." ) - else: - await puppet.default_mxid_intent.send_notice( + return + + await double_puppet.intent.invite_user(room_id, self.az.bot_mxid) + + title, about, levels, encrypted = await get_initial_state(double_puppet.intent, room_id) + if not title: + await puppet.default_mxid_intent.leave_room( + room_id, reason="Please set a title before inviting Telegram puppets." + ) + return + + portal = po.Portal( + tgid=TelegramID(0), + tg_receiver=TelegramID(0), + peer_type="channel", + mxid=evt.room_id, + title=title, + about=about, + encrypted=encrypted, + ) + await portal.az.intent.ensure_joined(room_id) + levels = await portal.az.intent.get_power_levels(room_id) + invited_by_level = levels.get_user_level(invited_by.mxid) + if invited_by_level > levels.get_user_level(self.az.bot_mxid): + levels.users[self.az.bot_mxid] = 100 if invited_by_level >= 100 else invited_by_level + await double_puppet.intent.set_power_levels(room_id, levels) + + invites, errors = await portal.get_telegram_users_in_matrix_room( + invited_by, pre_create=True + ) + if len(errors) > 0: + error_list = "\n".join(f"* [{mxid}](https://matrix.to/#/{mxid})" for mxid in errors) + await portal.az.intent.send_notice( room_id, - "This ghost will remain inactive until a Telegram chat is created for this room.", + f"Failed to add the following users to the chat:\n\n{error_list}\n\n" + "You can try `$cmdprefix+sp search -r ` to help the bridge find " + "those users.", ) + try: + await portal.create_telegram_chat(invited_by, invites=invites, supergroup=True) + except ValueError as e: + await portal.delete() + return await portal.az.intent.send_notice(room_id, e.args[0]) + async def handle_invite( self, room_id: RoomID, user_id: UserID, inviter: u.User, event_id: EventID ) -> None: diff --git a/mautrix_telegram/portal.py b/mautrix_telegram/portal.py index 8d7bc888..cdefd5e9 100644 --- a/mautrix_telegram/portal.py +++ b/mautrix_telegram/portal.py @@ -555,6 +555,7 @@ class Portal(DBPortal, BasePortal): await self.main_intent.set_power_levels(self.mxid, levels) await self.handle_matrix_power_levels(source, levels.users, {}, None) await self.update_bridge_info() + await self.main_intent.send_notice(self.mxid, f"Telegram chat created. ID: {self.tgid}") async def handle_matrix_invite( self, invited_by: u.User, puppet: p.Puppet | au.AbstractUser diff --git a/mautrix_telegram/portal_util/power_levels.py b/mautrix_telegram/portal_util/power_levels.py index b1a497b8..be2f44de 100644 --- a/mautrix_telegram/portal_util/power_levels.py +++ b/mautrix_telegram/portal_util/power_levels.py @@ -85,7 +85,11 @@ def get_base_power_levels( ) for evt_type, value in overrides.get("events", {}).items(): levels.events[EventType.find(evt_type)] = value - levels.users = overrides.get("users", {}) + userlevel_overrides = overrides.get("users", {}) + bot_level = levels.get_user_level(portal.main_intent.mxid) + for user, user_level in levels.users.items(): + if user_level < bot_level: + levels.users[user] = userlevel_overrides.get(user, 0) if portal.main_intent.mxid not in levels.users: levels.users[portal.main_intent.mxid] = 100 return levels From 3dd0c51be73ed1c9ecd8e7db0c6618cda0c16120 Mon Sep 17 00:00:00 2001 From: Malte E Date: Tue, 12 Jul 2022 21:25:52 +0200 Subject: [PATCH 2/2] add config option, update roadmap --- ROADMAP.md | 2 +- mautrix_telegram/config.py | 1 + mautrix_telegram/example-config.yaml | 3 +++ mautrix_telegram/matrix.py | 7 +++++++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/ROADMAP.md b/ROADMAP.md index adb624cc..8ea37c42 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -54,7 +54,7 @@ * [x] Automatic portal creation * [x] At startup * [x] When receiving invite or message - * [x] Private chat creation by inviting Matrix puppet of Telegram user to new room + * [x] Portal creation by inviting Matrix puppet of Telegram user to new room * [x] Option to use bot to relay messages for unauthenticated Matrix users (relaybot) * [x] Option to use own Matrix account for messages sent from other Telegram clients (double puppeting) * [ ] ‡ Calls (hard, not yet supported by Telethon) diff --git a/mautrix_telegram/config.py b/mautrix_telegram/config.py index 71772b80..861569af 100644 --- a/mautrix_telegram/config.py +++ b/mautrix_telegram/config.py @@ -130,6 +130,7 @@ class Config(BaseBridgeConfig): copy("bridge.sync_direct_chat_list") copy("bridge.double_puppet_server_map") copy("bridge.double_puppet_allow_discovery") + copy("bridge.create_group_on_invite") if "bridge.login_shared_secret" in self: base["bridge.login_shared_secret_map"] = { base["homeserver.domain"]: self["bridge.login_shared_secret"] diff --git a/mautrix_telegram/example-config.yaml b/mautrix_telegram/example-config.yaml index 63923ba8..c3b311b4 100644 --- a/mautrix_telegram/example-config.yaml +++ b/mautrix_telegram/example-config.yaml @@ -312,6 +312,9 @@ bridge: kick_on_logout: true # Should the "* user joined Telegram" notice always be marked as read automatically? always_read_joined_telegram_notice: true + # auto-create group chat portals by inviting Matrix puppet of Telegram user to a room + # requires double-puppeting to be enabled + create_group_on_invite: true # Settings for backfilling messages from Telegram. backfill: # Whether or not the Telegram ghosts of logged in Matrix users should be diff --git a/mautrix_telegram/matrix.py b/mautrix_telegram/matrix.py index fce2a1d6..b1eceacd 100644 --- a/mautrix_telegram/matrix.py +++ b/mautrix_telegram/matrix.py @@ -71,6 +71,11 @@ class MatrixHandler(BaseMatrixHandler): evt: StateEvent, members: list[UserID], ) -> None: + if not invited_by.is_logged_in: + await puppet.default_mxid_intent.leave_room( + room_id, reason="You are not logged into this Telegram bridge" + ) + return double_puppet = await pu.Puppet.get_by_custom_mxid(invited_by.mxid) if not double_puppet or self.az.bot_mxid in members: if self.az.bot_mxid not in members: @@ -92,6 +97,8 @@ class MatrixHandler(BaseMatrixHandler): room_id, reason="You do not have the permissions to bridge this room." ) return + elif not self.config["bridge.create_group_on_invite"]: + return await double_puppet.intent.invite_user(room_id, self.az.bot_mxid)