Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Implement MSC3026: busy presence state #9644

Merged
merged 5 commits into from
Mar 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/9644.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Implement the busy presence state as described in [MSC3026](https://github.com/matrix-org/matrix-doc/pull/3026).
1 change: 1 addition & 0 deletions synapse/api/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class PresenceState:
OFFLINE = "offline"
UNAVAILABLE = "unavailable"
ONLINE = "online"
BUSY = "org.matrix.msc3026.busy"
babolivier marked this conversation as resolved.
Show resolved Hide resolved


class JoinRules:
Expand Down
8 changes: 7 additions & 1 deletion synapse/app/generic_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,8 @@ def __init__(self, hs):
self.send_stop_syncing, UPDATE_SYNCING_USERS_MS
)

self._busy_presence_enabled = hs.config.experimental.msc3026_enabled

hs.get_reactor().addSystemEventTrigger(
"before",
"shutdown",
Expand Down Expand Up @@ -439,8 +441,12 @@ async def set_state(self, target_user, state, ignore_status_msg=False):
PresenceState.ONLINE,
PresenceState.UNAVAILABLE,
PresenceState.OFFLINE,
PresenceState.BUSY,
)
if presence not in valid_presence:

if presence not in valid_presence or (
presence == PresenceState.BUSY and not self._busy_presence_enabled
):
raise SynapseError(400, "Invalid presence state")

user_id = target_user.to_string()
Expand Down
3 changes: 2 additions & 1 deletion synapse/config/experimental.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def read_config(self, config: JsonDict, **kwargs):

# MSC2858 (multiple SSO identity providers)
self.msc2858_enabled = experimental.get("msc2858_enabled", False) # type: bool

# Spaces (MSC1772, MSC2946, etc)
self.spaces_enabled = experimental.get("spaces_enabled", False) # type: bool
# MSC3026 (busy presence state)
self.msc3026_enabled = experimental.get("msc3026_enabled", False) # type: bool
12 changes: 10 additions & 2 deletions synapse/handlers/presence.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ def __init__(self, hs: "HomeServer"):
self.clock = hs.get_clock()
self.store = hs.get_datastore()

self._busy_presence_enabled = hs.config.experimental.msc3026_enabled

active_presence = self.store.take_presence_startup_info()
self.user_to_current_state = {state.user_id: state for state in active_presence}

Expand Down Expand Up @@ -730,8 +732,12 @@ async def set_state(self, target_user, state, ignore_status_msg=False):
PresenceState.ONLINE,
PresenceState.UNAVAILABLE,
PresenceState.OFFLINE,
PresenceState.BUSY,
)
if presence not in valid_presence:

if presence not in valid_presence or (
presence == PresenceState.BUSY and not self._busy_presence_enabled
):
raise SynapseError(400, "Invalid presence state")

user_id = target_user.to_string()
Expand All @@ -744,7 +750,9 @@ async def set_state(self, target_user, state, ignore_status_msg=False):
msg = status_msg if presence != PresenceState.OFFLINE else None
new_fields["status_msg"] = msg

if presence == PresenceState.ONLINE:
if presence == PresenceState.ONLINE or (
presence == PresenceState.BUSY and self._busy_presence_enabled
):
new_fields["last_active_ts"] = self.clock.time_msec()

await self._update_states([prev_state.copy_and_replace(**new_fields)])
Expand Down
2 changes: 2 additions & 0 deletions synapse/rest/client/versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ def on_GET(self, request):
"io.element.e2ee_forced.public": self.e2ee_forced_public,
"io.element.e2ee_forced.private": self.e2ee_forced_private,
"io.element.e2ee_forced.trusted_private": self.e2ee_forced_trusted_private,
# Supports the busy presence state described in MSC3026.
"org.matrix.msc3026.busy_presence": self.config.experimental.msc3026_enabled,
},
},
)
Expand Down
20 changes: 20 additions & 0 deletions tests/handlers/test_presence.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,26 @@ def test_idle_timer(self):
self.assertIsNotNone(new_state)
self.assertEquals(new_state.state, PresenceState.UNAVAILABLE)

def test_busy_no_idle(self):
"""
Tests that a user setting their presence to busy but idling doesn't turn their
presence state into unavailable.
"""
user_id = "@foo:bar"
now = 5000000

state = UserPresenceState.default(user_id)
state = state.copy_and_replace(
state=PresenceState.BUSY,
last_active_ts=now - IDLE_TIMER - 1,
last_user_sync_ts=now,
)

new_state = handle_timeout(state, is_mine=True, syncing_user_ids=set(), now=now)

self.assertIsNotNone(new_state)
self.assertEquals(new_state.state, PresenceState.BUSY)

def test_sync_timeout(self):
user_id = "@foo:bar"
now = 5000000
Expand Down