From 09f86339c198515ffd09ef5fae79db658276ad17 Mon Sep 17 00:00:00 2001 From: Olivier 'reivilibre Date: Fri, 18 Jul 2025 10:27:50 +0100 Subject: [PATCH] Add models for Thread Subscriptions extension to Sliding Sync --- synapse/rest/client/sync.py | 13 +++++++++++++ synapse/types/handlers/sliding_sync.py | 24 ++++++++++++++++++++++++ synapse/types/rest/client/__init__.py | 12 ++++++++++++ 3 files changed, 49 insertions(+) diff --git a/synapse/rest/client/sync.py b/synapse/rest/client/sync.py index 99864b73c1..487715e01e 100644 --- a/synapse/rest/client/sync.py +++ b/synapse/rest/client/sync.py @@ -23,6 +23,8 @@ import logging from collections import defaultdict from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Optional, Tuple, Union +import attrs + from synapse.api.constants import AccountDataTypes, EduTypes, Membership, PresenceState from synapse.api.errors import Codes, StoreError, SynapseError from synapse.api.filtering import FilterCollection @@ -1256,6 +1258,17 @@ class SlidingSyncRestServlet(RestServlet): "rooms": extensions.typing.room_id_to_typing_map, } + if ( + extensions.thread_subscriptions is not None + and extensions.thread_subscriptions.changed is not None + ): + serialized_extensions["thread_subscriptions"] = { + "changes": [ + attrs.asdict(change, filter=lambda _attr, v: v is not None) + for change in extensions.thread_subscriptions.changed + ] + } + return serialized_extensions diff --git a/synapse/types/handlers/sliding_sync.py b/synapse/types/handlers/sliding_sync.py index 3ebd334a6d..1af1eb4827 100644 --- a/synapse/types/handlers/sliding_sync.py +++ b/synapse/types/handlers/sliding_sync.py @@ -357,11 +357,34 @@ class SlidingSyncResult: def __bool__(self) -> bool: return bool(self.room_id_to_typing_map) + @attr.s(slots=True, frozen=True, auto_attribs=True) + class ThreadSubscriptionsExtension: + """The Thread Subscriptions extension (MSC4308) + + Attributes: + changes: list of changes to thread subscriptions + """ + + @attr.s(slots=True, frozen=True, auto_attribs=True) + class ThreadSubscriptionChange: + room_id: str + root_event_id: str + subscribed: bool + + # always present when `subscribed` + automatic: Optional[bool] + + changed: Optional[List[ThreadSubscriptionChange]] + + def __bool__(self) -> bool: + return bool(self.changed) + to_device: Optional[ToDeviceExtension] = None e2ee: Optional[E2eeExtension] = None account_data: Optional[AccountDataExtension] = None receipts: Optional[ReceiptsExtension] = None typing: Optional[TypingExtension] = None + thread_subscriptions: Optional[ThreadSubscriptionsExtension] = None def __bool__(self) -> bool: return bool( @@ -370,6 +393,7 @@ class SlidingSyncResult: or self.account_data or self.receipts or self.typing + or self.thread_subscriptions ) next_pos: SlidingSyncStreamToken diff --git a/synapse/types/rest/client/__init__.py b/synapse/types/rest/client/__init__.py index c739bd16b0..99b237455b 100644 --- a/synapse/types/rest/client/__init__.py +++ b/synapse/types/rest/client/__init__.py @@ -364,11 +364,23 @@ class SlidingSyncBody(RequestBodyModel): # Process all room subscriptions defined in the Room Subscription API. (This is the default.) rooms: Optional[List[StrictStr]] = ["*"] + class ThreadSubscriptionsExtension(RequestBodyModel): + """The Thread Subscriptions extension (MSC4308) + + Attributes: + enabled + limit: maximum number of subscription changes to return (default 100) + """ + + enabled: Optional[StrictBool] = False + limit: StrictInt = 100 + to_device: Optional[ToDeviceExtension] = None e2ee: Optional[E2eeExtension] = None account_data: Optional[AccountDataExtension] = None receipts: Optional[ReceiptsExtension] = None typing: Optional[TypingExtension] = None + thread_subscriptions: Optional[ThreadSubscriptionsExtension] = None conn_id: Optional[StrictStr]