Skip to content

Commit fa91655

Browse files
Return some room data in Sliding Sync /sync (#17320)
- Timeline events - Stripped `invite_state` Based on [MSC3575](matrix-org/matrix-spec-proposals#3575): Sliding Sync
1 parent 0d2b75c commit fa91655

File tree

14 files changed

+3593
-259
lines changed

14 files changed

+3593
-259
lines changed

changelog.d/17320.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add `rooms` data to experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint.

synapse/events/utils.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -836,3 +836,21 @@ def maybe_upsert_event_field(
836836
del container[key]
837837

838838
return upsert_okay
839+
840+
841+
def strip_event(event: EventBase) -> JsonDict:
842+
"""
843+
Used for "stripped state" events which provide a simplified view of the state of a
844+
room intended to help a potential joiner identify the room (relevant when the user
845+
is invited or knocked).
846+
847+
Stripped state events can only have the `sender`, `type`, `state_key` and `content`
848+
properties present.
849+
"""
850+
851+
return {
852+
"type": event.type,
853+
"state_key": event.state_key,
854+
"content": event.content,
855+
"sender": event.sender,
856+
}

synapse/handlers/sliding_sync.py

Lines changed: 490 additions & 152 deletions
Large diffs are not rendered by default.

synapse/rest/client/sync.py

Lines changed: 99 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -761,7 +761,6 @@ class SlidingSyncRestServlet(RestServlet):
761761
"lists": {
762762
"foo-list": {
763763
"ranges": [ [0, 99] ],
764-
"sort": [ "by_notification_level", "by_recency", "by_name" ],
765764
"required_state": [
766765
["m.room.join_rules", ""],
767766
["m.room.history_visibility", ""],
@@ -771,18 +770,13 @@ class SlidingSyncRestServlet(RestServlet):
771770
"filters": {
772771
"is_dm": true
773772
},
774-
"bump_event_types": [ "m.room.message", "m.room.encrypted" ],
775773
}
776774
},
777775
// Room Subscriptions API
778776
"room_subscriptions": {
779777
"!sub1:bar": {
780778
"required_state": [ ["*","*"] ],
781779
"timeline_limit": 10,
782-
"include_old_rooms": {
783-
"timeline_limit": 1,
784-
"required_state": [ ["m.room.tombstone", ""], ["m.room.create", ""] ],
785-
}
786780
}
787781
},
788782
// Extensions API
@@ -791,7 +785,7 @@ class SlidingSyncRestServlet(RestServlet):
791785
792786
Response JSON::
793787
{
794-
"next_pos": "s58_224_0_13_10_1_1_16_0_1",
788+
"pos": "s58_224_0_13_10_1_1_16_0_1",
795789
"lists": {
796790
"foo-list": {
797791
"count": 1337,
@@ -830,7 +824,8 @@ class SlidingSyncRestServlet(RestServlet):
830824
"joined_count": 41,
831825
"invited_count": 1,
832826
"notification_count": 1,
833-
"highlight_count": 0
827+
"highlight_count": 0,
828+
"num_live": 2"
834829
},
835830
// rooms from list
836831
"!foo:bar": {
@@ -855,7 +850,8 @@ class SlidingSyncRestServlet(RestServlet):
855850
"joined_count": 4,
856851
"invited_count": 0,
857852
"notification_count": 54,
858-
"highlight_count": 3
853+
"highlight_count": 3,
854+
"num_live": 1,
859855
},
860856
// ... 99 more items
861857
},
@@ -871,10 +867,11 @@ def __init__(self, hs: "HomeServer"):
871867
super().__init__()
872868
self.auth = hs.get_auth()
873869
self.store = hs.get_datastores().main
870+
self.clock = hs.get_clock()
874871
self.filtering = hs.get_filtering()
875872
self.sliding_sync_handler = hs.get_sliding_sync_handler()
873+
self.event_serializer = hs.get_event_client_serializer()
876874

877-
# TODO: Update this to `on_GET` once we figure out how we want to handle params
878875
async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
879876
requester = await self.auth.get_user_by_req(request, allow_guest=True)
880877
user = requester.user
@@ -920,22 +917,25 @@ async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
920917
logger.info("Client has disconnected; not serializing response.")
921918
return 200, {}
922919

923-
response_content = await self.encode_response(sliding_sync_results)
920+
response_content = await self.encode_response(requester, sliding_sync_results)
924921

925922
return 200, response_content
926923

927924
# TODO: Is there a better way to encode things?
928925
async def encode_response(
929926
self,
927+
requester: Requester,
930928
sliding_sync_result: SlidingSyncResult,
931929
) -> JsonDict:
932930
response: JsonDict = defaultdict(dict)
933931

934-
response["next_pos"] = await sliding_sync_result.next_pos.to_string(self.store)
932+
response["pos"] = await sliding_sync_result.next_pos.to_string(self.store)
935933
serialized_lists = self.encode_lists(sliding_sync_result.lists)
936934
if serialized_lists:
937935
response["lists"] = serialized_lists
938-
response["rooms"] = {} # TODO: sliding_sync_result.rooms
936+
response["rooms"] = await self.encode_rooms(
937+
requester, sliding_sync_result.rooms
938+
)
939939
response["extensions"] = {} # TODO: sliding_sync_result.extensions
940940

941941
return response
@@ -961,6 +961,92 @@ def encode_operation(
961961

962962
return serialized_lists
963963

964+
async def encode_rooms(
965+
self,
966+
requester: Requester,
967+
rooms: Dict[str, SlidingSyncResult.RoomResult],
968+
) -> JsonDict:
969+
time_now = self.clock.time_msec()
970+
971+
serialize_options = SerializeEventConfig(
972+
event_format=format_event_for_client_v2_without_room_id,
973+
requester=requester,
974+
)
975+
976+
serialized_rooms: Dict[str, JsonDict] = {}
977+
for room_id, room_result in rooms.items():
978+
serialized_rooms[room_id] = {
979+
"joined_count": room_result.joined_count,
980+
"invited_count": room_result.invited_count,
981+
"notification_count": room_result.notification_count,
982+
"highlight_count": room_result.highlight_count,
983+
}
984+
985+
if room_result.name:
986+
serialized_rooms[room_id]["name"] = room_result.name
987+
988+
if room_result.avatar:
989+
serialized_rooms[room_id]["avatar"] = room_result.avatar
990+
991+
if room_result.heroes:
992+
serialized_rooms[room_id]["heroes"] = room_result.heroes
993+
994+
# We should only include the `initial` key if it's `True` to save bandwidth.
995+
# The absense of this flag means `False`.
996+
if room_result.initial:
997+
serialized_rooms[room_id]["initial"] = room_result.initial
998+
999+
# This will omitted for invite/knock rooms with `stripped_state`
1000+
if room_result.required_state is not None:
1001+
serialized_required_state = (
1002+
await self.event_serializer.serialize_events(
1003+
room_result.required_state,
1004+
time_now,
1005+
config=serialize_options,
1006+
)
1007+
)
1008+
serialized_rooms[room_id]["required_state"] = serialized_required_state
1009+
1010+
# This will omitted for invite/knock rooms with `stripped_state`
1011+
if room_result.timeline_events is not None:
1012+
serialized_timeline = await self.event_serializer.serialize_events(
1013+
room_result.timeline_events,
1014+
time_now,
1015+
config=serialize_options,
1016+
bundle_aggregations=room_result.bundled_aggregations,
1017+
)
1018+
serialized_rooms[room_id]["timeline"] = serialized_timeline
1019+
1020+
# This will omitted for invite/knock rooms with `stripped_state`
1021+
if room_result.limited is not None:
1022+
serialized_rooms[room_id]["limited"] = room_result.limited
1023+
1024+
# This will omitted for invite/knock rooms with `stripped_state`
1025+
if room_result.prev_batch is not None:
1026+
serialized_rooms[room_id]["prev_batch"] = (
1027+
await room_result.prev_batch.to_string(self.store)
1028+
)
1029+
1030+
# This will omitted for invite/knock rooms with `stripped_state`
1031+
if room_result.num_live is not None:
1032+
serialized_rooms[room_id]["num_live"] = room_result.num_live
1033+
1034+
# Field should be absent on non-DM rooms
1035+
if room_result.is_dm:
1036+
serialized_rooms[room_id]["is_dm"] = room_result.is_dm
1037+
1038+
# Stripped state only applies to invite/knock rooms
1039+
if room_result.stripped_state is not None:
1040+
# TODO: `knocked_state` but that isn't specced yet.
1041+
#
1042+
# TODO: Instead of adding `knocked_state`, it would be good to rename
1043+
# this to `stripped_state` so it can be shared between invite and knock
1044+
# rooms, see
1045+
# https://github.com/matrix-org/matrix-spec-proposals/pull/3575#discussion_r1117629919
1046+
serialized_rooms[room_id]["invite_state"] = room_result.stripped_state
1047+
1048+
return serialized_rooms
1049+
9641050

9651051
def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
9661052
SyncRestServlet(hs).register(http_server)

synapse/storage/databases/main/events_worker.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
)
5656
from synapse.events import EventBase, make_event_from_dict
5757
from synapse.events.snapshot import EventContext
58-
from synapse.events.utils import prune_event
58+
from synapse.events.utils import prune_event, strip_event
5959
from synapse.logging.context import (
6060
PreserveLoggingContext,
6161
current_context,
@@ -1025,15 +1025,7 @@ async def get_stripped_room_state_from_event_context(
10251025

10261026
state_to_include = await self.get_events(selected_state_ids.values())
10271027

1028-
return [
1029-
{
1030-
"type": e.type,
1031-
"state_key": e.state_key,
1032-
"content": e.content,
1033-
"sender": e.sender,
1034-
}
1035-
for e in state_to_include.values()
1036-
]
1028+
return [strip_event(e) for e in state_to_include.values()]
10371029

10381030
def _maybe_start_fetch_thread(self) -> None:
10391031
"""Starts an event fetch thread if we are not yet at the maximum number."""

0 commit comments

Comments
 (0)