@@ -151,7 +151,7 @@ def __init__(self, hs: "HomeServer"):
151
151
152
152
@abc .abstractmethod
153
153
async def user_syncing (
154
- self , user_id : str , affect_presence : bool
154
+ self , user_id : str , affect_presence : bool , presence_state : str
155
155
) -> ContextManager [None ]:
156
156
"""Returns a context manager that should surround any stream requests
157
157
from the user.
@@ -165,6 +165,7 @@ async def user_syncing(
165
165
affect_presence: If false this function will be a no-op.
166
166
Useful for streams that are not associated with an actual
167
167
client that is being used by a user.
168
+ presence_state: The presence state indicated in the sync request
168
169
"""
169
170
170
171
@abc .abstractmethod
@@ -228,6 +229,11 @@ async def current_state_for_users(
228
229
229
230
return states
230
231
232
+ async def current_state_for_user (self , user_id : str ) -> UserPresenceState :
233
+ """Get the current presence state for a user."""
234
+ res = await self .current_state_for_users ([user_id ])
235
+ return res [user_id ]
236
+
231
237
@abc .abstractmethod
232
238
async def set_state (
233
239
self ,
@@ -461,7 +467,7 @@ def send_stop_syncing(self) -> None:
461
467
self .send_user_sync (user_id , False , last_sync_ms )
462
468
463
469
async def user_syncing (
464
- self , user_id : str , affect_presence : bool
470
+ self , user_id : str , affect_presence : bool , presence_state : str
465
471
) -> ContextManager [None ]:
466
472
"""Record that a user is syncing.
467
473
@@ -471,6 +477,17 @@ async def user_syncing(
471
477
if not affect_presence or not self ._presence_enabled :
472
478
return _NullContextManager ()
473
479
480
+ prev_state = await self .current_state_for_user (user_id )
481
+ if prev_state != PresenceState .BUSY :
482
+ # We set state here but pass ignore_status_msg = True as we don't want to
483
+ # cause the status message to be cleared.
484
+ # Note that this causes last_active_ts to be incremented which is not
485
+ # what the spec wants: see comment in the BasePresenceHandler version
486
+ # of this function.
487
+ await self .set_state (
488
+ UserID .from_string (user_id ), {"presence" : presence_state }, True
489
+ )
490
+
474
491
curr_sync = self ._user_to_num_current_syncs .get (user_id , 0 )
475
492
self ._user_to_num_current_syncs [user_id ] = curr_sync + 1
476
493
@@ -942,7 +959,10 @@ async def bump_presence_active_time(self, user: UserID) -> None:
942
959
await self ._update_states ([prev_state .copy_and_replace (** new_fields )])
943
960
944
961
async def user_syncing (
945
- self , user_id : str , affect_presence : bool = True
962
+ self ,
963
+ user_id : str ,
964
+ affect_presence : bool = True ,
965
+ presence_state : str = PresenceState .ONLINE ,
946
966
) -> ContextManager [None ]:
947
967
"""Returns a context manager that should surround any stream requests
948
968
from the user.
@@ -956,6 +976,7 @@ async def user_syncing(
956
976
affect_presence: If false this function will be a no-op.
957
977
Useful for streams that are not associated with an actual
958
978
client that is being used by a user.
979
+ presence_state: The presence state indicated in the sync request
959
980
"""
960
981
# Override if it should affect the user's presence, if presence is
961
982
# disabled.
@@ -967,9 +988,25 @@ async def user_syncing(
967
988
self .user_to_num_current_syncs [user_id ] = curr_sync + 1
968
989
969
990
prev_state = await self .current_state_for_user (user_id )
991
+
992
+ # If they're busy then they don't stop being busy just by syncing,
993
+ # so just update the last sync time.
994
+ if prev_state .state != PresenceState .BUSY :
995
+ # XXX: We set_state separately here and just update the last_active_ts above
996
+ # This keeps the logic as similar as possible between the worker and single
997
+ # process modes. Using set_state will actually cause last_active_ts to be
998
+ # updated always, which is not what the spec calls for, but synapse has done
999
+ # this for... forever, I think.
1000
+ await self .set_state (
1001
+ UserID .from_string (user_id ), {"presence" : presence_state }, True
1002
+ )
1003
+ # Retrieve the new state for the logic below. This should come from the
1004
+ # in-memory cache.
1005
+ prev_state = await self .current_state_for_user (user_id )
1006
+
1007
+ # To keep the single process behaviour consistent with worker mode, run the
1008
+ # same logic as `update_external_syncs_row`, even though it looks weird.
970
1009
if prev_state .state == PresenceState .OFFLINE :
971
- # If they're currently offline then bring them online, otherwise
972
- # just update the last sync times.
973
1010
await self ._update_states (
974
1011
[
975
1012
prev_state .copy_and_replace (
@@ -979,6 +1016,10 @@ async def user_syncing(
979
1016
)
980
1017
]
981
1018
)
1019
+ # otherwise, set the new presence state & update the last sync time,
1020
+ # but don't update last_active_ts as this isn't an indication that
1021
+ # they've been active (even though it's probably been updated by
1022
+ # set_state above)
982
1023
else :
983
1024
await self ._update_states (
984
1025
[
@@ -1086,11 +1127,6 @@ async def update_external_syncs_clear(self, process_id: str) -> None:
1086
1127
)
1087
1128
self .external_process_last_updated_ms .pop (process_id , None )
1088
1129
1089
- async def current_state_for_user (self , user_id : str ) -> UserPresenceState :
1090
- """Get the current presence state for a user."""
1091
- res = await self .current_state_for_users ([user_id ])
1092
- return res [user_id ]
1093
-
1094
1130
async def _persist_and_notify (self , states : List [UserPresenceState ]) -> None :
1095
1131
"""Persist states in the database, poke the notifier and send to
1096
1132
interested remote servers
0 commit comments