From 418b936255df6a4f22e9e0f9a0db3069dfd21a47 Mon Sep 17 00:00:00 2001 From: Michael Hollister Date: Sun, 10 Mar 2024 22:54:48 -0500 Subject: [PATCH 1/8] Added presence update on change of profile information and config flags for selective presence tracking Signed-off-by: Michael Hollister --- changelog.d/16992.feature | 1 + synapse/api/presence.py | 4 ++ synapse/config/server.py | 10 +++++ synapse/federation/federation_server.py | 11 +++++ synapse/handlers/presence.py | 43 +++++++++++++++++-- synapse/handlers/profile.py | 26 +++++++++++ synapse/replication/tcp/streams/_base.py | 2 + synapse/storage/databases/main/presence.py | 26 +++++++++-- synapse/storage/schema/__init__.py | 5 ++- .../delta/87/01_presence_stream_updates.sql | 20 +++++++++ tests/api/test_filtering.py | 4 ++ tests/handlers/test_presence.py | 4 ++ 12 files changed, 148 insertions(+), 8 deletions(-) create mode 100644 changelog.d/16992.feature create mode 100644 synapse/storage/schema/main/delta/87/01_presence_stream_updates.sql diff --git a/changelog.d/16992.feature b/changelog.d/16992.feature new file mode 100644 index 00000000000..903916be7e5 --- /dev/null +++ b/changelog.d/16992.feature @@ -0,0 +1 @@ +Added presence update on change of profile information and config flags for selective presence tracking. Contributed by @Michael-Hollister. diff --git a/synapse/api/presence.py b/synapse/api/presence.py index 28c10403cef..891746dcd14 100644 --- a/synapse/api/presence.py +++ b/synapse/api/presence.py @@ -83,6 +83,8 @@ class UserPresenceState: last_user_sync_ts: int status_msg: Optional[str] currently_active: bool + displayname: Optional[str] + avatar_url: Optional[str] def as_dict(self) -> JsonDict: return attr.asdict(self) @@ -101,4 +103,6 @@ def default(cls, user_id: str) -> "UserPresenceState": last_user_sync_ts=0, status_msg=None, currently_active=False, + displayname=None, + avatar_url=None, ) diff --git a/synapse/config/server.py b/synapse/config/server.py index fd52c0475cf..b7791456c2f 100644 --- a/synapse/config/server.py +++ b/synapse/config/server.py @@ -389,6 +389,16 @@ def read_config(self, config: JsonDict, **kwargs: Any) -> None: "include_offline_users_on_sync", False ) + # Disabling server-side presence tracking + self.sync_presence_tracking = presence_config.get( + "sync_presence_tracking", True + ) + + # Disabling federation presence tracking + self.federation_presence_tracking = presence_config.get( + "federation_presence_tracking", True + ) + # Custom presence router module # This is the legacy way of configuring it (the config should now be put in the modules section) self.presence_router_module_class = None diff --git a/synapse/federation/federation_server.py b/synapse/federation/federation_server.py index 1932fa82a4a..0460a45168c 100644 --- a/synapse/federation/federation_server.py +++ b/synapse/federation/federation_server.py @@ -1428,6 +1428,17 @@ async def on_edu(self, edu_type: str, origin: str, content: dict) -> None: if not self.config.server.track_presence and edu_type == EduTypes.PRESENCE: return + if ( + not self.config.server.federation_presence_tracking + and edu_type == EduTypes.PRESENCE + ): + filtered_edus = [] + for e in content["push"]: + # Process only profile presence updates to reduce resource impact + if "status_msg" in e or "displayname" in e or "avatar_url" in e: + filtered_edus.append(e) + content["push"] = filtered_edus + # Check if we have a handler on this instance handler = self.edu_handlers.get(edu_type) if handler: diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py index 37ee625f717..6ea4cf78677 100644 --- a/synapse/handlers/presence.py +++ b/synapse/handlers/presence.py @@ -201,6 +201,7 @@ def __init__(self, hs: "HomeServer"): self._presence_enabled = hs.config.server.presence_enabled self._track_presence = hs.config.server.track_presence + self._sync_presence_tracking = hs.config.server.sync_presence_tracking self._federation = None if hs.should_send_federation(): @@ -451,6 +452,8 @@ async def send_full_presence_to_users(self, user_ids: StrCollection) -> None: state = { "presence": current_presence_state.state, "status_message": current_presence_state.status_msg, + "displayname": current_presence_state.displayname, + "avatar_url": current_presence_state.avatar_url, } # Copy the presence state to the tip of the presence stream. @@ -579,7 +582,11 @@ async def user_syncing( Called by the sync and events servlets to record that a user has connected to this worker and is waiting for some events. """ - if not affect_presence or not self._track_presence: + if ( + not affect_presence + or not self._track_presence + or not self._sync_presence_tracking + ): return _NullContextManager() # Note that this causes last_active_ts to be incremented which is not @@ -648,6 +655,8 @@ async def process_replication_rows( row.last_user_sync_ts, row.status_msg, row.currently_active, + row.displayname, + row.avatar_url, ) for row in rows ] @@ -1140,7 +1149,11 @@ async def user_syncing( client that is being used by a user. presence_state: The presence state indicated in the sync request """ - if not affect_presence or not self._track_presence: + if ( + not affect_presence + or not self._track_presence + or not self._sync_presence_tracking + ): return _NullContextManager() curr_sync = self._user_device_to_num_current_syncs.get((user_id, device_id), 0) @@ -1340,6 +1353,8 @@ async def incoming_presence(self, origin: str, content: JsonDict) -> None: new_fields["status_msg"] = push.get("status_msg", None) new_fields["currently_active"] = push.get("currently_active", False) + new_fields["displayname"] = push.get("displayname", None) + new_fields["avatar_url"] = push.get("avatar_url", None) prev_state = await self.current_state_for_user(user_id) updates.append(prev_state.copy_and_replace(**new_fields)) @@ -1369,6 +1384,8 @@ async def set_state( the `state` dict. """ status_msg = state.get("status_msg", None) + displayname = state.get("displayname", None) + avatar_url = state.get("avatar_url", None) presence = state["presence"] if presence not in self.VALID_PRESENCE: @@ -1414,6 +1431,8 @@ async def set_state( else: # Syncs do not override the status message. new_fields["status_msg"] = status_msg + new_fields["displayname"] = displayname + new_fields["avatar_url"] = avatar_url await self._update_states( [prev_state.copy_and_replace(**new_fields)], force_notify=force_notify @@ -1634,6 +1653,8 @@ async def _handle_state_delta(self, room_id: str, deltas: List[StateDelta]) -> N if state.state != PresenceState.OFFLINE or now - state.last_active_ts < 7 * 24 * 60 * 60 * 1000 or state.status_msg is not None + or state.displayname is not None + or state.avatar_url is not None ] await self._federation_queue.send_presence_to_destinations( @@ -1668,6 +1689,14 @@ def should_notify( notify_reason_counter.labels(user_location, "status_msg_change").inc() return True + if old_state.displayname != new_state.displayname: + notify_reason_counter.labels(user_location, "displayname_change").inc() + return True + + if old_state.avatar_url != new_state.avatar_url: + notify_reason_counter.labels(user_location, "avatar_url_change").inc() + return True + if old_state.state != new_state.state: notify_reason_counter.labels(user_location, "state_change").inc() state_transition_counter.labels( @@ -1725,6 +1754,8 @@ def format_user_presence_state( * status_msg: Optional. Included if `status_msg` is set on `state`. The user's status. * currently_active: Optional. Included only if `state.state` is "online". + * displayname: Optional. The current display name for this user, if any. + * avatar_url: Optional. The current avatar URL for this user, if any. Example: @@ -1733,7 +1764,9 @@ def format_user_presence_state( "user_id": "@alice:example.com", "last_active_ago": 16783813918, "status_msg": "Hello world!", - "currently_active": True + "currently_active": True, + "displayname": "Alice", + "avatar_url": "mxc://localhost/wefuiwegh8742w" } """ content: JsonDict = {"presence": state.state} @@ -1745,6 +1778,10 @@ def format_user_presence_state( content["status_msg"] = state.status_msg if state.state == PresenceState.ONLINE: content["currently_active"] = state.currently_active + if state.displayname: + content["displayname"] = state.displayname + if state.avatar_url: + content["avatar_url"] = state.avatar_url return content diff --git a/synapse/handlers/profile.py b/synapse/handlers/profile.py index 6663d4b271b..13ca64400ea 100644 --- a/synapse/handlers/profile.py +++ b/synapse/handlers/profile.py @@ -202,6 +202,19 @@ async def set_displayname( if propagate: await self._update_join_states(requester, target_user) + if self.hs.config.server.track_presence: + presence_handler = self.hs.get_presence_handler() + current_presence_state = await presence_handler.get_state(target_user) + + state = { + "presence": current_presence_state.state, + "status_message": current_presence_state.status_msg, + "displayname": new_displayname, + "avatar_url": current_presence_state.avatar_url, + } + + await presence_handler.set_state(target_user, requester.device_id, state) + async def get_avatar_url(self, target_user: UserID) -> Optional[str]: if self.hs.is_mine(target_user): try: @@ -295,6 +308,19 @@ async def set_avatar_url( if propagate: await self._update_join_states(requester, target_user) + if self.hs.config.server.track_presence: + presence_handler = self.hs.get_presence_handler() + current_presence_state = await presence_handler.get_state(target_user) + + state = { + "presence": current_presence_state.state, + "status_message": current_presence_state.status_msg, + "displayname": current_presence_state.displayname, + "avatar_url": new_avatar_url, + } + + await presence_handler.set_state(target_user, requester.device_id, state) + @cached() async def check_avatar_size_and_mime_type(self, mxc: str) -> bool: """Check that the size and content type of the avatar at the given MXC URI are diff --git a/synapse/replication/tcp/streams/_base.py b/synapse/replication/tcp/streams/_base.py index d021904de72..0c0e3689fa7 100644 --- a/synapse/replication/tcp/streams/_base.py +++ b/synapse/replication/tcp/streams/_base.py @@ -330,6 +330,8 @@ class PresenceStreamRow: last_user_sync_ts: int status_msg: str currently_active: bool + displayname: str + avatar_url: str NAME = "presence" ROW_TYPE = PresenceStreamRow diff --git a/synapse/storage/databases/main/presence.py b/synapse/storage/databases/main/presence.py index 065c8856036..aa7d10b3f70 100644 --- a/synapse/storage/databases/main/presence.py +++ b/synapse/storage/databases/main/presence.py @@ -171,6 +171,8 @@ def _update_presence_txn( "last_user_sync_ts", "status_msg", "currently_active", + "displayname", + "avatar_url", "instance_name", ), values=[ @@ -183,6 +185,8 @@ def _update_presence_txn( state.last_user_sync_ts, state.status_msg, state.currently_active, + state.displayname, + state.avatar_url, self._instance_name, ) for stream_id, state in zip(stream_orderings, presence_states) @@ -222,7 +226,8 @@ def get_all_presence_updates_txn( sql = """ SELECT stream_id, user_id, state, last_active_ts, last_federation_update_ts, last_user_sync_ts, - status_msg, currently_active + status_msg, currently_active, displayname, + avatar_url FROM presence_stream WHERE ? < stream_id AND stream_id <= ? ORDER BY stream_id ASC @@ -275,6 +280,8 @@ async def get_presence_for_users( "last_user_sync_ts", "status_msg", "currently_active", + "displayname", + "avatar_url", ), desc="get_presence_for_users", ), @@ -289,8 +296,10 @@ async def get_presence_for_users( last_user_sync_ts=last_user_sync_ts, status_msg=status_msg, currently_active=bool(currently_active), + displayname=displayname, + avatar_url=avatar_url, ) - for user_id, state, last_active_ts, last_federation_update_ts, last_user_sync_ts, status_msg, currently_active in rows + for user_id, state, last_active_ts, last_federation_update_ts, last_user_sync_ts, status_msg, currently_active, displayname, avatar_url, in rows } async def should_user_receive_full_presence_with_token( @@ -417,6 +426,8 @@ async def get_presence_for_all_users( "last_user_sync_ts", "status_msg", "currently_active", + "displayname", + "avatar_url", ), order_direction="ASC", ), @@ -430,6 +441,8 @@ async def get_presence_for_all_users( last_user_sync_ts, status_msg, currently_active, + displayname, + avatar_url, ) in rows: users_to_state[user_id] = UserPresenceState( user_id=user_id, @@ -439,6 +452,8 @@ async def get_presence_for_all_users( last_user_sync_ts=last_user_sync_ts, status_msg=status_msg, currently_active=bool(currently_active), + displayname=displayname, + avatar_url=avatar_url, ) # We've run out of updates to query @@ -464,7 +479,8 @@ def _get_active_presence(self, db_conn: Connection) -> List[UserPresenceState]: # query. sql = ( "SELECT user_id, state, last_active_ts, last_federation_update_ts," - " last_user_sync_ts, status_msg, currently_active FROM presence_stream" + " last_user_sync_ts, status_msg, currently_active, displayname, avatar_url " + " FROM presence_stream" " WHERE state != ?" ) @@ -482,8 +498,10 @@ def _get_active_presence(self, db_conn: Connection) -> List[UserPresenceState]: last_user_sync_ts=last_user_sync_ts, status_msg=status_msg, currently_active=bool(currently_active), + displayname=displayname, + avatar_url=avatar_url, ) - for user_id, state, last_active_ts, last_federation_update_ts, last_user_sync_ts, status_msg, currently_active in rows + for user_id, state, last_active_ts, last_federation_update_ts, last_user_sync_ts, status_msg, currently_active, displayname, avatar_url, in rows ] def take_presence_startup_info(self) -> List[UserPresenceState]: diff --git a/synapse/storage/schema/__init__.py b/synapse/storage/schema/__init__.py index 581d00346bf..8b3bb04665b 100644 --- a/synapse/storage/schema/__init__.py +++ b/synapse/storage/schema/__init__.py @@ -19,7 +19,7 @@ # # -SCHEMA_VERSION = 86 # remember to update the list below when updating +SCHEMA_VERSION = 87 # remember to update the list below when updating """Represents the expectations made by the codebase about the database schema This should be incremented whenever the codebase changes its requirements on the @@ -142,6 +142,9 @@ Changes in SCHEMA_VERSION = 86 - Add a column `authenticated` to the tables `local_media_repository` and `remote_media_cache` + +Changes in SCHEMA_VERSION = 87 + - Added displayname and avatar_url columns to presence_stream """ diff --git a/synapse/storage/schema/main/delta/87/01_presence_stream_updates.sql b/synapse/storage/schema/main/delta/87/01_presence_stream_updates.sql new file mode 100644 index 00000000000..54a32ba3a84 --- /dev/null +++ b/synapse/storage/schema/main/delta/87/01_presence_stream_updates.sql @@ -0,0 +1,20 @@ +-- +-- This file is licensed under the Affero General Public License (AGPL) version 3. +-- +-- Copyright (C) 2023 New Vector, Ltd +-- +-- +-- This file is licensed under the Affero General Public License (AGPL) version 3. +-- +-- Copyright (C) 2024 New Vector, Ltd +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU Affero General Public License as +-- published by the Free Software Foundation, either version 3 of the +-- License, or (at your option) any later version. +-- +-- See the GNU Affero General Public License for more details: +-- . + +ALTER TABLE presence_stream ADD COLUMN displayname TEXT; +ALTER TABLE presence_stream ADD COLUMN avatar_url TEXT; diff --git a/tests/api/test_filtering.py b/tests/api/test_filtering.py index 743c52d969c..00516530269 100644 --- a/tests/api/test_filtering.py +++ b/tests/api/test_filtering.py @@ -450,6 +450,8 @@ def test_filter_presence_match(self) -> None: last_user_sync_ts=0, status_msg=None, currently_active=False, + displayname=None, + avatar_url=None, ), ] @@ -478,6 +480,8 @@ def test_filter_presence_no_match(self) -> None: last_user_sync_ts=0, status_msg=None, currently_active=False, + displayname=None, + avatar_url=None, ), ] diff --git a/tests/handlers/test_presence.py b/tests/handlers/test_presence.py index cc630d606ca..ae99bda15e2 100644 --- a/tests/handlers/test_presence.py +++ b/tests/handlers/test_presence.py @@ -366,6 +366,8 @@ def test_persisting_presence_updates(self) -> None: last_user_sync_ts=1, status_msg="I'm online!", currently_active=True, + displayname=None, + avatar_url=None, ) presence_states.append(presence_state) @@ -718,6 +720,8 @@ def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None: last_user_sync_ts=now, status_msg=None, currently_active=True, + displayname=None, + avatar_url=None, ) ] ) From 0ff515d8b3c62b567ecdea1aa2a8eaab8d87bff5 Mon Sep 17 00:00:00 2001 From: Michael Hollister Date: Wed, 1 May 2024 13:37:47 -0500 Subject: [PATCH 2/8] Added missing type hints to DB queries --- synapse/storage/databases/main/presence.py | 28 ++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/synapse/storage/databases/main/presence.py b/synapse/storage/databases/main/presence.py index aa7d10b3f70..3a3aa5d891c 100644 --- a/synapse/storage/databases/main/presence.py +++ b/synapse/storage/databases/main/presence.py @@ -266,7 +266,19 @@ async def get_presence_for_users( # TODO All these columns are nullable, but we don't expect that: # https://github.com/matrix-org/synapse/issues/16467 rows = cast( - List[Tuple[str, str, int, int, int, Optional[str], Union[int, bool]]], + List[ + Tuple[ + str, + str, + int, + int, + int, + Optional[str], + Union[int, bool], + Optional[str], + Optional[str], + ] + ], await self.db_pool.simple_select_many_batch( table="presence_stream", column="user_id", @@ -409,7 +421,19 @@ async def get_presence_for_all_users( # TODO All these columns are nullable, but we don't expect that: # https://github.com/matrix-org/synapse/issues/16467 rows = cast( - List[Tuple[str, str, int, int, int, Optional[str], Union[int, bool]]], + List[ + Tuple[ + str, + str, + int, + int, + int, + Optional[str], + Union[int, bool], + Optional[str], + Optional[str], + ] + ], await self.db_pool.runInteraction( "get_presence_for_all_users", self.db_pool.simple_select_list_paginate_txn, From f1e6483d3e0e2ca47b5d2da9cd227426ccee9934 Mon Sep 17 00:00:00 2001 From: Michael Hollister Date: Wed, 1 May 2024 14:20:17 -0500 Subject: [PATCH 3/8] Added documentation for configuring new presence tracking options --- .../configuration/config_documentation.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/usage/configuration/config_documentation.md b/docs/usage/configuration/config_documentation.md index 40f64be8561..2419ddc2d89 100644 --- a/docs/usage/configuration/config_documentation.md +++ b/docs/usage/configuration/config_documentation.md @@ -247,6 +247,8 @@ Example configuration: presence: enabled: false include_offline_users_on_sync: false + sync_presence_tracking: true + federation_presence_tracking: true ``` `enabled` can also be set to a special value of "untracked" which ignores updates @@ -259,6 +261,18 @@ When clients perform an initial or `full_state` sync, presence results for offli not included by default. Setting `include_offline_users_on_sync` to `true` will always include offline users in the results. Defaults to false. +Enabling presence tracking can be resource intensive for the presence handler when server-side +tracking of user activity is enabled. Below are some additional configuration options if you +require additional performance: +* `sync_presence_tracking` (Default enabled): Determines if the server tracks a user's presence +activity when syncing. If disabled, the server will not automatically update the user's presence +activity when the sync endpoint is called. Note that client applications can still update their +presence by calling the respective presence endpoints. +* `federation_presence_tracking` (Default enabled): Determines if the server will accept +presence EDUs that only contain presence activity updates. If disabled, the server will drop +processing EDUs that do not contain updates to the `status_msg`, `displayname`, and +`avatar_url` fields. + --- ### `require_auth_for_profile_requests` @@ -1765,7 +1779,7 @@ rc_3pid_validation: This option sets ratelimiting how often invites can be sent in a room or to a specific user. `per_room` defaults to `per_second: 0.3`, `burst_count: 10`, -`per_user` defaults to `per_second: 0.003`, `burst_count: 5`, and `per_issuer` +`per_user` defaults to `per_second: 0.003`, `burst_count: 5`, and `per_issuer` defaults to `per_second: 0.3`, `burst_count: 10`. Client requests that invite user(s) when [creating a @@ -1966,7 +1980,7 @@ max_image_pixels: 35M --- ### `remote_media_download_burst_count` -Remote media downloads are ratelimited using a [leaky bucket algorithm](https://en.wikipedia.org/wiki/Leaky_bucket), where a given "bucket" is keyed to the IP address of the requester when requesting remote media downloads. This configuration option sets the size of the bucket against which the size in bytes of downloads are penalized - if the bucket is full, ie a given number of bytes have already been downloaded, further downloads will be denied until the bucket drains. Defaults to 500MiB. See also `remote_media_download_per_second` which determines the rate at which the "bucket" is emptied and thus has available space to authorize new requests. +Remote media downloads are ratelimited using a [leaky bucket algorithm](https://en.wikipedia.org/wiki/Leaky_bucket), where a given "bucket" is keyed to the IP address of the requester when requesting remote media downloads. This configuration option sets the size of the bucket against which the size in bytes of downloads are penalized - if the bucket is full, ie a given number of bytes have already been downloaded, further downloads will be denied until the bucket drains. Defaults to 500MiB. See also `remote_media_download_per_second` which determines the rate at which the "bucket" is emptied and thus has available space to authorize new requests. Example configuration: ```yaml From 7b734766a57d9f98737ceb2f1aa8932ea1037a9c Mon Sep 17 00:00:00 2001 From: Michael Hollister Date: Wed, 1 May 2024 14:22:28 -0500 Subject: [PATCH 4/8] Removed duplicate license comment block --- .../schema/main/delta/87/01_presence_stream_updates.sql | 5 ----- 1 file changed, 5 deletions(-) diff --git a/synapse/storage/schema/main/delta/87/01_presence_stream_updates.sql b/synapse/storage/schema/main/delta/87/01_presence_stream_updates.sql index 54a32ba3a84..046fef3193d 100644 --- a/synapse/storage/schema/main/delta/87/01_presence_stream_updates.sql +++ b/synapse/storage/schema/main/delta/87/01_presence_stream_updates.sql @@ -1,11 +1,6 @@ -- -- This file is licensed under the Affero General Public License (AGPL) version 3. -- --- Copyright (C) 2023 New Vector, Ltd --- --- --- This file is licensed under the Affero General Public License (AGPL) version 3. --- -- Copyright (C) 2024 New Vector, Ltd -- -- This program is free software: you can redistribute it and/or modify From e5aac2aafa60ce039257db5ff86adcf8494028d5 Mon Sep 17 00:00:00 2001 From: Michael Hollister Date: Thu, 30 May 2024 15:24:30 -0500 Subject: [PATCH 5/8] Improved wording of presence tracking documentation Co-authored-by: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> --- docs/usage/configuration/config_documentation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/usage/configuration/config_documentation.md b/docs/usage/configuration/config_documentation.md index 2419ddc2d89..f687c165345 100644 --- a/docs/usage/configuration/config_documentation.md +++ b/docs/usage/configuration/config_documentation.md @@ -262,15 +262,15 @@ not included by default. Setting `include_offline_users_on_sync` to `true` will offline users in the results. Defaults to false. Enabling presence tracking can be resource intensive for the presence handler when server-side -tracking of user activity is enabled. Below are some additional configuration options if you -require additional performance: +tracking of user activity is enabled. Below are some additional configuration options which may +help improve the performance of the presence feature without outright disabling it: * `sync_presence_tracking` (Default enabled): Determines if the server tracks a user's presence activity when syncing. If disabled, the server will not automatically update the user's presence activity when the sync endpoint is called. Note that client applications can still update their presence by calling the respective presence endpoints. * `federation_presence_tracking` (Default enabled): Determines if the server will accept presence EDUs that only contain presence activity updates. If disabled, the server will drop -processing EDUs that do not contain updates to the `status_msg`, `displayname`, and +processing EDUs that do not contain updates to the `status_msg`, `displayname`, or `avatar_url` fields. --- From d6d113eaa43f4c9c79cf3dd4fcaef1c3c28c5c0c Mon Sep 17 00:00:00 2001 From: Michael Hollister Date: Thu, 30 May 2024 22:45:38 -0500 Subject: [PATCH 6/8] Improved wording of documentation and config option naming --- .../configuration/config_documentation.md | 23 +++++++++++-------- synapse/config/server.py | 8 +++---- synapse/federation/federation_server.py | 12 +++++++++- synapse/handlers/presence.py | 8 ++++--- 4 files changed, 33 insertions(+), 18 deletions(-) diff --git a/docs/usage/configuration/config_documentation.md b/docs/usage/configuration/config_documentation.md index f687c165345..ffcc8d58206 100644 --- a/docs/usage/configuration/config_documentation.md +++ b/docs/usage/configuration/config_documentation.md @@ -247,8 +247,8 @@ Example configuration: presence: enabled: false include_offline_users_on_sync: false - sync_presence_tracking: true - federation_presence_tracking: true + local_activity_tracking: true + remote_activity_tracking: true ``` `enabled` can also be set to a special value of "untracked" which ignores updates @@ -264,14 +264,17 @@ offline users in the results. Defaults to false. Enabling presence tracking can be resource intensive for the presence handler when server-side tracking of user activity is enabled. Below are some additional configuration options which may help improve the performance of the presence feature without outright disabling it: -* `sync_presence_tracking` (Default enabled): Determines if the server tracks a user's presence -activity when syncing. If disabled, the server will not automatically update the user's presence -activity when the sync endpoint is called. Note that client applications can still update their -presence by calling the respective presence endpoints. -* `federation_presence_tracking` (Default enabled): Determines if the server will accept -presence EDUs that only contain presence activity updates. If disabled, the server will drop -processing EDUs that do not contain updates to the `status_msg`, `displayname`, or -`avatar_url` fields. +* `local_activity_tracking` (Default enabled): Determines if the server tracks a user's activity +when syncing or fetching events. If disabled, the server will not automatically update the +user's presence activity when the /sync or /events endpoints are called. Note that client +applications can still update their presence by calling the presence /status endpoint. +* `remote_activity_tracking` (Default enabled): Determines if the server will accept presence +EDUs from remote servers that are exclusively user activity updates. If disabled, the server +will reject processing these EDUs. However if a presence EDU contains profile updates to any of +the `status_msg`, `displayname`, or `avatar_url` fields, then the server will accept the EDU. + +If the presence `enabled` field is set "untracked", then these options will both act as if set +to false. --- ### `require_auth_for_profile_requests` diff --git a/synapse/config/server.py b/synapse/config/server.py index b7791456c2f..b565951ddb1 100644 --- a/synapse/config/server.py +++ b/synapse/config/server.py @@ -390,13 +390,13 @@ def read_config(self, config: JsonDict, **kwargs: Any) -> None: ) # Disabling server-side presence tracking - self.sync_presence_tracking = presence_config.get( - "sync_presence_tracking", True + self.presence_local_activity_tracking = presence_config.get( + "local_activity_tracking", True ) # Disabling federation presence tracking - self.federation_presence_tracking = presence_config.get( - "federation_presence_tracking", True + self.presence_remote_activity_tracking = presence_config.get( + "remote_activity_tracking", True ) # Custom presence router module diff --git a/synapse/federation/federation_server.py b/synapse/federation/federation_server.py index 0460a45168c..674a4a94326 100644 --- a/synapse/federation/federation_server.py +++ b/synapse/federation/federation_server.py @@ -1425,11 +1425,21 @@ def register_instances_for_edu( self._edu_type_to_instance[edu_type] = instance_names async def on_edu(self, edu_type: str, origin: str, content: dict) -> None: + """Passes an EDU to a registered handler if one exists + + This potentially modifies the `content` dict for `m.presence` EDUs when + presence `remote_activity_tracking` is disabled. + + Args: + edu_type: The type of the incoming EDU to process + origin: The server we received the event from + content: The content of the EDU + """ if not self.config.server.track_presence and edu_type == EduTypes.PRESENCE: return if ( - not self.config.server.federation_presence_tracking + not self.config.server.presence_remote_activity_tracking and edu_type == EduTypes.PRESENCE ): filtered_edus = [] diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py index 6ea4cf78677..00ce79ed604 100644 --- a/synapse/handlers/presence.py +++ b/synapse/handlers/presence.py @@ -201,7 +201,9 @@ def __init__(self, hs: "HomeServer"): self._presence_enabled = hs.config.server.presence_enabled self._track_presence = hs.config.server.track_presence - self._sync_presence_tracking = hs.config.server.sync_presence_tracking + self._presence_local_activity_tracking = ( + hs.config.server.presence_local_activity_tracking + ) self._federation = None if hs.should_send_federation(): @@ -585,7 +587,7 @@ async def user_syncing( if ( not affect_presence or not self._track_presence - or not self._sync_presence_tracking + or not self._presence_local_activity_tracking ): return _NullContextManager() @@ -1152,7 +1154,7 @@ async def user_syncing( if ( not affect_presence or not self._track_presence - or not self._sync_presence_tracking + or not self._presence_local_activity_tracking ): return _NullContextManager() From e06395e37755cfd68a39d2fefb39c6b0810350e4 Mon Sep 17 00:00:00 2001 From: Michael Hollister Date: Thu, 30 May 2024 22:50:59 -0500 Subject: [PATCH 7/8] Updated changelog description --- changelog.d/16992.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/16992.feature b/changelog.d/16992.feature index 903916be7e5..6b2cc834846 100644 --- a/changelog.d/16992.feature +++ b/changelog.d/16992.feature @@ -1 +1 @@ -Added presence update on change of profile information and config flags for selective presence tracking. Contributed by @Michael-Hollister. +Added presence tracking of user profile updates and config flags for disabling user activity tracking. Contributed by @Michael-Hollister. From 2428eb8684ee8e4b341a058382213dbb56175871 Mon Sep 17 00:00:00 2001 From: Michael Hollister Date: Thu, 30 May 2024 22:59:32 -0500 Subject: [PATCH 8/8] Docs grammar fix --- docs/usage/configuration/config_documentation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/usage/configuration/config_documentation.md b/docs/usage/configuration/config_documentation.md index ffcc8d58206..3cc5f0bf8f3 100644 --- a/docs/usage/configuration/config_documentation.md +++ b/docs/usage/configuration/config_documentation.md @@ -273,8 +273,8 @@ EDUs from remote servers that are exclusively user activity updates. If disabled will reject processing these EDUs. However if a presence EDU contains profile updates to any of the `status_msg`, `displayname`, or `avatar_url` fields, then the server will accept the EDU. -If the presence `enabled` field is set "untracked", then these options will both act as if set -to false. +If the presence `enabled` field is set to "untracked", then these options will both act as if +set to false. --- ### `require_auth_for_profile_requests`