Browse Source

Merge remote-tracking branch 'maltee1/power_level'

Tulir Asokan 3 years ago
parent
commit
99642da957
3 changed files with 121 additions and 28 deletions
  1. 2 0
      mausignald/signald.py
  2. 8 1
      mautrix_signal/matrix.py
  3. 111 27
      mautrix_signal/portal.py

+ 2 - 0
mausignald/signald.py

@@ -355,6 +355,7 @@ class SignaldClient(SignaldRPCClient):
         add_members: list[Address] | None = None,
         remove_members: list[Address] | None = None,
         update_access_control: GroupAccessControl | None = None,
+        update_role: GroupMember | None = None,
     ) -> Group | GroupV2 | None:
         update_params = {
             key: value
@@ -370,6 +371,7 @@ class SignaldClient(SignaldRPCClient):
                 "updateAccessControl": (
                     update_access_control.serialize() if update_access_control else None
                 ),
+                "updateRole": (update_role.serialize() if update_role else None),
             }.items()
             if value is not None
         }

+ 8 - 1
mautrix_signal/matrix.py

@@ -188,7 +188,12 @@ class MatrixHandler(BaseMatrixHandler):
             await super().handle_ephemeral_event(evt)
 
     async def handle_state_event(self, evt: StateEvent) -> None:
-        if evt.type not in (EventType.ROOM_NAME, EventType.ROOM_TOPIC, EventType.ROOM_AVATAR):
+        if evt.type not in (
+            EventType.ROOM_NAME,
+            EventType.ROOM_TOPIC,
+            EventType.ROOM_AVATAR,
+            EventType.ROOM_POWER_LEVELS,
+        ):
             return
 
         user = await u.User.get_by_mxid(evt.sender)
@@ -204,6 +209,8 @@ class MatrixHandler(BaseMatrixHandler):
             await portal.handle_matrix_avatar(user, evt.content.url)
         elif evt.type == EventType.ROOM_TOPIC:
             await portal.handle_matrix_topic(user, evt.content.topic)
+        elif evt.type == EventType.ROOM_POWER_LEVELS:
+            await portal.handle_matrix_power_level(user, evt.content, evt.unsigned.prev_content)
 
     async def allow_message(self, user: u.User) -> bool:
         return user.relay_whitelisted

+ 111 - 27
mautrix_signal/portal.py

@@ -36,6 +36,7 @@ from mausignald.types import (
     Group,
     GroupAccessControl,
     GroupID,
+    GroupMember,
     GroupMemberRole,
     GroupV2,
     GroupV2ID,
@@ -774,6 +775,20 @@ class Portal(DBPortal, BasePortal):
             )
         except RPCError as e:
             raise RejectMatrixInvite(str(e)) from e
+        power_levels = await self.main_intent.get_power_levels(self.mxid)
+        invitee_pl = power_levels.get_user_level(user.mxid)
+        if invitee_pl >= 50:
+            group_member = GroupMember(uuid=user.uuid, role=GroupMemberRole.ADMINISTRATOR)
+            try:
+                update_meta = await self.signal.update_group(
+                    invited_by.username, self.chat_id, update_role=group_member
+                )
+                self.revision = update_meta.revision
+            except Exception as e:
+                self.log.exception(f"Failed to update Signal member role: {e}")
+                await self._update_power_levels(
+                    await self.signal.get_group(invited_by.username, self.chat_id)
+                )
 
     async def handle_matrix_name(self, user: u.User, name: str) -> None:
         if self.name == name or self.is_direct or not name:
@@ -839,6 +854,101 @@ class Portal(DBPortal, BasePortal):
             except FileNotFoundError:
                 pass
 
+    async def handle_matrix_power_level(
+        self,
+        sender: u.User,
+        levels: PowerLevelStateEventContent,
+        prev_content: PowerLevelStateEventContent | None = None,
+    ) -> None:
+        old_users = prev_content.users if prev_content else None
+        new_users = levels.users
+        changes = {}
+        sender, is_relay = await self.get_relay_sender(sender, "power level change")
+        if not sender:
+            return
+
+        if not old_users:
+            changes = new_users
+        else:
+            for user, level in new_users.items():
+                if (
+                    user
+                    and user != self.main_intent.mxid
+                    and (user not in old_users or level != old_users[user])
+                ):
+                    changes[user] = level
+            for user, level in old_users.items():
+                if user and user != self.main_intent.mxid and user not in new_users:
+                    changes[user] = levels.users_default
+        if changes:
+            for user, level in changes.items():
+                address = p.Puppet.get_id_from_mxid(user)
+                if not address:
+                    mx_user = await u.User.get_by_mxid(user, create=False)
+                    if not mx_user or not mx_user.is_logged_in:
+                        continue
+                    address = mx_user.address
+                if not address or not address.uuid:
+                    continue
+                signal_role = (
+                    GroupMemberRole.DEFAULT if level < 50 else GroupMemberRole.ADMINISTRATOR
+                )
+                group_member = GroupMember(uuid=address.uuid, role=signal_role)
+                try:
+                    update_meta = await self.signal.update_group(
+                        sender.username, self.chat_id, update_role=group_member
+                    )
+                    self.revision = update_meta.revision
+                except Exception as e:
+                    self.log.exception(f"Failed to update Signal member role: {e}")
+                    await self._update_power_levels(
+                        await self.signal.get_group(sender.username, self.chat_id)
+                    )
+                    return
+        if not prev_content or levels.invite != prev_content.invite:
+            try:
+                update_meta = await self.signal.update_group(
+                    username=sender.username,
+                    group_id=self.chat_id,
+                    update_access_control=GroupAccessControl(
+                        members=(
+                            AccessControlMode.MEMBER
+                            if levels.invite == 0
+                            else AccessControlMode.ADMINISTRATOR
+                        ),
+                        attributes=None,
+                        link=None,
+                    ),
+                )
+                self.revision = update_meta.revision
+            except Exception as e:
+                self.log.exception(f"Failed to update Signal member add permission: {e}")
+                await self._update_power_levels(
+                    await self.signal.get_group(sender.username, self.chat_id)
+                )
+                return
+        if not prev_content or levels.state_default != prev_content.state_default:
+            try:
+                update_meta = await self.signal.update_group(
+                    username=sender.username,
+                    group_id=self.chat_id,
+                    update_access_control=GroupAccessControl(
+                        attributes=(
+                            AccessControlMode.MEMBER
+                            if levels.state_default == 0
+                            else AccessControlMode.ADMINISTRATOR
+                        ),
+                        members=None,
+                        link=None,
+                    ),
+                )
+                self.revision = update_meta.revision
+            except Exception as e:
+                self.log.exception(f"Failed to update Signal metadata change permission: {e}")
+                await self._update_power_levels(
+                    await self.signal.get_group(sender.username, self.chat_id)
+                )
+
     # endregion
     # region Signal event handling
 
@@ -1335,33 +1445,7 @@ class Portal(DBPortal, BasePortal):
                 pass
         if self.topic:
             await self.signal.update_group(source.username, self.chat_id, description=self.topic)
-        await self.signal.update_group(
-            username=source.username,
-            group_id=self.chat_id,
-            update_access_control=GroupAccessControl(
-                members=(
-                    AccessControlMode.MEMBER
-                    if levels.invite == 0
-                    else AccessControlMode.ADMINISTRATOR
-                ),
-                attributes=None,
-                link=None,
-            ),
-        )
-        update_meta = await self.signal.update_group(
-            username=source.username,
-            group_id=self.chat_id,
-            update_access_control=GroupAccessControl(
-                attributes=(
-                    AccessControlMode.MEMBER
-                    if levels.state_default == 0
-                    else AccessControlMode.ADMINISTRATOR
-                ),
-                members=None,
-                link=None,
-            ),
-        )
-        self.revision = update_meta.revision
+        await self.handle_matrix_power_level(source, levels)
         await self.update()
         await self.update_bridge_info()