diff options
author | Andrew Morgan <andrew@amorgan.xyz> | 2020-11-12 16:41:03 +0000 |
---|---|---|
committer | Andrew Morgan <andrew@amorgan.xyz> | 2020-11-13 16:23:00 +0000 |
commit | 3dbe05d02213440a11d1ef2a12c2ef15abe70be9 (patch) | |
tree | 07fdad675598a63c05c1691d3ea2b27fd50c030e /synapse/rest | |
parent | Send stripped state events back to the knocking homeserver (diff) | |
download | synapse-3dbe05d02213440a11d1ef2a12c2ef15abe70be9.tar.xz |
Extend sync to inform clients about the progress of their knocks
So we've got federation so that homeservers can communicate knocking information between them - but how does that information actually get down to the client? The client knows that it knocked successfully from a 200 in its original request, but what else does it need? This commit adds a new "knock" section to /sync (in addition to "invite", "join", and "leave") all help give the client the information it needs. The new "knock" section is used for sending down the stripped state events we collected earlier. The client will use these to display the room and its metadata in a little "pending knocks" section or similar. This is all this commit adds. If the user's knock has been accepted or rejected, they will receive that information in the "join" or "leave" sections of /sync. Most of this code is just cribbing off the invite and join sync code yet again, with some minor differences. For instance, we don't need to exclude knock events from sync if the sender is in your ignore list, as you are the only ones that can send knocks for yourself. The structure of the "knock" dict in sync is modeled after "invite", as clients also receive stripped state in that. The structure can be viewed in the linked MSC.
Diffstat (limited to 'synapse/rest')
-rw-r--r-- | synapse/rest/client/v2_alpha/sync.py | 62 |
1 files changed, 59 insertions, 3 deletions
diff --git a/synapse/rest/client/v2_alpha/sync.py b/synapse/rest/client/v2_alpha/sync.py index 2b84eb89c0..2c9c5add72 100644 --- a/synapse/rest/client/v2_alpha/sync.py +++ b/synapse/rest/client/v2_alpha/sync.py @@ -15,6 +15,7 @@ import itertools import logging +from typing import Any, Callable, Dict, List from synapse.api.constants import PresenceState from synapse.api.errors import Codes, StoreError, SynapseError @@ -24,7 +25,7 @@ from synapse.events.utils import ( format_event_raw, ) from synapse.handlers.presence import format_user_presence_state -from synapse.handlers.sync import SyncConfig +from synapse.handlers.sync import KnockedSyncResult, SyncConfig from synapse.http.servlet import RestServlet, parse_boolean, parse_integer, parse_string from synapse.types import StreamToken from synapse.util import json_decoder @@ -212,6 +213,10 @@ class SyncRestServlet(RestServlet): sync_result.invited, time_now, access_token_id, event_formatter ) + knocked = await self.encode_knocked( + sync_result.knocked, time_now, access_token_id, event_formatter + ) + archived = await self.encode_archived( sync_result.archived, time_now, @@ -229,7 +234,12 @@ class SyncRestServlet(RestServlet): "left": list(sync_result.device_lists.left), }, "presence": SyncRestServlet.encode_presence(sync_result.presence, time_now), - "rooms": {"join": joined, "invite": invited, "leave": archived}, + "rooms": { + "join": joined, + "invite": invited, + "knock": knocked, + "leave": archived, + }, "groups": { "join": sync_result.groups.join, "invite": sync_result.groups.invite, @@ -295,7 +305,7 @@ class SyncRestServlet(RestServlet): Args: rooms(list[synapse.handlers.sync.InvitedSyncResult]): list of - sync results for rooms this user is joined to + sync results for rooms this user is invited to time_now(int): current time - used as a baseline for age calculations token_id(int): ID of the user's auth token - used for namespacing @@ -324,6 +334,52 @@ class SyncRestServlet(RestServlet): return invited + async def encode_knocked( + self, + rooms: List[KnockedSyncResult], + time_now: int, + token_id: int, + event_formatter: Callable[[Dict], Dict], + ) -> Dict[str, Dict[str, Any]]: + """ + Encode the rooms we've knocked on in a sync result. + + Args: + rooms: list of sync results for rooms this user is knocking on + time_now: current time - used as a baseline for age calculations + token_id: ID of the user's auth token - used for namespacing of transaction IDs + event_formatter: function to convert from federation format to client format + + Returns: + The list of rooms the user has knocked on, in our response format. + """ + knocked = {} + for room in rooms: + knock = await self._event_serializer.serialize_event( + room.knock, time_now, token_id=token_id, event_format=event_formatter, + ) + + # Extract the `unsigned` key from the knock event. + # This is where we (cheekily) store the knock state events + unsigned = knock.setdefault("unsigned", {}) + + # Extract the stripped room state from the unsigned dict + # This is for clients to get a little bit of information about + # the room they've knocked on, without revealing any sensitive information + knocked_state = list(unsigned.pop("knock_room_state", [])) + + # Append the actual knock membership event itself as well + # TODO: I *believe* this is just for the client's sake of track its membership + # state in each room, but I could be wrong. This certainly doesn't seem like it + # could have any negative effects besides resource usage + knocked_state.append(knock) + + # Build the `knock_state` dictionary, which will contain the state of the + # room that the client has knocked on + knocked[room.room_id] = {"knock_state": {"events": knocked_state}} + + return knocked + async def encode_archived( self, rooms, time_now, token_id, event_fields, event_formatter ): |