summary refs log tree commit diff
diff options
context:
space:
mode:
authorDavid Baker <dave@matrix.org>2016-04-25 18:27:04 +0100
committerDavid Baker <dave@matrix.org>2016-04-25 18:27:04 +0100
commit7b4715bad704231b51c6d0462cfd19ed32df5e0b (patch)
tree3421315cc11a46b64a757514a638cff2ebb8127a
parentTypo (diff)
downloadsynapse-7b4715bad704231b51c6d0462cfd19ed32df5e0b.tar.xz
More variable calculation for email notifs
Include name of the person we're sending to and add summary text at the top giving an overview of what's happened.
-rw-r--r--res/templates/notif.html3
-rw-r--r--synapse/push/mailer.py57
-rw-r--r--synapse/util/presentable_names.py (renamed from synapse/util/room_name.py)35
3 files changed, 76 insertions, 19 deletions
diff --git a/res/templates/notif.html b/res/templates/notif.html
index 648ff034b3..aee52ec8c9 100644
--- a/res/templates/notif.html
+++ b/res/templates/notif.html
@@ -1,7 +1,8 @@
 <!doctype html>
 <html lang="en">
   <body>
-    <h1>{{ summaryText }}</h1>
+    <div className="salutation">Hi {{ user_display_name }},</div>
+    <div className="summarytext">{{ summary_text }}</div>
     <div class="content">
         {% for room in rooms %}
             {% include 'room.html' with context %}
diff --git a/synapse/push/mailer.py b/synapse/push/mailer.py
index 9212d36b84..9e2297a03b 100644
--- a/synapse/push/mailer.py
+++ b/synapse/push/mailer.py
@@ -21,11 +21,19 @@ import email.mime.multipart
 from email.mime.text import MIMEText
 
 from synapse.util.async import concurrently_execute
-from synapse.util.room_name import calculate_room_name
+from synapse.util.presentable_names import calculate_room_name, name_from_member_event
+from synapse.types import UserID
+from synapse.api.errors import StoreError
 
 import jinja2
 
 
+MESSAGE_FROM_PERSON_IN_ROOM = "You have a message from %s in the %s room"
+MESSAGE_FROM_PERSON = "You have a message from %s"
+MESSAGES_IN_ROOM = "There are some messages for you in the %s room"
+MESSAGES_IN_ROOMS = "Here are some messages you may have missed"
+
+
 class Mailer(object):
     def __init__(self, hs):
         self.hs = hs
@@ -55,6 +63,13 @@ class Mailer(object):
         # notifications
         state_by_room = {}
 
+        try:
+            user_display_name = yield self.store.get_profile_displayname(
+                UserID.from_string(user_id).localpart
+            )
+        except StoreError:
+            user_display_name = user_id
+
         @defer.inlineCallbacks
         def _fetch_room_state(room_id):
             room_state = yield self.state_handler.get_current_state(room_id)
@@ -70,8 +85,14 @@ class Mailer(object):
             ) for r in rooms_in_order
         ]
 
+        summary_text = yield self.make_summary_text(
+            notifs_by_room, state_by_room, user_id
+        )
+
         template_vars = {
+            "user_display_name": user_display_name,
             "unsubscribe_link": self.make_unsubscribe_link(),
+            "summary_text": summary_text,
             "rooms": rooms,
         }
 
@@ -93,6 +114,38 @@ class Mailer(object):
         room_vars['title'] = calculate_room_name(room_state, user_id)
         return room_vars
 
+    @defer.inlineCallbacks
+    def make_summary_text(self, notifs_by_room, state_by_room, user_id):
+        if len(notifs_by_room) == 1:
+            room_id = notifs_by_room.keys()[0]
+            sender_name = None
+            if len(notifs_by_room[room_id]) == 1:
+                # If the room has some kind of name, use it, but we don't
+                # want the generated-from-names one here otherwise we'll
+                # end up with, "new message from Bob in the Bob room"
+                room_name = calculate_room_name(
+                    state_by_room[room_id], user_id, fallback_to_members=False
+                )
+                event = yield self.store.get_event(
+                    notifs_by_room[room_id][0]["event_id"]
+                )
+                if ("m.room.member", event.sender) in state_by_room[room_id]:
+                    state_event = state_by_room[room_id][("m.room.member", event.sender)]
+                    sender_name = name_from_member_event(state_event)
+                if sender_name is not None and room_name is not None:
+                    defer.returnValue(
+                        MESSAGE_FROM_PERSON_IN_ROOM % (sender_name, room_name)
+                    )
+                elif sender_name is not None:
+                    defer.returnValue(MESSAGE_FROM_PERSON % (sender_name,))
+            else:
+                room_name = calculate_room_name(state_by_room[room_id], user_id)
+                defer.returnValue(MESSAGES_IN_ROOM % (room_name,))
+        else:
+            defer.returnValue(MESSAGES_IN_ROOMS)
+
+        defer.returnValue("Some thing have occurred in some rooms")
+
     def make_unsubscribe_link(self):
         return "https://vector.im/#/settings"  # XXX: matrix.to
 
@@ -104,4 +157,4 @@ def deduped_ordered_list(l):
         if item not in seen:
             seen.add(item)
             ret.append(item)
-    return ret
\ No newline at end of file
+    return ret
diff --git a/synapse/util/room_name.py b/synapse/util/presentable_names.py
index f55ef293b6..2ae01e453d 100644
--- a/synapse/util/room_name.py
+++ b/synapse/util/presentable_names.py
@@ -22,7 +22,7 @@ ALIAS_RE = re.compile(r"^#.*:.+$")
 ALL_ALONE = "Empty Room"
 
 
-def calculate_room_name(room_state, user_id):
+def calculate_room_name(room_state, user_id, fallback_to_members=True):
     # does it have a name?
     if ("m.room.name", "") in room_state:
         m_room_name = room_state[("m.room.name", "")]
@@ -34,13 +34,13 @@ def calculate_room_name(room_state, user_id):
         canon_alias = room_state[("m.room.canonical_alias", "")]
         if (
             canon_alias.content and canon_alias.content["alias"] and
-            looks_like_an_alias(canon_alias.content["alias"])
+            _looks_like_an_alias(canon_alias.content["alias"])
         ):
             return canon_alias.content["alias"]
 
     # at this point we're going to need to search the state by all state keys
     # for an event type, so rearrange the data structure
-    room_state_bytype = state_as_two_level_dict(room_state)
+    room_state_bytype = _state_as_two_level_dict(room_state)
 
     # right then, any aliases at all?
     if "m.room.aliases" in room_state_bytype:
@@ -49,7 +49,7 @@ def calculate_room_name(room_state, user_id):
             first_alias_event = m_room_aliases.values()[0]
             if first_alias_event.content and first_alias_event.content["aliases"]:
                 the_aliases = first_alias_event.content["aliases"]
-                if len(the_aliases) > 0 and looks_like_an_alias(the_aliases[0]):
+                if len(the_aliases) > 0 and _looks_like_an_alias(the_aliases[0]):
                     return the_aliases[0]
 
     my_member_event = None
@@ -66,6 +66,9 @@ def calculate_room_name(room_state, user_id):
         else:
             return "Room Invite"
 
+    if not fallback_to_members:
+        return None
+
     # we're going to have to generate a name based on who's in the room,
     # so find out who is in the room that isn't the user.
     if "m.room.member" in room_state_bytype:
@@ -105,17 +108,6 @@ def calculate_room_name(room_state, user_id):
         return descriptor_from_member_events(other_members)
 
 
-def state_as_two_level_dict(state):
-    ret = {}
-    for k, v in state.items():
-        ret.setdefault(k[0], {})[k[1]] = v
-    return ret
-
-
-def looks_like_an_alias(string):
-    return ALIAS_RE.match(string) is not None
-
-
 def descriptor_from_member_events(member_events):
     if len(member_events) == 0:
         return "nobody"
@@ -139,4 +131,15 @@ def name_from_member_event(member_event):
         member_event.content["displayname"]
     ):
         return member_event.content["displayname"]
-    return member_event.state_key
\ No newline at end of file
+    return member_event.state_key
+
+
+def _state_as_two_level_dict(state):
+    ret = {}
+    for k, v in state.items():
+        ret.setdefault(k[0], {})[k[1]] = v
+    return ret
+
+
+def _looks_like_an_alias(string):
+    return ALIAS_RE.match(string) is not None
\ No newline at end of file