summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--synapse/handlers/presence.py72
-rw-r--r--tests/handlers/test_presence.py29
-rw-r--r--tests/handlers/test_presencelike.py20
-rw-r--r--tests/rest/test_presence.py2
4 files changed, 82 insertions, 41 deletions
diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py
index fa5942ba05..c1af07133f 100644
--- a/synapse/handlers/presence.py
+++ b/synapse/handlers/presence.py
@@ -52,6 +52,13 @@ def partitionbool(l, func):
 
 class PresenceHandler(BaseHandler):
 
+    STATE_LEVELS = {
+        PresenceState.OFFLINE: 0,
+        PresenceState.UNAVAILABLE: 1,
+        PresenceState.ONLINE: 2,
+        PresenceState.FREE_FOR_CHAT: 3,
+    }
+
     def __init__(self, hs):
         super(PresenceHandler, self).__init__(hs)
 
@@ -173,20 +180,24 @@ class PresenceHandler(BaseHandler):
                 observed_user=target_user
             )
 
-            if visible:
-                state = yield self.store.get_presence_state(
-                    target_user.localpart
-                )
-                state["presence"] = state["state"]
-            else:
+            if not visible:
                 raise SynapseError(404, "Presence information not visible")
+            state = yield self.store.get_presence_state(target_user.localpart)
+            if "mtime" in state:
+                del state["mtime"]
+            state["presence"] = state["state"]
+
+            if target_user in self._user_cachemap:
+                state["last_active"] = (
+                    self._user_cachemap[target_user].get_state()["last_active"]
+                )
         else:
             # TODO(paul): Have remote server send us permissions set
             state = self._get_or_offline_usercache(target_user).get_state()
 
-        if "mtime" in state and (state["mtime"] is not None):
-            state["mtime_age"] = int(
-                self.clock.time_msec() - state.pop("mtime")
+        if "last_active" in state:
+            state["last_active_ago"] = int(
+                self.clock.time_msec() - state.pop("last_active")
             )
         defer.returnValue(state)
 
@@ -203,7 +214,6 @@ class PresenceHandler(BaseHandler):
         if target_user != auth_user:
             raise AuthError(400, "Cannot set another user's displayname")
 
-        # TODO(paul): Sanity-check 'state'
         if "status_msg" not in state:
             state["status_msg"] = None
 
@@ -217,12 +227,21 @@ class PresenceHandler(BaseHandler):
         if "state" in state:
             state["presence"] = state.pop("state")
 
+        if state["presence"] not in self.STATE_LEVELS:
+            raise SynapseError(400, "'%s' is not a valid presence state" %
+                state["presence"]
+            )
+
         logger.debug("Updating presence state of %s to %s",
                      target_user.localpart, state["presence"])
 
         state_to_store = dict(state)
         state_to_store["state"] = state_to_store.pop("presence")
 
+        statuscache=self._get_or_offline_usercache(target_user)
+        was_level = self.STATE_LEVELS[statuscache.get_state()["presence"]]
+        now_level = self.STATE_LEVELS[state["presence"]]
+
         yield defer.DeferredList([
             self.store.set_presence_state(
                 target_user.localpart, state_to_store
@@ -232,7 +251,8 @@ class PresenceHandler(BaseHandler):
             ),
         ])
 
-        state["mtime"] = self.clock.time_msec()
+        if now_level > was_level:
+            state["last_active"] = self.clock.time_msec()
 
         now_online = state["presence"] != PresenceState.OFFLINE
         was_polling = target_user in self._user_cachemap
@@ -391,9 +411,9 @@ class PresenceHandler(BaseHandler):
             observed_user = self.hs.parse_userid(p.pop("observed_user_id"))
             p["observed_user"] = observed_user
             p.update(self._get_or_offline_usercache(observed_user).get_state())
-            if "mtime" in p:
-                p["mtime_age"] = int(
-                    self.clock.time_msec() - p.pop("mtime")
+            if "last_active" in p:
+                p["last_active_ago"] = int(
+                    self.clock.time_msec() - p.pop("last_active")
                 )
 
         defer.returnValue(presence)
@@ -582,16 +602,22 @@ class PresenceHandler(BaseHandler):
     def _push_presence_remote(self, user, destination, state=None):
         if state is None:
             state = yield self.store.get_presence_state(user.localpart)
+            del state["mtime"]
             state["presence"] = state["state"]
 
+            if user in self._user_cachemap:
+                state["last_active"] = (
+                    self._user_cachemap[user].get_state()["last_active"]
+                )
+
             yield self.distributor.fire(
                 "collect_presencelike_data", user, state
             )
 
-        if "mtime" in state:
+        if "last_active" in state:
             state = dict(state)
-            state["mtime_age"] = int(
-                self.clock.time_msec() - state.pop("mtime")
+            state["last_active_ago"] = int(
+                self.clock.time_msec() - state.pop("last_active")
             )
 
         user_state = {
@@ -636,9 +662,9 @@ class PresenceHandler(BaseHandler):
                 state["presence"] = state["state"]
             del state["state"]
 
-            if "mtime_age" in state:
-                state["mtime"] = int(
-                    self.clock.time_msec() - state.pop("mtime_age")
+            if "last_active_ago" in state:
+                state["last_active"] = int(
+                    self.clock.time_msec() - state.pop("last_active_ago")
                 )
 
             statuscache = self._get_or_make_usercache(user)
@@ -846,9 +872,9 @@ class UserPresenceCache(object):
         content = self.get_state()
         content["user_id"] = user.to_string()
 
-        if "mtime" in content:
-            content["mtime_age"] = int(
-                clock.time_msec() - content.pop("mtime")
+        if "last_active" in content:
+            content["last_active_ago"] = int(
+                clock.time_msec() - content.pop("last_active")
             )
 
         return {"type": "m.presence", "content": content}
diff --git a/tests/handlers/test_presence.py b/tests/handlers/test_presence.py
index f37d9917fe..844c4e3fb7 100644
--- a/tests/handlers/test_presence.py
+++ b/tests/handlers/test_presence.py
@@ -213,7 +213,7 @@ class PresenceStateTestCase(unittest.TestCase):
                 state={
                     "presence": UNAVAILABLE,
                     "status_msg": "Away",
-                    "mtime": 1000000, # MockClock
+                    "last_active": 1000000, # MockClock
                 })
 
         yield self.handler.set_state(
@@ -621,6 +621,9 @@ class PresencePushTestCase(unittest.TestCase):
 
         # TODO(paul): Gut-wrenching
         self.handler._user_cachemap[self.u_apple] = UserPresenceCache()
+        self.handler._user_cachemap[self.u_apple].update(
+            {"presence": OFFLINE}, serial=0
+        )
         apple_set = self.handler._local_pushmap.setdefault("apple", set())
         apple_set.add(self.u_banana)
         apple_set.add(self.u_clementine)
@@ -640,7 +643,7 @@ class PresencePushTestCase(unittest.TestCase):
                     "user_id": "@apple:test",
                     "presence": ONLINE,
                     "state": ONLINE,
-                    "mtime_age": 0,
+                    "last_active_ago": 0,
                 }},
             ],
         )
@@ -673,7 +676,7 @@ class PresencePushTestCase(unittest.TestCase):
                 {"observed_user": self.u_banana,
                  "presence": ONLINE,
                  "state": ONLINE,
-                 "mtime_age": 2000},
+                 "last_active_ago": 2000},
                 {"observed_user": self.u_clementine,
                  "presence": OFFLINE,
                  "state": OFFLINE},
@@ -690,7 +693,7 @@ class PresencePushTestCase(unittest.TestCase):
                      "user_id": "@banana:test",
                      "presence": ONLINE,
                      "state": ONLINE,
-                     "mtime_age": 2000
+                     "last_active_ago": 2000
                 }},
             ]
         )
@@ -707,7 +710,7 @@ class PresencePushTestCase(unittest.TestCase):
                             {"user_id": "@apple:test",
                              "presence": u"online",
                              "state": u"online",
-                             "mtime_age": 0},
+                             "last_active_ago": 0},
                         ],
                     }
                 )
@@ -723,6 +726,9 @@ class PresencePushTestCase(unittest.TestCase):
 
         # TODO(paul): Gut-wrenching
         self.handler._user_cachemap[self.u_apple] = UserPresenceCache()
+        self.handler._user_cachemap[self.u_apple].update(
+            {"presence": OFFLINE}, serial=0
+        )
         apple_set = self.handler._remote_sendmap.setdefault("apple", set())
         apple_set.add(self.u_potato.domain)
 
@@ -750,7 +756,7 @@ class PresencePushTestCase(unittest.TestCase):
                     "push": [
                         {"user_id": "@potato:remote",
                          "state": "online",
-                         "mtime_age": 1000},
+                         "last_active_ago": 1000},
                     ],
                 }
             )
@@ -767,7 +773,7 @@ class PresencePushTestCase(unittest.TestCase):
                      "user_id": "@potato:remote",
                      "presence": ONLINE,
                      "state": ONLINE,
-                     "mtime_age": 1000,
+                     "last_active_ago": 1000,
                 }}
             ]
         )
@@ -777,7 +783,7 @@ class PresencePushTestCase(unittest.TestCase):
         state = yield self.handler.get_state(self.u_potato, self.u_apple)
 
         self.assertEquals(
-            {"state": ONLINE, "presence": ONLINE, "mtime_age": 3000},
+            {"state": ONLINE, "presence": ONLINE, "last_active_ago": 3000},
             state
         )
 
@@ -792,7 +798,7 @@ class PresencePushTestCase(unittest.TestCase):
         self.handler._user_cachemap[self.u_clementine].update(
             {
                 "presence": PresenceState.ONLINE,
-                "mtime": self.clock.time_msec(),
+                "last_active": self.clock.time_msec(),
             }, self.u_clementine
         )
 
@@ -811,7 +817,7 @@ class PresencePushTestCase(unittest.TestCase):
                      "user_id": "@clementine:test",
                      "presence": ONLINE,
                      "state": ONLINE,
-                     "mtime_age": 0,
+                     "last_active_ago": 0,
                 }}
             ]
         )
@@ -967,7 +973,8 @@ class PresencePollingTestCase(unittest.TestCase):
         def get_presence_state(user_localpart):
             return defer.succeed(
                     {"state": self.current_user_state[user_localpart],
-                     "status_msg": None}
+                     "status_msg": None,
+                     "mtime": 123456000}
             )
         self.datastore.get_presence_state = get_presence_state
 
diff --git a/tests/handlers/test_presencelike.py b/tests/handlers/test_presencelike.py
index 2402bed8d5..38cc34350b 100644
--- a/tests/handlers/test_presencelike.py
+++ b/tests/handlers/test_presencelike.py
@@ -166,7 +166,11 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
         # TODO(paul): Gut-wrenching
         from synapse.handlers.presence import UserPresenceCache
         self.handlers.presence_handler._user_cachemap[self.u_apple] = (
-                UserPresenceCache())
+            UserPresenceCache()
+        )
+        self.handlers.presence_handler._user_cachemap[self.u_apple].update(
+            {"presence": OFFLINE}, serial=0
+        )
         apple_set = self.handlers.presence_handler._local_pushmap.setdefault(
                 "apple", set())
         apple_set.add(self.u_banana)
@@ -184,7 +188,7 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
             {"observed_user": self.u_banana,
                 "presence": ONLINE,
                 "state": ONLINE,
-                "mtime_age": 0,
+                "last_active_ago": 0,
                 "displayname": "Frank",
                 "avatar_url": "http://foo"},
             {"observed_user": self.u_clementine,
@@ -202,7 +206,7 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
         statuscache = self.mock_update_client.call_args[1]["statuscache"]
         self.assertEquals({
             "presence": ONLINE,
-            "mtime": 1000000, # MockClock
+            "last_active": 1000000, # MockClock
             "displayname": "Frank",
             "avatar_url": "http://foo",
         }, statuscache.state)
@@ -225,7 +229,7 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
         statuscache = self.mock_update_client.call_args[1]["statuscache"]
         self.assertEquals({
             "presence": ONLINE,
-            "mtime": 1000000, # MockClock
+            "last_active": 1000000, # MockClock
             "displayname": "I am an Apple",
             "avatar_url": "http://foo",
         }, statuscache.state)
@@ -243,7 +247,11 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
         # TODO(paul): Gut-wrenching
         from synapse.handlers.presence import UserPresenceCache
         self.handlers.presence_handler._user_cachemap[self.u_apple] = (
-                UserPresenceCache())
+            UserPresenceCache()
+        )
+        self.handlers.presence_handler._user_cachemap[self.u_apple].update(
+            {"presence": OFFLINE}, serial=0
+        )
         apple_set = self.handlers.presence_handler._remote_sendmap.setdefault(
                 "apple", set())
         apple_set.add(self.u_potato.domain)
@@ -259,7 +267,7 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
                         {"user_id": "@apple:test",
                          "presence": "online",
                          "state": "online",
-                         "mtime_age": 0,
+                         "last_active_ago": 0,
                          "displayname": "Frank",
                          "avatar_url": "http://foo"},
                     ],
diff --git a/tests/rest/test_presence.py b/tests/rest/test_presence.py
index f5afa4a256..e2cdd80e07 100644
--- a/tests/rest/test_presence.py
+++ b/tests/rest/test_presence.py
@@ -330,6 +330,6 @@ class PresenceEventStreamTestCase(unittest.TestCase):
                  "presence": ONLINE,
                  "state": ONLINE,
                  "displayname": "Frank",
-                 "mtime_age": 0,
+                 "last_active_ago": 0,
             }},
         ]}, response)