def _find_desired_pl( self, room_id: RoomID, member: MemberStateEventContent, ) -> Optional[int]: role_map: Optional[Dict[str, int]] = self.config["rooms"].get(room_id) if not role_map: return None user: Optional[DiscordMember] = member.get( "uk.half-shot.discord.member") # Ensure we have information about the Discord user's roles. # We won't have this for the event generated by a profile update, # or if membership is not join. if user is None: return None user_roles: List[DiscordRole] = user["roles"] for role in sorted(user_roles, key=lambda role: -role["position"]): role_pl = role_map.get(role["name"]) if role_pl is not None: self.log.debug("Found role %s in %s", role["name"], room_id) return role_pl return None
async def int_handle_event(self, evt: Event) -> None: if isinstance(evt, StateEvent) and evt.type == EventType.ROOM_MEMBER and self.e2ee: await self.e2ee.handle_member_event(evt) if self.filter_matrix_event(evt): return self.log.trace("Received event: %s", evt) start_time = time.time() if evt.type == EventType.ROOM_MEMBER: evt: StateEvent prev_content = evt.unsigned.prev_content or MemberStateEventContent() prev_membership = prev_content.membership if prev_content else Membership.JOIN if evt.content.membership == Membership.INVITE: await self.int_handle_invite(evt.room_id, UserID(evt.state_key), evt.sender, evt.event_id) elif evt.content.membership == Membership.LEAVE: if prev_membership == Membership.BAN: await self.handle_unban(evt.room_id, UserID(evt.state_key), evt.sender, evt.content.reason, evt.event_id) elif prev_membership == Membership.INVITE: if evt.sender == evt.state_key: await self.handle_reject(evt.room_id, UserID(evt.state_key), evt.content.reason, evt.event_id) else: await self.handle_disinvite(evt.room_id, UserID(evt.state_key), evt.sender, evt.content.reason, evt.event_id) elif evt.sender == evt.state_key: await self.handle_leave(evt.room_id, UserID(evt.state_key), evt.event_id) else: await self.handle_kick(evt.room_id, UserID(evt.state_key), evt.sender, evt.content.reason, evt.event_id) elif evt.content.membership == Membership.BAN: await self.handle_ban(evt.room_id, UserID(evt.state_key), evt.sender, evt.content.reason, evt.event_id) elif evt.content.membership == Membership.JOIN: if prev_membership != Membership.JOIN: await self.handle_join(evt.room_id, UserID(evt.state_key), evt.event_id) else: await self.handle_member_info_change(evt.room_id, UserID(evt.state_key), evt.content, prev_content, evt.event_id) elif evt.type in (EventType.ROOM_MESSAGE, EventType.STICKER): evt: MessageEvent if evt.type != EventType.ROOM_MESSAGE: evt.content.msgtype = MessageType(str(evt.type)) await self.handle_message(evt.room_id, evt.sender, evt.content, evt.event_id) elif evt.type == EventType.ROOM_ENCRYPTED and self.e2ee: await self.handle_encrypted(evt) elif evt.type == EventType.ROOM_ENCRYPTION: await self.handle_encryption(evt) else: if evt.type.is_state and isinstance(evt, StateEvent): await self.handle_state_event(evt) else: await self.handle_event(evt) await self.log_event_handle_duration(evt, time.time() - start_time)
async def handle_event(self, event: Event) -> None: self.log.debug("Handle event") domain = self.config['homeserver.domain'] namespace = self.config['appservice.namespace'] event_type: str = event.get("type", "m.unknown") room_id: Optional[RoomID] = event.get("room_id", None) event_id: Optional[EventID] = event.get("event_id", None) sender: Optional[UserID] = event.get("sender", None) content: Dict = event.get("content", {}) self.log.debug(f"Event {event}") self.log.debug(f"Event type: {event.type}") self.log.debug(f"Event room_id: {room_id}") self.log.debug(f"Event sender: {sender}") self.log.debug(f"Event content: {content}") if event.type == EventType.ROOM_MEMBER: event: StateEvent prev_content = event.unsigned.prev_content or MemberStateEventContent( ) prev_membership = prev_content.membership if prev_content else Membership.JOIN if event.content.membership == Membership.LEAVE: if event.sender == event.state_key: await self.sl.matrix_user_left(UserID(event.state_key), event.room_id, event.event_id) elif event.content.membership == Membership.JOIN: if prev_membership != Membership.JOIN: await self.sl.matrix_user_joined(UserID(event.state_key), event.room_id, event.event_id) elif event.type in (EventType.ROOM_MESSAGE, EventType.STICKER): event: MessageEvent if event.type != EventType.ROOM_MESSAGE: event.content.msgtype = MessageType(str(event.type)) await self.handle_message(event.room_id, event.sender, event.content, event.event_id)
async def fill_member_event( self, room_id: RoomID, user_id: UserID, content: MemberStateEventContent, ) -> MemberStateEventContent | None: """ Fill a membership event content that is going to be sent in :meth:`send_member_event`. This is used to set default fields like the displayname and avatar, which are usually set by the server in the sugar membership endpoints like /join and /invite, but are not set automatically when sending member events manually. This implementation in StoreUpdatingAPI will first try to call the default implementation (which calls :attr:`fill_member_event_callback`). If that doesn't return anything, this will try to get the profile from the current member event, and then fall back to fetching the global profile from the server. Args: room_id: The room where the member event is going to be sent. user_id: The user whose membership is changing. content: The new member event content. Returns: The filled member event content. """ callback_content = await super().fill_member_event( room_id, user_id, content) if callback_content is not None: self.log.trace("Filled new member event for %s using callback", user_id) return callback_content if content.displayname is None and content.avatar_url is None: existing_member = await self.state_store.get_member( room_id, user_id) if existing_member is not None: self.log.trace( "Found existing member event %s to fill new member event for %s", existing_member, user_id, ) content.displayname = existing_member.displayname content.avatar_url = existing_member.avatar_url return content try: profile = await self.get_profile(user_id) except (MNotFound, MForbidden): profile = None if profile: self.log.trace( "Fetched profile %s to fill new member event of %s", profile, user_id) content.displayname = profile.displayname content.avatar_url = profile.avatar_url return content else: self.log.trace( "Didn't find profile info to fill new member event of %s", user_id) else: self.log.trace( "Member event for %s already contains displayname or avatar, not re-filling", user_id, ) return None
async def int_handle_event(self, evt: Event, send_bridge_checkpoint: bool = True) -> None: if isinstance(evt, StateEvent) and evt.type == EventType.ROOM_MEMBER and self.e2ee: await self.e2ee.handle_member_event(evt) if not await self.allow_matrix_event(evt): return self.log.trace("Received event: %s", evt) if send_bridge_checkpoint: self.send_bridge_checkpoint(evt) start_time = time.time() if evt.type == EventType.ROOM_MEMBER: evt: StateEvent unsigned = evt.unsigned or StateUnsigned() prev_content = unsigned.prev_content or MemberStateEventContent() prev_membership = prev_content.membership if prev_content else Membership.JOIN if evt.content.membership == Membership.INVITE: if prev_membership == Membership.KNOCK: await self.handle_accept_knock( evt.room_id, UserID(evt.state_key), evt.sender, evt.content.reason, evt.event_id, ) else: await self.int_handle_invite(evt) elif evt.content.membership == Membership.LEAVE: if prev_membership == Membership.BAN: await self.handle_unban( evt.room_id, UserID(evt.state_key), evt.sender, evt.content.reason, evt.event_id, ) elif prev_membership == Membership.INVITE: if evt.sender == evt.state_key: await self.handle_reject( evt.room_id, UserID(evt.state_key), evt.content.reason, evt.event_id ) else: await self.handle_disinvite( evt.room_id, UserID(evt.state_key), evt.sender, evt.content.reason, evt.event_id, ) elif prev_membership == Membership.KNOCK: if evt.sender == evt.state_key: await self.handle_retract_knock( evt.room_id, UserID(evt.state_key), evt.content.reason, evt.event_id ) else: await self.handle_reject_knock( evt.room_id, UserID(evt.state_key), evt.sender, evt.content.reason, evt.event_id, ) elif evt.sender == evt.state_key: await self.handle_leave(evt.room_id, UserID(evt.state_key), evt.event_id) else: await self.handle_kick( evt.room_id, UserID(evt.state_key), evt.sender, evt.content.reason, evt.event_id, ) elif evt.content.membership == Membership.BAN: await self.handle_ban( evt.room_id, UserID(evt.state_key), evt.sender, evt.content.reason, evt.event_id, ) elif evt.content.membership == Membership.JOIN: if prev_membership != Membership.JOIN: await self.handle_join(evt.room_id, UserID(evt.state_key), evt.event_id) else: await self.handle_member_info_change( evt.room_id, UserID(evt.state_key), evt.content, prev_content, evt.event_id ) elif evt.content.membership == Membership.KNOCK: await self.handle_knock( evt.room_id, UserID(evt.state_key), evt.content.reason, evt.event_id, ) elif evt.type in (EventType.ROOM_MESSAGE, EventType.STICKER): evt: MessageEvent if evt.type != EventType.ROOM_MESSAGE: evt.content.msgtype = MessageType(str(evt.type)) await self.handle_message(evt.room_id, evt.sender, evt.content, evt.event_id) elif evt.type == EventType.ROOM_ENCRYPTED: await self.handle_encrypted(evt) elif evt.type == EventType.ROOM_ENCRYPTION: await self.handle_encryption(evt) else: if evt.type.is_state and isinstance(evt, StateEvent): await self.handle_state_event(evt) elif evt.type.is_ephemeral and isinstance( evt, (PresenceEvent, TypingEvent, ReceiptEvent) ): await self.handle_ephemeral_event(evt) else: await self.handle_event(evt) await self.log_event_handle_duration(evt, time.time() - start_time)