diff --git a/synapse/rest/client/v1/room.py b/synapse/rest/client/v1/room.py
index 31a1193cd3..502a917588 100644
--- a/synapse/rest/client/v1/room.py
+++ b/synapse/rest/client/v1/room.py
@@ -413,7 +413,7 @@ class RoomBatchSendEventRestServlet(TransactionRestServlet):
assert_params_in_dict(body, ["state_events_at_start", "events"])
prev_events_from_query = parse_strings_from_args(request.args, "prev_event")
- chunk_id_from_query = parse_string(request, "chunk_id", default=None)
+ chunk_id_from_query = parse_string(request, "chunk_id")
if prev_events_from_query is None:
raise SynapseError(
@@ -504,7 +504,6 @@ class RoomBatchSendEventRestServlet(TransactionRestServlet):
events_to_create = body["events"]
- prev_event_ids = prev_events_from_query
inherited_depth = await self._inherit_depth_from_prev_ids(
prev_events_from_query
)
@@ -516,6 +515,10 @@ class RoomBatchSendEventRestServlet(TransactionRestServlet):
chunk_id_to_connect_to = chunk_id_from_query
base_insertion_event = None
if chunk_id_from_query:
+ # All but the first base insertion event should point at a fake
+ # event, which causes the HS to ask for the state at the start of
+ # the chunk later.
+ prev_event_ids = [fake_prev_event_id]
# TODO: Verify the chunk_id_from_query corresponds to an insertion event
pass
# Otherwise, create an insertion event to act as a starting point.
@@ -526,6 +529,8 @@ class RoomBatchSendEventRestServlet(TransactionRestServlet):
# an insertion event), in which case we just create a new insertion event
# that can then get pointed to by a "marker" event later.
else:
+ prev_event_ids = prev_events_from_query
+
base_insertion_event_dict = self._create_insertion_event_dict(
sender=requester.user.to_string(),
room_id=room_id,
@@ -553,9 +558,18 @@ class RoomBatchSendEventRestServlet(TransactionRestServlet):
]
# Connect this current chunk to the insertion event from the previous chunk
- last_event_in_chunk["content"][
- EventContentFields.MSC2716_CHUNK_ID
- ] = chunk_id_to_connect_to
+ chunk_event = {
+ "type": EventTypes.MSC2716_CHUNK,
+ "sender": requester.user.to_string(),
+ "room_id": room_id,
+ "content": {EventContentFields.MSC2716_CHUNK_ID: chunk_id_to_connect_to},
+ # Since the chunk event is put at the end of the chunk,
+ # where the newest-in-time event is, copy the origin_server_ts from
+ # the last event we're inserting
+ "origin_server_ts": last_event_in_chunk["origin_server_ts"],
+ }
+ # Add the chunk event to the end of the chunk (newest-in-time)
+ events_to_create.append(chunk_event)
# Add an "insertion" event to the start of each chunk (next to the oldest-in-time
# event in the chunk) so the next chunk can be connected to this one.
@@ -567,7 +581,7 @@ class RoomBatchSendEventRestServlet(TransactionRestServlet):
# the first event we're inserting
origin_server_ts=events_to_create[0]["origin_server_ts"],
)
- # Prepend the insertion event to the start of the chunk
+ # Prepend the insertion event to the start of the chunk (oldest-in-time)
events_to_create = [insertion_event] + events_to_create
event_ids = []
@@ -726,7 +740,7 @@ class PublicRoomListRestServlet(TransactionRestServlet):
self.auth = hs.get_auth()
async def on_GET(self, request):
- server = parse_string(request, "server", default=None)
+ server = parse_string(request, "server")
try:
await self.auth.get_user_by_req(request, allow_guest=True)
@@ -745,8 +759,8 @@ class PublicRoomListRestServlet(TransactionRestServlet):
if server:
raise e
- limit = parse_integer(request, "limit", 0)
- since_token = parse_string(request, "since", None)
+ limit: Optional[int] = parse_integer(request, "limit", 0)
+ since_token = parse_string(request, "since")
if limit == 0:
# zero is a special value which corresponds to no limit.
@@ -780,7 +794,7 @@ class PublicRoomListRestServlet(TransactionRestServlet):
async def on_POST(self, request):
await self.auth.get_user_by_req(request, allow_guest=True)
- server = parse_string(request, "server", default=None)
+ server = parse_string(request, "server")
content = parse_json_object_from_request(request)
limit: Optional[int] = int(content.get("limit", 100))
diff --git a/synapse/rest/client/v2_alpha/account.py b/synapse/rest/client/v2_alpha/account.py
index 085561d3e9..fb5ad2906e 100644
--- a/synapse/rest/client/v2_alpha/account.py
+++ b/synapse/rest/client/v2_alpha/account.py
@@ -884,7 +884,14 @@ class WhoamiRestServlet(RestServlet):
async def on_GET(self, request):
requester = await self.auth.get_user_by_req(request)
- return 200, {"user_id": requester.user.to_string()}
+ response = {"user_id": requester.user.to_string()}
+
+ # Appservices and similar accounts do not have device IDs
+ # that we can report on, so exclude them for compliance.
+ if requester.device_id is not None:
+ response["device_id"] = requester.device_id
+
+ return 200, response
def register_servlets(hs, http_server):
diff --git a/synapse/rest/client/v2_alpha/capabilities.py b/synapse/rest/client/v2_alpha/capabilities.py
index 6a24021484..88e3aac797 100644
--- a/synapse/rest/client/v2_alpha/capabilities.py
+++ b/synapse/rest/client/v2_alpha/capabilities.py
@@ -14,7 +14,7 @@
import logging
from typing import TYPE_CHECKING, Tuple
-from synapse.api.room_versions import KNOWN_ROOM_VERSIONS
+from synapse.api.room_versions import KNOWN_ROOM_VERSIONS, MSC3244_CAPABILITIES
from synapse.http.servlet import RestServlet
from synapse.http.site import SynapseRequest
from synapse.types import JsonDict
@@ -55,6 +55,12 @@ class CapabilitiesRestServlet(RestServlet):
"m.change_password": {"enabled": change_password},
}
}
+
+ if self.config.experimental.msc3244_enabled:
+ response["capabilities"]["m.room_versions"][
+ "org.matrix.msc3244.room_capabilities"
+ ] = MSC3244_CAPABILITIES
+
return 200, response
diff --git a/synapse/rest/client/v2_alpha/keys.py b/synapse/rest/client/v2_alpha/keys.py
index 33cf8de186..d0d9d30d40 100644
--- a/synapse/rest/client/v2_alpha/keys.py
+++ b/synapse/rest/client/v2_alpha/keys.py
@@ -194,7 +194,7 @@ class KeyChangesServlet(RestServlet):
async def on_GET(self, request):
requester = await self.auth.get_user_by_req(request, allow_guest=True)
- from_token_string = parse_string(request, "from")
+ from_token_string = parse_string(request, "from", required=True)
set_tag("from", from_token_string)
# We want to enforce they do pass us one, but we ignore it and return
diff --git a/synapse/rest/client/v2_alpha/read_marker.py b/synapse/rest/client/v2_alpha/read_marker.py
index 5988fa47e5..027f8b81fa 100644
--- a/synapse/rest/client/v2_alpha/read_marker.py
+++ b/synapse/rest/client/v2_alpha/read_marker.py
@@ -14,6 +14,8 @@
import logging
+from synapse.api.constants import ReadReceiptEventFields
+from synapse.api.errors import Codes, SynapseError
from synapse.http.servlet import RestServlet, parse_json_object_from_request
from ._base import client_patterns
@@ -37,14 +39,24 @@ class ReadMarkerRestServlet(RestServlet):
await self.presence_handler.bump_presence_active_time(requester.user)
body = parse_json_object_from_request(request)
-
read_event_id = body.get("m.read", None)
+ hidden = body.get(ReadReceiptEventFields.MSC2285_HIDDEN, False)
+
+ if not isinstance(hidden, bool):
+ raise SynapseError(
+ 400,
+ "Param %s must be a boolean, if given"
+ % ReadReceiptEventFields.MSC2285_HIDDEN,
+ Codes.BAD_JSON,
+ )
+
if read_event_id:
await self.receipts_handler.received_client_receipt(
room_id,
"m.read",
user_id=requester.user.to_string(),
event_id=read_event_id,
+ hidden=hidden,
)
read_marker_event_id = body.get("m.fully_read", None)
diff --git a/synapse/rest/client/v2_alpha/receipts.py b/synapse/rest/client/v2_alpha/receipts.py
index 8cf4aebdbe..4b98979b47 100644
--- a/synapse/rest/client/v2_alpha/receipts.py
+++ b/synapse/rest/client/v2_alpha/receipts.py
@@ -14,8 +14,9 @@
import logging
-from synapse.api.errors import SynapseError
-from synapse.http.servlet import RestServlet
+from synapse.api.constants import ReadReceiptEventFields
+from synapse.api.errors import Codes, SynapseError
+from synapse.http.servlet import RestServlet, parse_json_object_from_request
from ._base import client_patterns
@@ -42,10 +43,25 @@ class ReceiptRestServlet(RestServlet):
if receipt_type != "m.read":
raise SynapseError(400, "Receipt type must be 'm.read'")
+ body = parse_json_object_from_request(request)
+ hidden = body.get(ReadReceiptEventFields.MSC2285_HIDDEN, False)
+
+ if not isinstance(hidden, bool):
+ raise SynapseError(
+ 400,
+ "Param %s must be a boolean, if given"
+ % ReadReceiptEventFields.MSC2285_HIDDEN,
+ Codes.BAD_JSON,
+ )
+
await self.presence_handler.bump_presence_active_time(requester.user)
await self.receipts_handler.received_client_receipt(
- room_id, receipt_type, user_id=requester.user.to_string(), event_id=event_id
+ room_id,
+ receipt_type,
+ user_id=requester.user.to_string(),
+ event_id=event_id,
+ hidden=hidden,
)
return 200, {}
diff --git a/synapse/rest/client/v2_alpha/relations.py b/synapse/rest/client/v2_alpha/relations.py
index c7da6759db..0821cd285f 100644
--- a/synapse/rest/client/v2_alpha/relations.py
+++ b/synapse/rest/client/v2_alpha/relations.py
@@ -158,19 +158,21 @@ class RelationPaginationServlet(RestServlet):
event = await self.event_handler.get_event(requester.user, room_id, parent_id)
limit = parse_integer(request, "limit", default=5)
- from_token = parse_string(request, "from")
- to_token = parse_string(request, "to")
+ from_token_str = parse_string(request, "from")
+ to_token_str = parse_string(request, "to")
if event.internal_metadata.is_redacted():
# If the event is redacted, return an empty list of relations
pagination_chunk = PaginationChunk(chunk=[])
else:
# Return the relations
- if from_token:
- from_token = RelationPaginationToken.from_string(from_token)
+ from_token = None
+ if from_token_str:
+ from_token = RelationPaginationToken.from_string(from_token_str)
- if to_token:
- to_token = RelationPaginationToken.from_string(to_token)
+ to_token = None
+ if to_token_str:
+ to_token = RelationPaginationToken.from_string(to_token_str)
pagination_chunk = await self.store.get_relations_for_event(
event_id=parent_id,
@@ -256,19 +258,21 @@ class RelationAggregationPaginationServlet(RestServlet):
raise SynapseError(400, "Relation type must be 'annotation'")
limit = parse_integer(request, "limit", default=5)
- from_token = parse_string(request, "from")
- to_token = parse_string(request, "to")
+ from_token_str = parse_string(request, "from")
+ to_token_str = parse_string(request, "to")
if event.internal_metadata.is_redacted():
# If the event is redacted, return an empty list of relations
pagination_chunk = PaginationChunk(chunk=[])
else:
# Return the relations
- if from_token:
- from_token = AggregationPaginationToken.from_string(from_token)
+ from_token = None
+ if from_token_str:
+ from_token = AggregationPaginationToken.from_string(from_token_str)
- if to_token:
- to_token = AggregationPaginationToken.from_string(to_token)
+ to_token = None
+ if to_token_str:
+ to_token = AggregationPaginationToken.from_string(to_token_str)
pagination_chunk = await self.store.get_aggregation_groups_for_event(
event_id=parent_id,
@@ -336,14 +340,16 @@ class RelationAggregationGroupPaginationServlet(RestServlet):
raise SynapseError(400, "Relation type must be 'annotation'")
limit = parse_integer(request, "limit", default=5)
- from_token = parse_string(request, "from")
- to_token = parse_string(request, "to")
+ from_token_str = parse_string(request, "from")
+ to_token_str = parse_string(request, "to")
- if from_token:
- from_token = RelationPaginationToken.from_string(from_token)
+ from_token = None
+ if from_token_str:
+ from_token = RelationPaginationToken.from_string(from_token_str)
- if to_token:
- to_token = RelationPaginationToken.from_string(to_token)
+ to_token = None
+ if to_token_str:
+ to_token = RelationPaginationToken.from_string(to_token_str)
result = await self.store.get_relations_for_event(
event_id=parent_id,
diff --git a/synapse/rest/client/v2_alpha/sync.py b/synapse/rest/client/v2_alpha/sync.py
index 32e8500795..e321668698 100644
--- a/synapse/rest/client/v2_alpha/sync.py
+++ b/synapse/rest/client/v2_alpha/sync.py
@@ -112,7 +112,7 @@ class SyncRestServlet(RestServlet):
default="online",
allowed_values=self.ALLOWED_PRESENCE,
)
- filter_id = parse_string(request, "filter", default=None)
+ filter_id = parse_string(request, "filter")
full_state = parse_boolean(request, "full_state", default=False)
logger.debug(
diff --git a/synapse/rest/client/versions.py b/synapse/rest/client/versions.py
index 4582c274c7..fa2e4e9cba 100644
--- a/synapse/rest/client/versions.py
+++ b/synapse/rest/client/versions.py
@@ -82,6 +82,8 @@ class VersionsRestServlet(RestServlet):
"io.element.e2ee_forced.trusted_private": self.e2ee_forced_trusted_private,
# Supports the busy presence state described in MSC3026.
"org.matrix.msc3026.busy_presence": self.config.experimental.msc3026_enabled,
+ # Supports receiving hidden read receipts as per MSC2285
+ "org.matrix.msc2285": self.config.experimental.msc2285_enabled,
},
},
)
|