diff --git a/synapse/config/experimental.py b/synapse/config/experimental.py index a436f6a65..9c63c272d 100644 --- a/synapse/config/experimental.py +++ b/synapse/config/experimental.py @@ -600,3 +600,6 @@ def read_config( "beeper_user_notification_counts_enabled", False, ) + + # MSC4446: Allow moving the fully read marker backwards. + self.msc4446_enabled: bool = experimental.get("msc4446_enabled", False) diff --git a/synapse/handlers/read_marker.py b/synapse/handlers/read_marker.py index 1df418d3b..9aeac3ee1 100644 --- a/synapse/handlers/read_marker.py +++ b/synapse/handlers/read_marker.py @@ -44,6 +44,7 @@ async def received_client_read_marker( room_id: str, user_id: str, event_id: str, + allow_backward: bool = False, extra_content: Optional[JsonDict] = None, ) -> None: """Updates the read marker for a given user in a given room if the event ID given @@ -62,7 +63,7 @@ async def received_client_read_marker( # Get event ordering, this also ensures we know about the event event_ordering = await self.store.get_event_ordering(event_id, room_id) - if existing_read_marker: + if existing_read_marker and not allow_backward: try: old_event_ordering = await self.store.get_event_ordering( existing_read_marker["event_id"], room_id diff --git a/synapse/rest/client/read_marker.py b/synapse/rest/client/read_marker.py index e92903cd8..a21811d60 100644 --- a/synapse/rest/client/read_marker.py +++ b/synapse/rest/client/read_marker.py @@ -23,6 +23,7 @@ from typing import TYPE_CHECKING, Tuple from synapse.api.constants import ReceiptTypes +from synapse.api.errors import Codes, SynapseError from synapse.http.server import HttpServer from synapse.http.servlet import RestServlet, parse_json_object_from_request from synapse.http.site import SynapseRequest @@ -73,6 +74,19 @@ async def handle_read_marker( ) unrecognized_types = set(body.keys()) - self._known_receipt_types + + if self.config.experimental.msc4446_enabled: + allow_backward = body.get("com.beeper.allow_backward", False) + if not isinstance(allow_backward, bool): + raise SynapseError( + 400, + "com.beeper.allow_backward must be a boolean.", + Codes.INVALID_PARAM, + ) + unrecognized_types -= {"com.beeper.allow_backward"} + else: + allow_backward = False + if unrecognized_types: # It's fine if there are unrecognized receipt types, but let's log # it to help debug clients that have typoed the receipt type. @@ -93,6 +107,7 @@ async def handle_read_marker( room_id, user_id=requester.user.to_string(), event_id=event_id, + allow_backward=allow_backward, extra_content=body.get("com.beeper.fully_read.extra", None), ) else: diff --git a/synapse/rest/client/receipts.py b/synapse/rest/client/receipts.py index 11f7f3fb5..bb266d297 100644 --- a/synapse/rest/client/receipts.py +++ b/synapse/rest/client/receipts.py @@ -20,6 +20,7 @@ # import logging +from http import HTTPStatus from typing import TYPE_CHECKING, Tuple from synapse.api.constants import MAIN_TIMELINE, ReceiptTypes @@ -50,6 +51,7 @@ def __init__(self, hs: "HomeServer"): self.read_marker_handler = hs.get_read_marker_handler() self.presence_handler = hs.get_presence_handler() self._main_store = hs.get_datastores().main + self._msc4446_enabled = hs.config.experimental.msc4446_enabled self._known_receipt_types = { ReceiptTypes.READ, @@ -74,6 +76,25 @@ async def on_POST( body = parse_json_object_from_request(request, allow_empty_body=False) + if self._msc4446_enabled: + allow_backward = body.get("com.beeper.allow_backward", False) + if not isinstance(allow_backward, bool): + raise SynapseError( + HTTPStatus.BAD_REQUEST, + "com.beeper.allow_backward must be a boolean.", + Codes.INVALID_PARAM, + ) + + if allow_backward and receipt_type != ReceiptTypes.FULLY_READ: + raise SynapseError( + HTTPStatus.BAD_REQUEST, + "com.beeper.allow_backward is only allowed to be true for " + f"{ReceiptTypes.FULLY_READ}.", + Codes.INVALID_PARAM, + ) + else: + allow_backward = False + # Pull the thread ID, if one exists. thread_id = None if "thread_id" in body: @@ -109,6 +130,7 @@ async def on_POST( room_id, user_id=requester.user.to_string(), event_id=event_id, + allow_backward=allow_backward, extra_content=body, ) else: diff --git a/synapse/rest/client/versions.py b/synapse/rest/client/versions.py index 20395430d..0673b0a7e 100644 --- a/synapse/rest/client/versions.py +++ b/synapse/rest/client/versions.py @@ -180,6 +180,8 @@ async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]: "org.matrix.msc4155": self.config.experimental.msc4155_enabled, # MSC4306: Support for thread subscriptions "org.matrix.msc4306": self.config.experimental.msc4306_enabled, + # MSC4446: Allow moving the fully read marker backwards. + "com.beeper.msc4446": self.config.experimental.msc4446_enabled, # MSC4169: Backwards-compatible redaction sending using `/send` "com.beeper.msc4169": self.config.experimental.msc4169_enabled, },