summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--synapse/handlers/presence.py5
-rw-r--r--synapse/push/__init__.py19
-rw-r--r--synapse/push/httppusher.py38
-rw-r--r--synapse/push/pusherpool.py17
4 files changed, 76 insertions, 3 deletions
diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py
index 8aeed99274..24d901b51f 100644
--- a/synapse/handlers/presence.py
+++ b/synapse/handlers/presence.py
@@ -86,6 +86,10 @@ class PresenceHandler(BaseHandler):
             "changed_presencelike_data", self.changed_presencelike_data
         )
 
+        # outbound signal from the presence module to advertise when a user's
+        # presence has changed
+        distributor.declare("user_presence_changed")
+
         self.distributor = distributor
 
         self.federation = hs.get_replication_layer()
@@ -603,6 +607,7 @@ class PresenceHandler(BaseHandler):
             room_ids=room_ids,
             statuscache=statuscache,
         )
+        yield self.distributor.fire("user_presence_changed", user, statuscache)
 
     @defer.inlineCallbacks
     def _push_presence_remote(self, user, destination, state=None):
diff --git a/synapse/push/__init__.py b/synapse/push/__init__.py
index b6d01a82a0..4862d0de27 100644
--- a/synapse/push/__init__.py
+++ b/synapse/push/__init__.py
@@ -54,6 +54,9 @@ class Pusher(object):
         self.failing_since = failing_since
         self.alive = True
 
+        # The last value of last_active_time that we saw
+        self.last_last_active_time = 0
+
     @defer.inlineCallbacks
     def _actions_for_event(self, ev):
         """
@@ -273,6 +276,22 @@ class Pusher(object):
         """
         pass
 
+    def reset_badge_count(self):
+        pass
+
+    def presence_changed(self, state):
+        """
+        We clear badge counts whenever a user's last_active time is bumped
+        This is by no means perfect but I think it's the best we can do
+        without read receipts.
+        """
+        if 'last_active' in state.state:
+            last_active = state.state['last_active']
+            if last_active > self.last_last_active_time:
+                logger.info("Resetting badge count for %s", self.user_name)
+                self.reset_badge_count()
+                self.last_last_active_time = last_active
+
 
 def _value_for_dotted_key(dotted_key, event):
     parts = dotted_key.split(".")
diff --git a/synapse/push/httppusher.py b/synapse/push/httppusher.py
index 22532fcc6a..d592bc2fd2 100644
--- a/synapse/push/httppusher.py
+++ b/synapse/push/httppusher.py
@@ -71,11 +71,12 @@ class HttpPusher(Pusher):
                 # we may have to fetch this over federation and we
                 # can't trust it anyway: is it worth it?
                 #'from_display_name': 'Steve Stevington'
-                #'counts': { -- we don't mark messages as read yet so
+                'counts': { #-- we don't mark messages as read yet so
                 # we have no way of knowing
-                #    'unread': 1,
+                    # Just set the badge to 1 until we have read receipts
+                    'unread': 1,
                 #    'missed_calls': 2
-                # },
+                },
                 'devices': [
                     {
                         'app_id': self.app_id,
@@ -111,3 +112,34 @@ class HttpPusher(Pusher):
         if 'rejected' in resp:
             rejected = resp['rejected']
         defer.returnValue(rejected)
+
+    @defer.inlineCallbacks
+    def reset_badge_count(self):
+        d = {
+            'notification': {
+                'id': '',
+                'type': None,
+                'from': '',
+                'counts': {
+                    'unread': 0,
+                    'missed_calls': 0
+                },
+                'devices': [
+                    {
+                        'app_id': self.app_id,
+                        'pushkey': self.pushkey,
+                        'pushkey_ts': long(self.pushkey_ts / 1000),
+                        'data': self.data_minus_url,
+                    }
+                ]
+            }
+        }
+        try:
+            resp = yield self.httpCli.post_json_get_json(self.url, d)
+        except:
+            logger.exception("Failed to push %s ", self.url)
+            defer.returnValue(False)
+        rejected = []
+        if 'rejected' in resp:
+            rejected = resp['rejected']
+        defer.returnValue(rejected)
\ No newline at end of file
diff --git a/synapse/push/pusherpool.py b/synapse/push/pusherpool.py
index 2dfecf178b..65ab4f46e1 100644
--- a/synapse/push/pusherpool.py
+++ b/synapse/push/pusherpool.py
@@ -18,6 +18,7 @@ from twisted.internet import defer
 
 from httppusher import HttpPusher
 from synapse.push import PusherConfigException
+from synapse.api.constants import PresenceState
 
 import logging
 import json
@@ -32,6 +33,22 @@ class PusherPool:
         self.pushers = {}
         self.last_pusher_started = -1
 
+        distributor = self.hs.get_distributor()
+        distributor.observe(
+            "user_presence_changed", self.user_presence_changed
+        )
+
+    @defer.inlineCallbacks
+    def user_presence_changed(self, user, state):
+        user_name = user.to_string()
+
+        # until we have read receipts, pushers use this to reset a user's
+        # badge counters to zero
+        for p in self.pushers.values():
+            if p.user_name == user_name:
+                yield p.presence_changed(state)
+
+
     @defer.inlineCallbacks
     def start(self):
         pushers = yield self.store.get_all_pushers()