summary refs log tree commit diff
path: root/synapse/push
diff options
context:
space:
mode:
authorDavid Baker <dave@matrix.org>2016-04-21 19:19:07 +0100
committerDavid Baker <dave@matrix.org>2016-04-21 19:19:07 +0100
commitc10ed26c303741fe0e43f11e2fbeeb148f466b17 (patch)
treebdd406d8db391cf0f2182a65666971d39953f3e0 /synapse/push
parentGenerate mails from a template (diff)
downloadsynapse-c10ed26c303741fe0e43f11e2fbeeb148f466b17.tar.xz
Flesh out email templating
Mostly WIP porting the room name calculation logic from the web client so our room names in the email mirror the clients.
Diffstat (limited to 'synapse/push')
-rw-r--r--synapse/push/emailpusher.py7
-rw-r--r--synapse/push/mailer.py61
2 files changed, 65 insertions, 3 deletions
diff --git a/synapse/push/emailpusher.py b/synapse/push/emailpusher.py
index 4e21221fb7..7c810029fa 100644
--- a/synapse/push/emailpusher.py
+++ b/synapse/push/emailpusher.py
@@ -83,6 +83,13 @@ class EmailPusher(object):
         yield self._process()
 
     @defer.inlineCallbacks
+    def on_new_receipts(self, min_stream_id, max_stream_id):
+        # We could wake up and cancel the timer but there tend to be quite a
+        # lot of read receipts so it's probably less work to just let the
+        # timer fire
+        return defer.succeed(None)
+
+    @defer.inlineCallbacks
     def on_timer(self):
         self.timed_call = None
         yield self._process()
diff --git a/synapse/push/mailer.py b/synapse/push/mailer.py
index 0f20d43f75..e68d701ffd 100644
--- a/synapse/push/mailer.py
+++ b/synapse/push/mailer.py
@@ -14,18 +14,23 @@
 # limitations under the License.
 
 from twisted.internet import defer
-
 from twisted.mail.smtp import sendmail
+
 import email.utils
 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
+
 import jinja2
 
 
 class Mailer(object):
     def __init__(self, hs):
         self.hs = hs
+        self.store = self.hs.get_datastore()
+        self.state_handler = self.hs.get_state_handler()
         loader = jinja2.FileSystemLoader(self.hs.config.email_template_dir)
         env = jinja2.Environment(loader=loader)
         self.notif_template = env.get_template(self.hs.config.email_notif_template_html)
@@ -38,9 +43,41 @@ class Mailer(object):
         if raw_to == '':
             raise RuntimeError("Invalid 'to' address")
 
-        plainText = self.notif_template.render()
+        rooms_in_order = deduped_ordered_list(
+            [pa['room_id'] for pa in push_actions]
+        )
+
+        notifs_by_room = {}
+        for pa in push_actions:
+            notifs_by_room.setdefault(pa["room_id"], []).append(pa)
+
+        # collect the current state for all the rooms in which we have
+        # notifications
+        state_by_room = {}
+
+        @defer.inlineCallbacks
+        def _fetch_room_state(room_id):
+            room_state = yield self.state_handler.get_current_state(room_id)
+            state_by_room[room_id] = room_state
+
+        # Run at most 3 of these at once: sync does 10 at a time but email
+        # notifs are much realtime than sync so we can afford to wait a bit.
+        yield concurrently_execute(_fetch_room_state, rooms_in_order, 3)
 
-        text_part = MIMEText(plainText, "plain")
+        rooms = [
+            self.get_room_vars(
+                r, user_id, notifs_by_room[r], state_by_room[r]
+            ) for r in rooms_in_order
+        ]
+
+        template_vars = {
+            "unsubscribe_link": self.make_unsubscribe_link(),
+            "rooms": rooms,
+        }
+
+        plainText = self.notif_template.render(**template_vars)
+
+        text_part = MIMEText(plainText, "html")
         text_part['Subject'] = "New Matrix Notifications"
         text_part['From'] = self.hs.config.email_notif_from
         text_part['To'] = email_address
@@ -50,3 +87,21 @@ class Mailer(object):
             raw_from, raw_to, text_part.as_string(),
             port=self.hs.config.email_smtp_port
         )
+
+    def get_room_vars(self, room_id, user_id, notifs, room_state):
+        room_vars = {}
+        room_vars['title'] = calculate_room_name(room_state, user_id)
+        return room_vars
+
+    def make_unsubscribe_link(self):
+        return "https://vector.im/#/settings"  # XXX: matrix.to
+
+
+def deduped_ordered_list(l):
+    seen = set()
+    ret = []
+    for item in l:
+        if item not in seen:
+            seen.add(item)
+            ret.append(item)
+    return ret
\ No newline at end of file