Add group upgrade command and basic supergroup power level bridging.

Fixes #27
Fixes #31
This commit is contained in:
Tulir Asokan
2018-02-04 23:59:20 +02:00
parent 51359747ca
commit 385a4eed49
3 changed files with 69 additions and 13 deletions
+5 -3
View File
@@ -75,7 +75,9 @@ The bridge does not do this automatically.
* [ ] Pinning messages * [ ] Pinning messages
* [ ] Power level * [ ] Power level
* [x] Normal chats * [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 * [ ] Membership actions
* [x] Inviting * [x] Inviting
* [x] Puppets * [x] Puppets
@@ -107,7 +109,7 @@ The bridge does not do this automatically.
* [x] Typing notifications * [x] Typing notifications
* [x] Pinning messages * [x] Pinning messages
* [x] Admin/chat creator status * [x] Admin/chat creator status
* [ ] Supergroup/channel permissions * [ ] Supergroup/channel permissions (precise per-user not supported in Matrix)
* [x] Membership actions * [x] Membership actions
* [x] Inviting * [x] Inviting
* [x] Kicking * [x] Kicking
@@ -134,7 +136,7 @@ The bridge does not do this automatically.
* [x] Starting private chats (`pm`) * [x] Starting private chats (`pm`)
* [x] Joining chats with invite links (`join`) * [x] Joining chats with invite links (`join`)
* [x] Creating a Telegram chat for an existing Matrix room (`create`) * [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 public/private status of supergroup/channel (`setpublic`)
* [ ] Change username of supergroup/channel (`groupname`) * [ ] Change username of supergroup/channel (`groupname`)
* [x] Getting the Telegram invite link to a Matrix room (`invitelink`) * [x] Getting the Telegram invite link to a Matrix room (`invitelink`)
+18 -1
View File
@@ -370,7 +370,24 @@ class CommandHandler:
@command_handler @command_handler
def upgrade(self, sender, args): 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 @command_handler
def setpublic(self, sender, args): def setpublic(self, sender, args):
+46 -9
View File
@@ -159,7 +159,7 @@ class Portal:
levels["events"]["m.room.name"] = power_level_requirement levels["events"]["m.room.name"] = power_level_requirement
levels["events"]["m.room.avatar"] = 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.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.main_intent.set_power_levels(self.mxid, levels)
self.update_after_create(user, entity, direct, puppet) self.update_after_create(user, entity, direct, puppet)
@@ -313,7 +313,7 @@ class Portal:
self.delete() self.delete()
del self.by_tgid[self.tgid_full] del self.by_tgid[self.tgid_full]
del self.by_mxid[self.mxid] del self.by_mxid[self.mxid]
elif source: elif source and source.tgid != user.tgid:
target = user.get_input_entity(source) target = user.get_input_entity(source)
if self.peer_type == "chat": if self.peer_type == "chat":
source.client(DeleteChatUserRequest(chat_id=self.tgid, user_id=target)) source.client(DeleteChatUserRequest(chat_id=self.tgid, user_id=target))
@@ -375,6 +375,7 @@ class Portal:
deleter.client.delete_messages(self.peer, [message.tgid]) deleter.client.delete_messages(self.peer, [message.tgid])
def handle_matrix_power_levels(self, sender, new_users, old_users): 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(): for user, level in new_users.items():
user_id = p.Puppet.get_id_from_mxid(user) user_id = p.Puppet.get_id_from_mxid(user)
if not user_id: if not user_id:
@@ -383,8 +384,21 @@ class Portal:
continue continue
user_id = mx_user.tgid user_id = mx_user.tgid
if user not in old_users or level != old_users[user]: if user not in old_users or level != old_users[user]:
sender.client( if self.peer_type == "chat":
EditChatAdminRequest(chat_id=self.tgid, user_id=user_id, is_admin=level >= 50)) 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): def handle_matrix_about(self, sender, about):
if self.peer_type not in {"channel"}: if self.peer_type not in {"channel"}:
@@ -449,6 +463,22 @@ class Portal:
user_tgids.add(puppet_id) user_tgids.add(puppet_id)
return user_tgids 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): def create_telegram_chat(self, source, supergroup=False):
if not self.mxid: if not self.mxid:
raise ValueError("Can't create Telegram chat for portal without Matrix room.") raise ValueError("Can't create Telegram chat for portal without Matrix room.")
@@ -476,6 +506,7 @@ class Portal:
self.tgid = entity.id self.tgid = entity.id
self.tg_receiver = self.tgid self.tg_receiver = self.tgid
self.by_tgid[self.tgid_full] = self
self.update_info(source, entity) self.update_info(source, entity)
self.save() self.save()
@@ -663,17 +694,19 @@ class Portal:
levels = self.main_intent.get_power_levels(self.mxid) levels = self.main_intent.get_power_levels(self.mxid)
changed = False 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 changed = True
levels["events"]["m.room.power_levels"] = 50 levels["events"]["m.room.power_levels"] = admin_power_level
for participant in participants: for participant in participants:
puppet = p.Puppet.get(participant.user_id) puppet = p.Puppet.get(participant.user_id)
user = u.User.get_by_tgid(participant.user_id) user = u.User.get_by_tgid(participant.user_id)
print(participant)
new_level = 0 new_level = 0
if isinstance(participant, ChatParticipantAdmin): if isinstance(participant, (ChatParticipantAdmin, ChannelParticipantAdmin)):
new_level = 50 new_level = 50
elif isinstance(participant, ChatParticipantCreator): elif isinstance(participant, (ChatParticipantCreator, ChannelParticipantCreator)):
new_level = 95 new_level = 95
if user and (user.mxid in levels["users"] or new_level > 0): if user and (user.mxid in levels["users"] or new_level > 0):
levels["users"][user.mxid] = new_level levels["users"][user.mxid] = new_level
@@ -705,8 +738,12 @@ class Portal:
existing = DBPortal.query.get(self.tgid_full) existing = DBPortal.query.get(self.tgid_full)
if existing: if existing:
self.db.object_session(existing).delete(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.tgid = new_id
self.tg_receiver = new_id
self.by_tgid[self.tgid_full] = self self.by_tgid[self.tgid_full] = self
self.save() self.save()