From 7b671f4dd65bd9b7414397198715e820b407cbbd Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Wed, 18 Mar 2026 13:27:17 +0000 Subject: [PATCH] Move sticky_ttl_ms serialization to serialize_event --- synapse/events/utils.py | 23 +++++++++++++++++++++++ synapse/visibility.py | 14 -------------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/synapse/events/utils.py b/synapse/events/utils.py index 3919e752ac..7166341a28 100644 --- a/synapse/events/utils.py +++ b/synapse/events/utils.py @@ -41,6 +41,7 @@ from synapse.api.constants import ( MAX_PDU_SIZE, EventContentFields, EventTypes, + EventUnsignedContentFields, RelationTypes, ) from synapse.api.errors import Codes, SynapseError @@ -435,6 +436,9 @@ class SerializeEventConfig: # only server admins can see through other configuration. For example, # whether an event was soft failed by the server. include_admin_metadata: bool = False + # Whether MSC4354 (sticky events) is enabled. When True, the sticky TTL + # will be computed and included in the unsigned section of sticky events. + msc4354_enabled: bool = False _DEFAULT_SERIALIZE_EVENT_CONFIG = SerializeEventConfig() @@ -554,6 +558,20 @@ def _serialize_event( if e.internal_metadata.policy_server_spammy: d["unsigned"]["io.element.synapse.policy_server_spammy"] = True + if config.msc4354_enabled: + sticky_duration = e.sticky_duration() + if sticky_duration: + expires_at = ( + # min() ensures that the origin server can't lie about the time and + # send the event 'in the future', as that would allow them to exceed + # the 1 hour limit on stickiness duration. + min(e.origin_server_ts, time_now_ms) + sticky_duration.as_millis() + ) + if expires_at > time_now_ms: + d["unsigned"][EventUnsignedContentFields.STICKY_TTL] = ( + expires_at - time_now_ms + ) + only_event_fields = config.only_event_fields if only_event_fields: if not isinstance(only_event_fields, list) or not all( @@ -575,6 +593,8 @@ class EventClientSerializer: def __init__(self, hs: "HomeServer") -> None: self._store = hs.get_datastores().main self._auth = hs.get_auth() + self._config = hs.config + self._clock = hs.get_clock() self._add_extra_fields_to_unsigned_client_event_callbacks: list[ ADD_EXTRA_FIELDS_TO_UNSIGNED_CLIENT_EVENT_CALLBACK ] = [] @@ -615,6 +635,9 @@ class EventClientSerializer: ): config = make_config_for_admin(config) + if self._config.experimental.msc4354_enabled: + config = attr.evolve(config, msc4354_enabled=True) + serialized_event = _serialize_event(event, time_now, config=config) # If the event was redacted, fetch the redaction event from the database diff --git a/synapse/visibility.py b/synapse/visibility.py index 5ba2a14a24..452a2d50fb 100644 --- a/synapse/visibility.py +++ b/synapse/visibility.py @@ -237,20 +237,6 @@ async def filter_and_transform_events_for_client( # to the cache! cloned = clone_event(filtered) cloned.unsigned[EventUnsignedContentFields.MEMBERSHIP] = user_membership - if storage.main.config.experimental.msc4354_enabled: - sticky_duration = cloned.sticky_duration() - if sticky_duration: - now_ms = storage.main.clock.time_msec() - expires_at = ( - # min() ensures that the origin server can't lie about the time and - # send the event 'in the future', as that would allow them to exceed - # the 1 hour limit on stickiness duration. - min(cloned.origin_server_ts, now_ms) + sticky_duration.as_millis() - ) - if expires_at > now_ms: - cloned.unsigned[EventUnsignedContentFields.STICKY_TTL] = ( - expires_at - now_ms - ) return cloned