diff --git a/synapse/rest/admin/__init__.py b/synapse/rest/admin/__init__.py index f3c99663e84..1d527a96516 100644 --- a/synapse/rest/admin/__init__.py +++ b/synapse/rest/admin/__init__.py @@ -81,6 +81,7 @@ RoomEventContextServlet, RoomMembersRestServlet, RoomMessagesRestServlet, + RoomParticipantsRestServlet, RoomRestServlet, RoomRestV2Servlet, RoomStateRestServlet, @@ -320,6 +321,7 @@ def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None: ListDestinationsRestServlet(hs).register(http_server) RoomMessagesRestServlet(hs).register(http_server) RoomTimestampToEventRestServlet(hs).register(http_server) + RoomParticipantsRestServlet(hs).register(http_server) UserReplaceMasterCrossSigningKeyRestServlet(hs).register(http_server) UserByExternalId(hs).register(http_server) UserByThreePid(hs).register(http_server) diff --git a/synapse/rest/admin/rooms.py b/synapse/rest/admin/rooms.py index 01f9de9ffa5..601c1f69542 100644 --- a/synapse/rest/admin/rooms.py +++ b/synapse/rest/admin/rooms.py @@ -989,3 +989,28 @@ async def on_GET( "event_id": event_id, "origin_server_ts": origin_server_ts, } + + +class RoomParticipantsRestServlet(RestServlet): + """ + This endpoint allows admins to fetch currently joined members of a room who + have also posted to the room. + + GET /_synapse/admin/v1/rooms/{room_id}/participants + """ + + PATTERNS = admin_patterns("/rooms/(?P[^/]*)/participants$") + + def __init__(self, hs: "HomeServer"): + self._auth = hs.get_auth() + self._store = hs.get_datastores().main + + async def on_GET( + self, request: SynapseRequest, room_id: str + ) -> Tuple[int, JsonDict]: + requester = await self._auth.get_user_by_req(request) + await assert_user_is_admin(self._auth, requester) + + participants = await self._store.get_participants_in_room(room_id) + + return HTTPStatus.OK, {"participants": list(participants)} diff --git a/synapse/storage/databases/main/roommember.py b/synapse/storage/databases/main/roommember.py index 50ed6a28bf0..007521bcb46 100644 --- a/synapse/storage/databases/main/roommember.py +++ b/synapse/storage/databases/main/roommember.py @@ -1606,6 +1606,34 @@ def _get_rooms_for_user_by_join_date_txn( from_ts, ) + async def get_participants_in_room(self, room_id: str) -> FrozenSet[str]: + """ + Return a list of all currently joined room members who have posted + in the given room. + + Args: + room_id: room ID of the room to search in + """ + + def _get_participants_in_room_txn( + txn: LoggingTransaction, room_id: str + ) -> frozenset: + sql = """ + SELECT DISTINCT c.state_key + FROM current_state_events AS c + INNER JOIN events AS e USING(room_id) + WHERE room_id = ? + AND c.membership = 'join' + AND e.type = 'm.room.message' + AND c.state_key = e.sender + """ + txn.execute(sql, (room_id,)) + return frozenset([r[0] for r in txn]) + + return await self.db_pool.runInteraction( + "_get_participants_in_room_txn", _get_participants_in_room_txn, room_id + ) + class RoomMemberBackgroundUpdateStore(SQLBaseStore): def __init__(