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.pull/759/head
parent
290f125a13
commit
7b4715bad7
|
@ -1,7 +1,8 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<body>
|
<body>
|
||||||
<h1>{{ summaryText }}</h1>
|
<div className="salutation">Hi {{ user_display_name }},</div>
|
||||||
|
<div className="summarytext">{{ summary_text }}</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
{% for room in rooms %}
|
{% for room in rooms %}
|
||||||
{% include 'room.html' with context %}
|
{% include 'room.html' with context %}
|
||||||
|
|
|
@ -21,11 +21,19 @@ import email.mime.multipart
|
||||||
from email.mime.text import MIMEText
|
from email.mime.text import MIMEText
|
||||||
|
|
||||||
from synapse.util.async import concurrently_execute
|
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
|
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):
|
class Mailer(object):
|
||||||
def __init__(self, hs):
|
def __init__(self, hs):
|
||||||
self.hs = hs
|
self.hs = hs
|
||||||
|
@ -55,6 +63,13 @@ class Mailer(object):
|
||||||
# notifications
|
# notifications
|
||||||
state_by_room = {}
|
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
|
@defer.inlineCallbacks
|
||||||
def _fetch_room_state(room_id):
|
def _fetch_room_state(room_id):
|
||||||
room_state = yield self.state_handler.get_current_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
|
) for r in rooms_in_order
|
||||||
]
|
]
|
||||||
|
|
||||||
|
summary_text = yield self.make_summary_text(
|
||||||
|
notifs_by_room, state_by_room, user_id
|
||||||
|
)
|
||||||
|
|
||||||
template_vars = {
|
template_vars = {
|
||||||
|
"user_display_name": user_display_name,
|
||||||
"unsubscribe_link": self.make_unsubscribe_link(),
|
"unsubscribe_link": self.make_unsubscribe_link(),
|
||||||
|
"summary_text": summary_text,
|
||||||
"rooms": rooms,
|
"rooms": rooms,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,6 +114,38 @@ class Mailer(object):
|
||||||
room_vars['title'] = calculate_room_name(room_state, user_id)
|
room_vars['title'] = calculate_room_name(room_state, user_id)
|
||||||
return room_vars
|
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):
|
def make_unsubscribe_link(self):
|
||||||
return "https://vector.im/#/settings" # XXX: matrix.to
|
return "https://vector.im/#/settings" # XXX: matrix.to
|
||||||
|
|
||||||
|
@ -104,4 +157,4 @@ def deduped_ordered_list(l):
|
||||||
if item not in seen:
|
if item not in seen:
|
||||||
seen.add(item)
|
seen.add(item)
|
||||||
ret.append(item)
|
ret.append(item)
|
||||||
return ret
|
return ret
|
||||||
|
|
|
@ -22,7 +22,7 @@ ALIAS_RE = re.compile(r"^#.*:.+$")
|
||||||
ALL_ALONE = "Empty Room"
|
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?
|
# does it have a name?
|
||||||
if ("m.room.name", "") in room_state:
|
if ("m.room.name", "") in room_state:
|
||||||
m_room_name = room_state[("m.room.name", "")]
|
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", "")]
|
canon_alias = room_state[("m.room.canonical_alias", "")]
|
||||||
if (
|
if (
|
||||||
canon_alias.content and canon_alias.content["alias"] and
|
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"]
|
return canon_alias.content["alias"]
|
||||||
|
|
||||||
# at this point we're going to need to search the state by all state keys
|
# 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
|
# 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?
|
# right then, any aliases at all?
|
||||||
if "m.room.aliases" in room_state_bytype:
|
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]
|
first_alias_event = m_room_aliases.values()[0]
|
||||||
if first_alias_event.content and first_alias_event.content["aliases"]:
|
if first_alias_event.content and first_alias_event.content["aliases"]:
|
||||||
the_aliases = 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]
|
return the_aliases[0]
|
||||||
|
|
||||||
my_member_event = None
|
my_member_event = None
|
||||||
|
@ -66,6 +66,9 @@ def calculate_room_name(room_state, user_id):
|
||||||
else:
|
else:
|
||||||
return "Room Invite"
|
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,
|
# 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.
|
# so find out who is in the room that isn't the user.
|
||||||
if "m.room.member" in room_state_bytype:
|
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)
|
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):
|
def descriptor_from_member_events(member_events):
|
||||||
if len(member_events) == 0:
|
if len(member_events) == 0:
|
||||||
return "nobody"
|
return "nobody"
|
||||||
|
@ -139,4 +131,15 @@ def name_from_member_event(member_event):
|
||||||
member_event.content["displayname"]
|
member_event.content["displayname"]
|
||||||
):
|
):
|
||||||
return member_event.content["displayname"]
|
return member_event.content["displayname"]
|
||||||
return member_event.state_key
|
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
|
Loading…
Reference in New Issue