From 385a4eed494aed0a101421eb2d0130a8683c5ca0 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 4 Feb 2018 23:59:20 +0200 Subject: [PATCH] Add group upgrade command and basic supergroup power level bridging. Fixes #27 Fixes #31 --- README.md | 8 ++++-- mautrix_telegram/commands.py | 19 ++++++++++++- mautrix_telegram/portal.py | 55 ++++++++++++++++++++++++++++++------ 3 files changed, 69 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index b7ba988f..990a78e9 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,9 @@ The bridge does not do this automatically. * [ ] Pinning messages * [ ] Power level * [x] Normal chats - * [ ] Supergroups/channels (currently only creator level bridged) + * [ ] Non-hardcoded PL requirements + * [x] Supergroups/channels + * [ ] Precise bridging (non-hardcoded PL requirements, bridge specific permissions, etc..) * [ ] Membership actions * [x] Inviting * [x] Puppets @@ -107,7 +109,7 @@ The bridge does not do this automatically. * [x] Typing notifications * [x] Pinning messages * [x] Admin/chat creator status - * [ ] Supergroup/channel permissions + * [ ] Supergroup/channel permissions (precise per-user not supported in Matrix) * [x] Membership actions * [x] Inviting * [x] Kicking @@ -134,7 +136,7 @@ The bridge does not do this automatically. * [x] Starting private chats (`pm`) * [x] Joining chats with invite links (`join`) * [x] Creating a Telegram chat for an existing Matrix room (`create`) - * [ ] Upgrading the chat of a portal room into a supergroup (`upgrade`) + * [x] Upgrading the chat of a portal room into a supergroup (`upgrade`) * [ ] Change public/private status of supergroup/channel (`setpublic`) * [ ] Change username of supergroup/channel (`groupname`) * [x] Getting the Telegram invite link to a Matrix room (`invitelink`) diff --git a/mautrix_telegram/commands.py b/mautrix_telegram/commands.py index e3577ac4..25f37f8b 100644 --- a/mautrix_telegram/commands.py +++ b/mautrix_telegram/commands.py @@ -370,7 +370,24 @@ class CommandHandler: @command_handler def upgrade(self, sender, args): - self.reply("Not yet implemented.") + if not sender.logged_in: + return self.reply("This command requires you to be logged in.") + + portal = po.Portal.get_by_mxid(self._room_id) + if not portal: + return self.reply("This is not a portal room.") + elif portal.peer_type == "channel": + return self.reply("This is already a supergroup or a channel.") + elif portal.peer_type == "user": + return self.reply("You can't upgrade private chats.") + + try: + portal.upgrade_telegram_chat(sender) + return self.reply(f"Group upgraded to supergroup. New ID: {portal.tgid}") + except ChatAdminRequiredError: + return self.reply("You don't have the permission to upgrade this group.") + except ValueError as e: + return self.reply(e.args[0]) @command_handler def setpublic(self, sender, args): diff --git a/mautrix_telegram/portal.py b/mautrix_telegram/portal.py index 0fb9b367..0b7547ee 100644 --- a/mautrix_telegram/portal.py +++ b/mautrix_telegram/portal.py @@ -159,7 +159,7 @@ class Portal: levels["events"]["m.room.name"] = power_level_requirement levels["events"]["m.room.avatar"] = power_level_requirement levels["events"]["m.room.topic"] = 50 if self.peer_type == "channel" else 100 - levels["events"]["m.room.power_levels"] = 95 + levels["events"]["m.room.power_levels"] = 75 self.main_intent.set_power_levels(self.mxid, levels) self.update_after_create(user, entity, direct, puppet) @@ -313,7 +313,7 @@ class Portal: self.delete() del self.by_tgid[self.tgid_full] del self.by_mxid[self.mxid] - elif source: + elif source and source.tgid != user.tgid: target = user.get_input_entity(source) if self.peer_type == "chat": source.client(DeleteChatUserRequest(chat_id=self.tgid, user_id=target)) @@ -375,6 +375,7 @@ class Portal: deleter.client.delete_messages(self.peer, [message.tgid]) def handle_matrix_power_levels(self, sender, new_users, old_users): + # TODO handle all power level changes and bridge exact admin rights to supergroups/channels for user, level in new_users.items(): user_id = p.Puppet.get_id_from_mxid(user) if not user_id: @@ -383,8 +384,21 @@ class Portal: continue user_id = mx_user.tgid if user not in old_users or level != old_users[user]: - sender.client( - EditChatAdminRequest(chat_id=self.tgid, user_id=user_id, is_admin=level >= 50)) + if self.peer_type == "chat": + sender.client(EditChatAdminRequest( + chat_id=self.tgid, user_id=user_id, is_admin=level >= 50)) + elif self.peer_type == "channel": + moderator = level >= 50 + admin = level >= 75 + rights = ChannelAdminRights(change_info=moderator, post_messages=moderator, + edit_messages=moderator, delete_messages=moderator, + ban_users=moderator, invite_users=moderator, + invite_link=moderator, pin_messages=moderator, + add_admins=admin, manage_call=moderator) + sender.client( + EditAdminRequest(channel=self.get_input_entity(sender), + user_id=sender.client.get_input_entity(PeerUser(user_id)), + admin_rights=rights)) def handle_matrix_about(self, sender, about): if self.peer_type not in {"channel"}: @@ -449,6 +463,22 @@ class Portal: user_tgids.add(puppet_id) return user_tgids + def upgrade_telegram_chat(self, source): + if self.peer_type != "chat": + raise ValueError("Only normal group chats are upgradable to supergroups.") + + updates = source.client(MigrateChatRequest(chat_id=self.tgid)) + entity = None + for chat in updates.chats: + if isinstance(chat, Channel): + entity = chat + break + if not entity: + raise ValueError("Upgrade may have failed: output channel not found.") + self.peer_type = "channel" + self.migrate_and_save(entity.id) + self.update_info(source, entity) + def create_telegram_chat(self, source, supergroup=False): if not self.mxid: raise ValueError("Can't create Telegram chat for portal without Matrix room.") @@ -476,6 +506,7 @@ class Portal: self.tgid = entity.id self.tg_receiver = self.tgid + self.by_tgid[self.tgid_full] = self self.update_info(source, entity) self.save() @@ -663,17 +694,19 @@ class Portal: levels = self.main_intent.get_power_levels(self.mxid) changed = False - if levels["events"]["m.room.power_levels"] != 50: + admin_power_level = 75 if self.peer_type == "channel" else 50 + if levels["events"]["m.room.power_levels"] != admin_power_level: changed = True - levels["events"]["m.room.power_levels"] = 50 + levels["events"]["m.room.power_levels"] = admin_power_level for participant in participants: puppet = p.Puppet.get(participant.user_id) user = u.User.get_by_tgid(participant.user_id) + print(participant) new_level = 0 - if isinstance(participant, ChatParticipantAdmin): + if isinstance(participant, (ChatParticipantAdmin, ChannelParticipantAdmin)): new_level = 50 - elif isinstance(participant, ChatParticipantCreator): + elif isinstance(participant, (ChatParticipantCreator, ChannelParticipantCreator)): new_level = 95 if user and (user.mxid in levels["users"] or new_level > 0): levels["users"][user.mxid] = new_level @@ -705,8 +738,12 @@ class Portal: existing = DBPortal.query.get(self.tgid_full) if existing: self.db.object_session(existing).delete(existing) - del self.by_tgid[self.tgid_full] + try: + del self.by_tgid[self.tgid_full] + except KeyError: + pass self.tgid = new_id + self.tg_receiver = new_id self.by_tgid[self.tgid_full] = self self.save()