summary refs log tree commit diff
diff options
context:
space:
mode:
authorBrendan Abolivier <babolivier@matrix.org>2021-03-18 16:34:47 +0100
committerBrendan Abolivier <babolivier@matrix.org>2021-03-18 16:34:47 +0100
commit405aeb0b2c40443d22ce8c265df18e81bd995b44 (patch)
tree34dc1487f416df1a73d1e0d25be0a703dfa0ae96
parentEnsure we use a copy of the event content dict before modifying it in seriali... (diff)
downloadsynapse-405aeb0b2c40443d22ce8c265df18e81bd995b44.tar.xz
Implement MSC3026: busy presence state
-rw-r--r--changelog.d/9644.feature1
-rw-r--r--synapse/api/constants.py1
-rw-r--r--synapse/app/generic_worker.py1
-rw-r--r--synapse/handlers/presence.py3
-rw-r--r--synapse/rest/client/versions.py2
-rw-r--r--tests/handlers/test_presence.py20
6 files changed, 27 insertions, 1 deletions
diff --git a/changelog.d/9644.feature b/changelog.d/9644.feature
new file mode 100644
index 0000000000..556bcf0f9f
--- /dev/null
+++ b/changelog.d/9644.feature
@@ -0,0 +1 @@
+Implement the busy presence state as described in [MSC3026](https://github.com/matrix-org/matrix-doc/pull/3026).
diff --git a/synapse/api/constants.py b/synapse/api/constants.py
index 691f8f9adf..cc8541bc16 100644
--- a/synapse/api/constants.py
+++ b/synapse/api/constants.py
@@ -51,6 +51,7 @@ class PresenceState:
     OFFLINE = "offline"
     UNAVAILABLE = "unavailable"
     ONLINE = "online"
+    BUSY = "org.matrix.msc3026.busy"
 
 
 class JoinRules:
diff --git a/synapse/app/generic_worker.py b/synapse/app/generic_worker.py
index 274d582d07..236d98a29d 100644
--- a/synapse/app/generic_worker.py
+++ b/synapse/app/generic_worker.py
@@ -439,6 +439,7 @@ class GenericWorkerPresence(BasePresenceHandler):
             PresenceState.ONLINE,
             PresenceState.UNAVAILABLE,
             PresenceState.OFFLINE,
+            PresenceState.BUSY,
         )
         if presence not in valid_presence:
             raise SynapseError(400, "Invalid presence state")
diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py
index 54631b4ee2..bcb99f627b 100644
--- a/synapse/handlers/presence.py
+++ b/synapse/handlers/presence.py
@@ -730,6 +730,7 @@ class PresenceHandler(BasePresenceHandler):
             PresenceState.ONLINE,
             PresenceState.UNAVAILABLE,
             PresenceState.OFFLINE,
+            PresenceState.BUSY,
         )
         if presence not in valid_presence:
             raise SynapseError(400, "Invalid presence state")
@@ -744,7 +745,7 @@ class PresenceHandler(BasePresenceHandler):
             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:
             new_fields["last_active_ts"] = self.clock.time_msec()
 
         await self._update_states([prev_state.copy_and_replace(**new_fields)])
diff --git a/synapse/rest/client/versions.py b/synapse/rest/client/versions.py
index d24a199318..f387d29b57 100644
--- a/synapse/rest/client/versions.py
+++ b/synapse/rest/client/versions.py
@@ -81,6 +81,8 @@ class VersionsRestServlet(RestServlet):
                     "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": True,
                 },
             },
         )
diff --git a/tests/handlers/test_presence.py b/tests/handlers/test_presence.py
index 996c614198..77330f59a9 100644
--- a/tests/handlers/test_presence.py
+++ b/tests/handlers/test_presence.py
@@ -310,6 +310,26 @@ class PresenceTimeoutTestCase(unittest.TestCase):
         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