Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit ad4eab9

Browse files
authored
Add a module API method to retrieve state from a room (#11204)
1 parent 3ed17ff commit ad4eab9

File tree

3 files changed

+74
-1
lines changed

3 files changed

+74
-1
lines changed

changelog.d/11204.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add a module API method to retrieve the current state of a room.

synapse/module_api/__init__.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
DomainSpecificString,
5656
JsonDict,
5757
Requester,
58+
StateMap,
5859
UserID,
5960
UserInfo,
6061
create_requester,
@@ -89,6 +90,8 @@
8990
"PRESENCE_ALL_USERS",
9091
"LoginResponse",
9192
"JsonDict",
93+
"EventBase",
94+
"StateMap",
9295
]
9396

9497
logger = logging.getLogger(__name__)
@@ -964,6 +967,52 @@ async def get_user_ip_and_agents(
964967
else:
965968
return []
966969

970+
async def get_room_state(
971+
self,
972+
room_id: str,
973+
event_filter: Optional[Iterable[Tuple[str, Optional[str]]]] = None,
974+
) -> StateMap[EventBase]:
975+
"""Returns the current state of the given room.
976+
977+
The events are returned as a mapping, in which the key for each event is a tuple
978+
which first element is the event's type and the second one is its state key.
979+
980+
Added in Synapse v1.47.0
981+
982+
Args:
983+
room_id: The ID of the room to get state from.
984+
event_filter: A filter to apply when retrieving events. None if no filter
985+
should be applied. If provided, must be an iterable of tuples. A tuple's
986+
first element is the event type and the second is the state key, or is
987+
None if the state key should not be filtered on.
988+
An example of a filter is:
989+
[
990+
("m.room.member", "@alice:example.com"), # Member event for @alice:example.com
991+
("org.matrix.some_event", ""), # State event of type "org.matrix.some_event"
992+
# with an empty string as its state key
993+
("org.matrix.some_other_event", None), # State events of type "org.matrix.some_other_event"
994+
# regardless of their state key
995+
]
996+
"""
997+
if event_filter:
998+
# If a filter was provided, turn it into a StateFilter and retrieve a filtered
999+
# view of the state.
1000+
state_filter = StateFilter.from_types(event_filter)
1001+
state_ids = await self._store.get_filtered_current_state_ids(
1002+
room_id,
1003+
state_filter,
1004+
)
1005+
else:
1006+
# If no filter was provided, get the whole state. We could also reuse the call
1007+
# to get_filtered_current_state_ids above, with `state_filter = StateFilter.all()`,
1008+
# but get_filtered_current_state_ids isn't cached and `get_current_state_ids`
1009+
# is, so using the latter when we can is better for perf.
1010+
state_ids = await self._store.get_current_state_ids(room_id)
1011+
1012+
state_events = await self._store.get_events(state_ids.values())
1013+
1014+
return {key: state_events[event_id] for key, event_id in state_ids.items()}
1015+
9671016

9681017
class PublicRoomListManager:
9691018
"""Contains methods for adding to, removing from and querying whether a room

tests/module_api/test_api.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
from twisted.internet import defer
1717

18-
from synapse.api.constants import EduTypes
18+
from synapse.api.constants import EduTypes, EventTypes
1919
from synapse.events import EventBase
2020
from synapse.federation.units import Transaction
2121
from synapse.handlers.presence import UserPresenceState
@@ -509,6 +509,29 @@ def test_update_membership(self):
509509
self.assertEqual(res["displayname"], "simone")
510510
self.assertIsNone(res["avatar_url"])
511511

512+
def test_get_room_state(self):
513+
"""Tests that a module can retrieve the state of a room through the module API."""
514+
user_id = self.register_user("peter", "hackme")
515+
tok = self.login("peter", "hackme")
516+
517+
# Create a room and send some custom state in it.
518+
room_id = self.helper.create_room_as(tok=tok)
519+
self.helper.send_state(room_id, "org.matrix.test", {}, tok=tok)
520+
521+
# Check that the module API can successfully fetch state for the room.
522+
state = self.get_success(
523+
defer.ensureDeferred(self.module_api.get_room_state(room_id))
524+
)
525+
526+
# Check that a few standard events are in the returned state.
527+
self.assertIn((EventTypes.Create, ""), state)
528+
self.assertIn((EventTypes.Member, user_id), state)
529+
530+
# Check that our custom state event is in the returned state.
531+
self.assertEqual(state[("org.matrix.test", "")].sender, user_id)
532+
self.assertEqual(state[("org.matrix.test", "")].state_key, "")
533+
self.assertEqual(state[("org.matrix.test", "")].content, {})
534+
512535

513536
class ModuleApiWorkerTestCase(BaseMultiWorkerStreamTestCase):
514537
"""For testing ModuleApi functionality in a multi-worker setup"""

0 commit comments

Comments
 (0)