Pārlūkot izejas kodu

Fix things in group change handling

Tulir Asokan 2 gadi atpakaļ
vecāks
revīzija
9e134782c3
4 mainītis faili ar 59 papildinājumiem un 37 dzēšanām
  1. 4 0
      mausignald/types.py
  2. 34 28
      mautrix_signal/portal.py
  3. 8 2
      mautrix_signal/signal.py
  4. 13 7
      mautrix_signal/user.py

+ 4 - 0
mausignald/types.py

@@ -208,6 +208,10 @@ class GroupMember(SerializableAttrs):
     joined_revision: int = 0
     role: GroupMemberRole = GroupMemberRole.UNKNOWN
 
+    @property
+    def address(self) -> Address:
+        return Address(uuid=self.uuid)
+
 
 @dataclass
 class BannedGroupMember(SerializableAttrs):

+ 34 - 28
mautrix_signal/portal.py

@@ -1245,21 +1245,25 @@ class Portal(DBPortal, BasePortal):
                         await editor_intent.leave_room(self.mxid)
                     else:
                         await self._kick_with_puppet(user, editor)
+
         if group_change.modify_member_roles:
             levels = await editor.intent_for(self).get_power_levels(self.mxid)
             for group_member in group_change.modify_member_roles:
                 users = [
-                    await p.Puppet.get_by_address(Address(uuid=group_member.uuid)),
+                    await p.Puppet.get_by_address(group_member.address),
                     await u.User.get_by_uuid(group_member.uuid),
                 ]
                 for user in users:
                     if not user:
                         continue
-                    if group_member.role == GroupMemberRole.ADMINISTRATOR:
-                        new_pl = 50
-                    else:
-                        new_pl = 0
-                    levels.users[user.mxid] = new_pl
+                    if (
+                        group_member.role == GroupMemberRole.ADMINISTRATOR
+                        and levels.users.get(user.mxid, 0) < 50
+                    ):
+                        levels.users[user.mxid] = 50
+                        levels.users = {k: v for k, v in sorted(list(levels.users.items()))}
+                    elif levels.users.get(user.mxid, 0) >= 50:
+                        levels.users.pop(user.mxid, 0)
             await self._try_with_puppet(
                 lambda i: i.set_power_levels(self.mxid, levels), puppet=editor
             )
@@ -1267,7 +1271,7 @@ class Portal(DBPortal, BasePortal):
         if group_change.new_banned_members:
             for banned_member in group_change.new_banned_members:
                 users = [
-                    await p.Puppet.get_by_address(Address(uuid=banned_member.uuid)),
+                    await p.Puppet.get_by_address(banned_member.address),
                     await u.User.get_by_uuid(banned_member.uuid),
                 ]
                 for user in users:
@@ -1284,10 +1288,11 @@ class Portal(DBPortal, BasePortal):
                             self.log.debug(f"Could not ban {user.mxid}: {e}")
                     except MBadState as e:
                         self.log.debug(f"Could not ban {user.mxid}: {e}")
+
         if group_change.new_unbanned_members:
             for banned_member in group_change.new_unbanned_members:
                 users = [
-                    await p.Puppet.get_by_address(Address(uuid=banned_member.uuid)),
+                    await p.Puppet.get_by_address(banned_member.address),
                     await u.User.get_by_uuid(banned_member.uuid),
                 ]
                 for user in users:
@@ -1304,6 +1309,7 @@ class Portal(DBPortal, BasePortal):
                             self.log.debug(f"Could not unban {user.mxid}: {e}")
                     except MBadState as e:
                         self.log.debug(f"Could not unban {user.mxid}: {e}")
+
         if (
             group_change.new_members
             or group_change.new_pending_members
@@ -1315,13 +1321,8 @@ class Portal(DBPortal, BasePortal):
                 + (group_change.new_pending_members or [])
                 + (group_change.promote_requesting_members or [])
             ):
-                puppet = await p.Puppet.get_by_address(Address(uuid=group_member.uuid))
-                try:
-                    await source.sync_contact(Address(uuid=group_member.uuid))
-                except ProfileUnavailableError:
-                    self.log.debug(
-                        f"Profile of puppet with uuid {group_member.uuid} is unavailable"
-                    )
+                puppet = await p.Puppet.get_by_address(group_member.address)
+                await source.sync_contact(group_member.address)
                 users = [puppet, await u.User.get_by_uuid(group_member.uuid)]
                 for user in users:
                     if not user:
@@ -1351,34 +1352,32 @@ class Portal(DBPortal, BasePortal):
                             await user.intent_for(self).ensure_joined(self.mxid)
                         except IntentError as e:
                             self.log.debug(f"{user.name} could not join group: {e}")
+
         if group_change.promote_pending_members:
-            for member in group_change.promote_pending_members:
-                try:
-                    await source.sync_contact(Address(uuid=group_member.uuid))
-                except ProfileUnavailableError:
-                    self.log.debug(
-                        f"Profile of puppet with uuid {group_member.uuid} is unavailable"
-                    )
-                user = await p.Puppet.get_by_address(address)
+            for group_member in group_change.promote_pending_members:
+                await source.sync_contact(group_member.address)
+                user = await p.Puppet.get_by_address(group_member.address)
                 if not user:
                     continue
                 try:
                     await user.intent_for(self).ensure_joined(self.mxid)
                 except IntentError as e:
                     self.log.debug(f"{user.name} could not join group: {e}")
+
         if group_change.new_requesting_members:
-            for member in group_change.new_requesting_members:
+            for group_member in group_change.new_requesting_members:
                 try:
-                    await source.sync_contact(Address(uuid=group_member.uuid))
+                    await source.sync_contact(group_member.address)
                 except ProfileUnavailableError:
                     self.log.debug(
                         f"Profile of puppet with uuid {group_member.uuid} is unavailable"
                     )
-                user = await p.Puppet.get_by_address(Address(uuid=member.uuid))
+                user = await p.Puppet.get_by_address(group_member.address)
                 try:
-                    await user.intent_for(self).knock(self.mxid, reason="via invite link")
+                    await user.intent_for(self).knock_room(self.mxid, reason="via invite link")
                 except (MForbidden, MBadState) as e:
                     self.log.debug(f"{user.name} failed knock: {e}")
+
         if group_change.new_access_control:
             ac = group_change.new_access_control
             if ac.attributes or ac.members:
@@ -1402,6 +1401,7 @@ class Portal(DBPortal, BasePortal):
                 await self._try_with_puppet(
                     lambda i: i.set_join_rule(self.mxid, join_rule), puppet=editor
                 )
+
         if group_change.new_is_announcement_group:
             levels = await editor.intent_for(self).get_power_levels(self.mxid)
             if group_change.new_is_announcement_group == AnnouncementsMode.ENABLED:
@@ -1411,6 +1411,7 @@ class Portal(DBPortal, BasePortal):
             await self._try_with_puppet(
                 lambda i: i.set_power_levels(self.mxid, levels), puppet=editor
             )
+
         changed = False
         if group_change.new_description:
             changed = await self._update_topic(group_change.new_description, editor)
@@ -1426,6 +1427,7 @@ class Portal(DBPortal, BasePortal):
                 )
                 or changed
             )
+
         if changed:
             await self.update_bridge_info()
             await self.update()
@@ -2123,7 +2125,10 @@ class Portal(DBPortal, BasePortal):
                                 level = 50
                         else:
                             level = 50 if detail.role == GroupMemberRole.ADMINISTRATOR else 0
-                        levels.users[puppet_mxid] = level
+                        if level == 0:
+                            levels.users.pop(puppet_mxid, None)
+                        else:
+                            levels.users[puppet_mxid] = level
                 announcements = info.announcements
             else:
                 ac = GroupAccessControl()
@@ -2146,6 +2151,7 @@ class Portal(DBPortal, BasePortal):
         levels.redact = 99
         if self.main_intent.mxid not in levels.users:
             levels.users[self.main_intent.mxid] = 9001 if is_initial else 100
+        levels.users = {k: v for k, v in sorted(list(levels.users.items()))}
         return levels
 
     async def _create_matrix_room(self, source: u.User, info: ChatInfo) -> RoomID | None:

+ 8 - 2
mautrix_signal/signal.py

@@ -222,10 +222,16 @@ class SignalHandler(SignaldClient):
             and msg.group_v2.group_change
             and msg.group_v2.revision == portal.revision + 1
         ):
-            self.log.debug(f"Got GroupChange for {msg.group_v2.id}, updating info")
+            self.log.debug(
+                f"Got update for {msg.group_v2.id} ({portal.revision} -> "
+                f"{msg.group_v2.revision}), applying diff"
+            )
             await portal.handle_signal_group_change(msg.group_v2.group_change, user)
         elif msg.group_v2 and msg.group_v2.revision > portal.revision:
-            self.log.debug(f"Got new revision of {msg.group_v2.id}, updating info")
+            self.log.debug(
+                f"Got update with multiple revisions for {msg.group_v2.id} ({portal.revision} -> "
+                f"{msg.group_v2.revision}), resyncing info"
+            )
             await portal.update_info(user, msg.group_v2)
         if msg.expires_in_seconds is not None and (msg.is_message or msg.is_expiration_update):
             await portal.update_expires_in_seconds(sender, msg.expires_in_seconds)

+ 13 - 7
mautrix_signal/user.py

@@ -21,7 +21,7 @@ from datetime import datetime
 from uuid import UUID
 import asyncio
 
-from mausignald.errors import AuthorizationFailedError
+from mausignald.errors import AuthorizationFailedError, ProfileUnavailableError
 from mausignald.types import (
     Account,
     Address,
@@ -287,26 +287,32 @@ class User(DBUser, BaseUser):
             self.log.exception("Error while syncing groups")
             await self.handle_auth_failure(e)
 
-    async def sync_contact(self, contact: Profile | Address, create_portals: bool = False) -> None:
+    async def sync_contact(
+        self, contact: Profile | Address, create_portals: bool = False, use_cache: bool = True
+    ) -> None:
         self.log.trace("Syncing contact %s", contact)
         try:
             if isinstance(contact, Address):
                 address = contact
-                profile = await self.bridge.signal.get_profile(
-                    self.username, address, use_cache=True
-                )
+                try:
+                    profile = await self.bridge.signal.get_profile(
+                        self.username, address, use_cache=use_cache
+                    )
+                except ProfileUnavailableError:
+                    self.log.debug(f"Profile of {address} was not available when syncing")
+                    profile = None
                 if profile and profile.name:
                     self.log.trace("Got profile for %s: %s", address, profile)
             else:
                 address = contact.address
                 profile = contact
             puppet = await pu.Puppet.get_by_address(address)
-            await puppet.update_info(profile, self)
+            await puppet.update_info(profile or address, self)
             if create_portals:
                 portal = await po.Portal.get_by_chat_id(
                     puppet.address, receiver=self.username, create=True
                 )
-                await portal.create_matrix_room(self, profile)
+                await portal.create_matrix_room(self, profile or address)
         except Exception as e:
             await self.handle_auth_failure(e)
             raise