From 613a7ea84757bddac86eb6cb0a7a747a99f098b2 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 7 Jul 2025 10:38:19 +0100 Subject: [PATCH] Add support for appservices. --- synapse/handlers/appservice.py | 32 +++++++++++++++++++++++++++++++- synapse/handlers/room.py | 17 +++++++++++------ 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/synapse/handlers/appservice.py b/synapse/handlers/appservice.py index f3bbdb5a05..a04a061305 100644 --- a/synapse/handlers/appservice.py +++ b/synapse/handlers/appservice.py @@ -36,7 +36,7 @@ from prometheus_client import Counter from twisted.internet import defer import synapse -from synapse.api.constants import EduTypes, EventTypes +from synapse.api.constants import EduTypes, EventTypes, Membership from synapse.appservice import ApplicationService from synapse.events import EventBase from synapse.handlers.presence import format_user_presence_state @@ -139,6 +139,24 @@ class ApplicationServicesHandler: events_by_room: Dict[str, List[EventBase]] = {} for event in events: + # We never want to send leave events from deleted rooms to the AS since the events + # may have been purged by the time it's pushed to the AS. Instead, we handle this + # elsewhere. + membership = event.content.get("membership") + state_key = event.get_state_key() + if ( + state_key is not None + and event.type == EventTypes.Member + and membership == Membership.LEAVE + ): + if await self.store.has_room_been_deleted(event.room_id): + logger.debug( + "Filtering %s from appservice as it's a leave event (%s) from deleted room %s", + event.event_id, + state_key, + event.room_id, + ) + continue events_by_room.setdefault(event.room_id, []).append(event) async def handle_event(event: EventBase) -> None: @@ -222,6 +240,18 @@ class ApplicationServicesHandler: finally: self.is_processing = False + async def notify_room_deletion( + self, room_id: str, deleted_stream_id: int, users: Iterable[str] + ) -> None: + services = self.store.get_app_services() + for service in services: + if any( + service.is_interested_in_user(user_id) for user_id in users + ) or await service.is_interested_in_room(room_id, self.store): + await self.store.create_appservice_stream_id_txn( + service, deleted_stream_id + ) + def notify_interested_services_ephemeral( self, stream_key: StreamKeyType, diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py index 92df6f4733..c7a5d1a301 100644 --- a/synapse/handlers/room.py +++ b/synapse/handlers/room.py @@ -1801,6 +1801,7 @@ class RoomShutdownHandler: self._replication = hs.get_replication_data_handler() self._third_party_rules = hs.get_module_api_callbacks().third_party_event_rules self.event_creation_handler = hs.get_event_creation_handler() + self.as_handler = hs.get_application_service_handler() self.store = hs.get_datastores().main async def shutdown_room( @@ -1920,19 +1921,23 @@ class RoomShutdownHandler: else: logger.info("Shutting down room %r", room_id) - users = await self.store.get_local_users_related_to_room(room_id) + # If the user is not in the room (or is banned), nothing to do. + users = [ + user + for user in await self.store.get_local_users_related_to_room(room_id) + if user[1] in (Membership.JOIN, Membership.INVITE, Membership.KNOCK) + ] # When deleting a room, we want to store the local membership state so that we # can still send synthetic leaves down sync after the room has been purged (if indeed it has). # We must do this prior to kicking as otherwise the current_state_events # table will be empty. - await self.store.store_deleted_room_members(room_id) + delete_stream_id = await self.store.store_deleted_room_members(room_id) + await self.as_handler.notify_room_deletion( + room_id, delete_stream_id, [user_id for user_id, _membership in users] + ) for user_id, membership in users: - # If the user is not in the room (or is banned), nothing to do. - if membership not in (Membership.JOIN, Membership.INVITE, Membership.KNOCK): - continue - logger.info("Kicking %r from %r...", user_id, room_id) try: