forked from MirrorHub/synapse
Allow email subjects to be customised through Synapse's configuration (#7846)
This commit is contained in:
parent
491f0dab1b
commit
85223106f3
4 changed files with 202 additions and 39 deletions
1
changelog.d/7846.feature
Normal file
1
changelog.d/7846.feature
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Allow email subjects to be customised through Synapse's configuration.
|
|
@ -1949,8 +1949,8 @@ email:
|
||||||
#
|
#
|
||||||
#notif_from: "Your Friendly %(app)s homeserver <noreply@example.com>"
|
#notif_from: "Your Friendly %(app)s homeserver <noreply@example.com>"
|
||||||
|
|
||||||
# app_name defines the default value for '%(app)s' in notif_from. It
|
# app_name defines the default value for '%(app)s' in notif_from and email
|
||||||
# defaults to 'Matrix'.
|
# subjects. It defaults to 'Matrix'.
|
||||||
#
|
#
|
||||||
#app_name: my_branded_matrix_server
|
#app_name: my_branded_matrix_server
|
||||||
|
|
||||||
|
@ -2019,6 +2019,73 @@ email:
|
||||||
#
|
#
|
||||||
#template_dir: "res/templates"
|
#template_dir: "res/templates"
|
||||||
|
|
||||||
|
# Subjects to use when sending emails from Synapse.
|
||||||
|
#
|
||||||
|
# The placeholder '%(app)s' will be replaced with the value of the 'app_name'
|
||||||
|
# setting above, or by a value dictated by the Matrix client application.
|
||||||
|
#
|
||||||
|
# If a subject isn't overridden in this configuration file, the value used as
|
||||||
|
# its example will be used.
|
||||||
|
#
|
||||||
|
#subjects:
|
||||||
|
|
||||||
|
# Subjects for notification emails.
|
||||||
|
#
|
||||||
|
# On top of the '%(app)s' placeholder, these can use the following
|
||||||
|
# placeholders:
|
||||||
|
#
|
||||||
|
# * '%(person)s', which will be replaced by the display name of the user(s)
|
||||||
|
# that sent the message(s), e.g. "Alice and Bob".
|
||||||
|
# * '%(room)s', which will be replaced by the name of the room the
|
||||||
|
# message(s) have been sent to, e.g. "My super room".
|
||||||
|
#
|
||||||
|
# See the example provided for each setting to see which placeholder can be
|
||||||
|
# used and how to use them.
|
||||||
|
#
|
||||||
|
# Subject to use to notify about one message from one or more user(s) in a
|
||||||
|
# room which has a name.
|
||||||
|
#message_from_person_in_room: "[%(app)s] You have a message on %(app)s from %(person)s in the %(room)s room..."
|
||||||
|
#
|
||||||
|
# Subject to use to notify about one message from one or more user(s) in a
|
||||||
|
# room which doesn't have a name.
|
||||||
|
#message_from_person: "[%(app)s] You have a message on %(app)s from %(person)s..."
|
||||||
|
#
|
||||||
|
# Subject to use to notify about multiple messages from one or more users in
|
||||||
|
# a room which doesn't have a name.
|
||||||
|
#messages_from_person: "[%(app)s] You have messages on %(app)s from %(person)s..."
|
||||||
|
#
|
||||||
|
# Subject to use to notify about multiple messages in a room which has a
|
||||||
|
# name.
|
||||||
|
#messages_in_room: "[%(app)s] You have messages on %(app)s in the %(room)s room..."
|
||||||
|
#
|
||||||
|
# Subject to use to notify about multiple messages in multiple rooms.
|
||||||
|
#messages_in_room_and_others: "[%(app)s] You have messages on %(app)s in the %(room)s room and others..."
|
||||||
|
#
|
||||||
|
# Subject to use to notify about multiple messages from multiple persons in
|
||||||
|
# multiple rooms. This is similar to the setting above except it's used when
|
||||||
|
# the room in which the notification was triggered has no name.
|
||||||
|
#messages_from_person_and_others: "[%(app)s] You have messages on %(app)s from %(person)s and others..."
|
||||||
|
#
|
||||||
|
# Subject to use to notify about an invite to a room which has a name.
|
||||||
|
#invite_from_person_to_room: "[%(app)s] %(person)s has invited you to join the %(room)s room on %(app)s..."
|
||||||
|
#
|
||||||
|
# Subject to use to notify about an invite to a room which doesn't have a
|
||||||
|
# name.
|
||||||
|
#invite_from_person: "[%(app)s] %(person)s has invited you to chat on %(app)s..."
|
||||||
|
|
||||||
|
# Subject for emails related to account administration.
|
||||||
|
#
|
||||||
|
# On top of the '%(app)s' placeholder, these one can use the
|
||||||
|
# '%(server_name)s' placeholder, which will be replaced by the value of the
|
||||||
|
# 'server_name' setting in your Synapse configuration.
|
||||||
|
#
|
||||||
|
# Subject to use when sending a password reset email.
|
||||||
|
#password_reset: "[%(server_name)s] Password reset"
|
||||||
|
#
|
||||||
|
# Subject to use when sending a verification email to assert an address's
|
||||||
|
# ownership.
|
||||||
|
#email_validation: "[%(server_name)s] Validate your email"
|
||||||
|
|
||||||
|
|
||||||
# Password providers allow homeserver administrators to integrate
|
# Password providers allow homeserver administrators to integrate
|
||||||
# their Synapse installation with existing authentication methods
|
# their Synapse installation with existing authentication methods
|
||||||
|
|
|
@ -22,6 +22,7 @@ import os
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
import attr
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
|
|
||||||
from ._base import Config, ConfigError
|
from ._base import Config, ConfigError
|
||||||
|
@ -32,6 +33,33 @@ Password reset emails are enabled on this homeserver due to a partial
|
||||||
%s
|
%s
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
DEFAULT_SUBJECTS = {
|
||||||
|
"message_from_person_in_room": "[%(app)s] You have a message on %(app)s from %(person)s in the %(room)s room...",
|
||||||
|
"message_from_person": "[%(app)s] You have a message on %(app)s from %(person)s...",
|
||||||
|
"messages_from_person": "[%(app)s] You have messages on %(app)s from %(person)s...",
|
||||||
|
"messages_in_room": "[%(app)s] You have messages on %(app)s in the %(room)s room...",
|
||||||
|
"messages_in_room_and_others": "[%(app)s] You have messages on %(app)s in the %(room)s room and others...",
|
||||||
|
"messages_from_person_and_others": "[%(app)s] You have messages on %(app)s from %(person)s and others...",
|
||||||
|
"invite_from_person": "[%(app)s] %(person)s has invited you to chat on %(app)s...",
|
||||||
|
"invite_from_person_to_room": "[%(app)s] %(person)s has invited you to join the %(room)s room on %(app)s...",
|
||||||
|
"password_reset": "[%(server_name)s] Password reset",
|
||||||
|
"email_validation": "[%(server_name)s] Validate your email",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s
|
||||||
|
class EmailSubjectConfig:
|
||||||
|
message_from_person_in_room = attr.ib(type=str)
|
||||||
|
message_from_person = attr.ib(type=str)
|
||||||
|
messages_from_person = attr.ib(type=str)
|
||||||
|
messages_in_room = attr.ib(type=str)
|
||||||
|
messages_in_room_and_others = attr.ib(type=str)
|
||||||
|
messages_from_person_and_others = attr.ib(type=str)
|
||||||
|
invite_from_person = attr.ib(type=str)
|
||||||
|
invite_from_person_to_room = attr.ib(type=str)
|
||||||
|
password_reset = attr.ib(type=str)
|
||||||
|
email_validation = attr.ib(type=str)
|
||||||
|
|
||||||
|
|
||||||
class EmailConfig(Config):
|
class EmailConfig(Config):
|
||||||
section = "email"
|
section = "email"
|
||||||
|
@ -294,8 +322,17 @@ class EmailConfig(Config):
|
||||||
if not os.path.isfile(p):
|
if not os.path.isfile(p):
|
||||||
raise ConfigError("Unable to find email template file %s" % (p,))
|
raise ConfigError("Unable to find email template file %s" % (p,))
|
||||||
|
|
||||||
|
subjects_config = email_config.get("subjects", {})
|
||||||
|
subjects = {}
|
||||||
|
|
||||||
|
for key, default in DEFAULT_SUBJECTS.items():
|
||||||
|
subjects[key] = subjects_config.get(key, default)
|
||||||
|
|
||||||
|
self.email_subjects = EmailSubjectConfig(**subjects)
|
||||||
|
|
||||||
def generate_config_section(self, config_dir_path, server_name, **kwargs):
|
def generate_config_section(self, config_dir_path, server_name, **kwargs):
|
||||||
return """\
|
return (
|
||||||
|
"""\
|
||||||
# Configuration for sending emails from Synapse.
|
# Configuration for sending emails from Synapse.
|
||||||
#
|
#
|
||||||
email:
|
email:
|
||||||
|
@ -323,17 +360,17 @@ class EmailConfig(Config):
|
||||||
# notif_from defines the "From" address to use when sending emails.
|
# notif_from defines the "From" address to use when sending emails.
|
||||||
# It must be set if email sending is enabled.
|
# It must be set if email sending is enabled.
|
||||||
#
|
#
|
||||||
# The placeholder '%(app)s' will be replaced by the application name,
|
# The placeholder '%%(app)s' will be replaced by the application name,
|
||||||
# which is normally 'app_name' (below), but may be overridden by the
|
# which is normally 'app_name' (below), but may be overridden by the
|
||||||
# Matrix client application.
|
# Matrix client application.
|
||||||
#
|
#
|
||||||
# Note that the placeholder must be written '%(app)s', including the
|
# Note that the placeholder must be written '%%(app)s', including the
|
||||||
# trailing 's'.
|
# trailing 's'.
|
||||||
#
|
#
|
||||||
#notif_from: "Your Friendly %(app)s homeserver <noreply@example.com>"
|
#notif_from: "Your Friendly %%(app)s homeserver <noreply@example.com>"
|
||||||
|
|
||||||
# app_name defines the default value for '%(app)s' in notif_from. It
|
# app_name defines the default value for '%%(app)s' in notif_from and email
|
||||||
# defaults to 'Matrix'.
|
# subjects. It defaults to 'Matrix'.
|
||||||
#
|
#
|
||||||
#app_name: my_branded_matrix_server
|
#app_name: my_branded_matrix_server
|
||||||
|
|
||||||
|
@ -401,7 +438,76 @@ class EmailConfig(Config):
|
||||||
# https://github.com/matrix-org/synapse/tree/master/synapse/res/templates
|
# https://github.com/matrix-org/synapse/tree/master/synapse/res/templates
|
||||||
#
|
#
|
||||||
#template_dir: "res/templates"
|
#template_dir: "res/templates"
|
||||||
|
|
||||||
|
# Subjects to use when sending emails from Synapse.
|
||||||
|
#
|
||||||
|
# The placeholder '%%(app)s' will be replaced with the value of the 'app_name'
|
||||||
|
# setting above, or by a value dictated by the Matrix client application.
|
||||||
|
#
|
||||||
|
# If a subject isn't overridden in this configuration file, the value used as
|
||||||
|
# its example will be used.
|
||||||
|
#
|
||||||
|
#subjects:
|
||||||
|
|
||||||
|
# Subjects for notification emails.
|
||||||
|
#
|
||||||
|
# On top of the '%%(app)s' placeholder, these can use the following
|
||||||
|
# placeholders:
|
||||||
|
#
|
||||||
|
# * '%%(person)s', which will be replaced by the display name of the user(s)
|
||||||
|
# that sent the message(s), e.g. "Alice and Bob".
|
||||||
|
# * '%%(room)s', which will be replaced by the name of the room the
|
||||||
|
# message(s) have been sent to, e.g. "My super room".
|
||||||
|
#
|
||||||
|
# See the example provided for each setting to see which placeholder can be
|
||||||
|
# used and how to use them.
|
||||||
|
#
|
||||||
|
# Subject to use to notify about one message from one or more user(s) in a
|
||||||
|
# room which has a name.
|
||||||
|
#message_from_person_in_room: "%(message_from_person_in_room)s"
|
||||||
|
#
|
||||||
|
# Subject to use to notify about one message from one or more user(s) in a
|
||||||
|
# room which doesn't have a name.
|
||||||
|
#message_from_person: "%(message_from_person)s"
|
||||||
|
#
|
||||||
|
# Subject to use to notify about multiple messages from one or more users in
|
||||||
|
# a room which doesn't have a name.
|
||||||
|
#messages_from_person: "%(messages_from_person)s"
|
||||||
|
#
|
||||||
|
# Subject to use to notify about multiple messages in a room which has a
|
||||||
|
# name.
|
||||||
|
#messages_in_room: "%(messages_in_room)s"
|
||||||
|
#
|
||||||
|
# Subject to use to notify about multiple messages in multiple rooms.
|
||||||
|
#messages_in_room_and_others: "%(messages_in_room_and_others)s"
|
||||||
|
#
|
||||||
|
# Subject to use to notify about multiple messages from multiple persons in
|
||||||
|
# multiple rooms. This is similar to the setting above except it's used when
|
||||||
|
# the room in which the notification was triggered has no name.
|
||||||
|
#messages_from_person_and_others: "%(messages_from_person_and_others)s"
|
||||||
|
#
|
||||||
|
# Subject to use to notify about an invite to a room which has a name.
|
||||||
|
#invite_from_person_to_room: "%(invite_from_person_to_room)s"
|
||||||
|
#
|
||||||
|
# Subject to use to notify about an invite to a room which doesn't have a
|
||||||
|
# name.
|
||||||
|
#invite_from_person: "%(invite_from_person)s"
|
||||||
|
|
||||||
|
# Subject for emails related to account administration.
|
||||||
|
#
|
||||||
|
# On top of the '%%(app)s' placeholder, these one can use the
|
||||||
|
# '%%(server_name)s' placeholder, which will be replaced by the value of the
|
||||||
|
# 'server_name' setting in your Synapse configuration.
|
||||||
|
#
|
||||||
|
# Subject to use when sending a password reset email.
|
||||||
|
#password_reset: "%(password_reset)s"
|
||||||
|
#
|
||||||
|
# Subject to use when sending a verification email to assert an address's
|
||||||
|
# ownership.
|
||||||
|
#email_validation: "%(email_validation)s"
|
||||||
"""
|
"""
|
||||||
|
% DEFAULT_SUBJECTS
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ThreepidBehaviour(Enum):
|
class ThreepidBehaviour(Enum):
|
||||||
|
|
|
@ -27,6 +27,7 @@ import jinja2
|
||||||
|
|
||||||
from synapse.api.constants import EventTypes
|
from synapse.api.constants import EventTypes
|
||||||
from synapse.api.errors import StoreError
|
from synapse.api.errors import StoreError
|
||||||
|
from synapse.config.emailconfig import EmailSubjectConfig
|
||||||
from synapse.logging.context import make_deferred_yieldable
|
from synapse.logging.context import make_deferred_yieldable
|
||||||
from synapse.push.presentable_names import (
|
from synapse.push.presentable_names import (
|
||||||
calculate_room_name,
|
calculate_room_name,
|
||||||
|
@ -42,23 +43,6 @@ logger = logging.getLogger(__name__)
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
MESSAGE_FROM_PERSON_IN_ROOM = (
|
|
||||||
"You have a message on %(app)s from %(person)s in the %(room)s room..."
|
|
||||||
)
|
|
||||||
MESSAGE_FROM_PERSON = "You have a message on %(app)s from %(person)s..."
|
|
||||||
MESSAGES_FROM_PERSON = "You have messages on %(app)s from %(person)s..."
|
|
||||||
MESSAGES_IN_ROOM = "You have messages on %(app)s in the %(room)s room..."
|
|
||||||
MESSAGES_IN_ROOM_AND_OTHERS = (
|
|
||||||
"You have messages on %(app)s in the %(room)s room and others..."
|
|
||||||
)
|
|
||||||
MESSAGES_FROM_PERSON_AND_OTHERS = (
|
|
||||||
"You have messages on %(app)s from %(person)s and others..."
|
|
||||||
)
|
|
||||||
INVITE_FROM_PERSON_TO_ROOM = (
|
|
||||||
"%(person)s has invited you to join the %(room)s room on %(app)s..."
|
|
||||||
)
|
|
||||||
INVITE_FROM_PERSON = "%(person)s has invited you to chat on %(app)s..."
|
|
||||||
|
|
||||||
CONTEXT_BEFORE = 1
|
CONTEXT_BEFORE = 1
|
||||||
CONTEXT_AFTER = 1
|
CONTEXT_AFTER = 1
|
||||||
|
|
||||||
|
@ -121,6 +105,7 @@ class Mailer(object):
|
||||||
self.state_handler = self.hs.get_state_handler()
|
self.state_handler = self.hs.get_state_handler()
|
||||||
self.storage = hs.get_storage()
|
self.storage = hs.get_storage()
|
||||||
self.app_name = app_name
|
self.app_name = app_name
|
||||||
|
self.email_subjects = hs.config.email_subjects # type: EmailSubjectConfig
|
||||||
|
|
||||||
logger.info("Created Mailer for app_name %s" % app_name)
|
logger.info("Created Mailer for app_name %s" % app_name)
|
||||||
|
|
||||||
|
@ -147,7 +132,8 @@ class Mailer(object):
|
||||||
|
|
||||||
await self.send_email(
|
await self.send_email(
|
||||||
email_address,
|
email_address,
|
||||||
"[%s] Password Reset" % self.hs.config.server_name,
|
self.email_subjects.password_reset
|
||||||
|
% {"server_name": self.hs.config.server_name},
|
||||||
template_vars,
|
template_vars,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -174,7 +160,8 @@ class Mailer(object):
|
||||||
|
|
||||||
await self.send_email(
|
await self.send_email(
|
||||||
email_address,
|
email_address,
|
||||||
"[%s] Register your Email Address" % self.hs.config.server_name,
|
self.email_subjects.email_validation
|
||||||
|
% {"server_name": self.hs.config.server_name},
|
||||||
template_vars,
|
template_vars,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -202,7 +189,8 @@ class Mailer(object):
|
||||||
|
|
||||||
await self.send_email(
|
await self.send_email(
|
||||||
email_address,
|
email_address,
|
||||||
"[%s] Validate Your Email" % self.hs.config.server_name,
|
self.email_subjects.email_validation
|
||||||
|
% {"server_name": self.hs.config.server_name},
|
||||||
template_vars,
|
template_vars,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -273,9 +261,7 @@ class Mailer(object):
|
||||||
"reason": reason,
|
"reason": reason,
|
||||||
}
|
}
|
||||||
|
|
||||||
await self.send_email(
|
await self.send_email(email_address, summary_text, template_vars)
|
||||||
email_address, "[%s] %s" % (self.app_name, summary_text), template_vars
|
|
||||||
)
|
|
||||||
|
|
||||||
async def send_email(self, email_address, subject, extra_template_vars):
|
async def send_email(self, email_address, subject, extra_template_vars):
|
||||||
"""Send an email with the given information and template text"""
|
"""Send an email with the given information and template text"""
|
||||||
|
@ -482,12 +468,12 @@ class Mailer(object):
|
||||||
inviter_name = name_from_member_event(inviter_member_event)
|
inviter_name = name_from_member_event(inviter_member_event)
|
||||||
|
|
||||||
if room_name is None:
|
if room_name is None:
|
||||||
return INVITE_FROM_PERSON % {
|
return self.email_subjects.invite_from_person % {
|
||||||
"person": inviter_name,
|
"person": inviter_name,
|
||||||
"app": self.app_name,
|
"app": self.app_name,
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
return INVITE_FROM_PERSON_TO_ROOM % {
|
return self.email_subjects.invite_from_person_to_room % {
|
||||||
"person": inviter_name,
|
"person": inviter_name,
|
||||||
"room": room_name,
|
"room": room_name,
|
||||||
"app": self.app_name,
|
"app": self.app_name,
|
||||||
|
@ -505,13 +491,13 @@ class Mailer(object):
|
||||||
sender_name = name_from_member_event(state_event)
|
sender_name = name_from_member_event(state_event)
|
||||||
|
|
||||||
if sender_name is not None and room_name is not None:
|
if sender_name is not None and room_name is not None:
|
||||||
return MESSAGE_FROM_PERSON_IN_ROOM % {
|
return self.email_subjects.message_from_person_in_room % {
|
||||||
"person": sender_name,
|
"person": sender_name,
|
||||||
"room": room_name,
|
"room": room_name,
|
||||||
"app": self.app_name,
|
"app": self.app_name,
|
||||||
}
|
}
|
||||||
elif sender_name is not None:
|
elif sender_name is not None:
|
||||||
return MESSAGE_FROM_PERSON % {
|
return self.email_subjects.message_from_person % {
|
||||||
"person": sender_name,
|
"person": sender_name,
|
||||||
"app": self.app_name,
|
"app": self.app_name,
|
||||||
}
|
}
|
||||||
|
@ -519,7 +505,10 @@ class Mailer(object):
|
||||||
# There's more than one notification for this room, so just
|
# There's more than one notification for this room, so just
|
||||||
# say there are several
|
# say there are several
|
||||||
if room_name is not None:
|
if room_name is not None:
|
||||||
return MESSAGES_IN_ROOM % {"room": room_name, "app": self.app_name}
|
return self.email_subjects.messages_in_room % {
|
||||||
|
"room": room_name,
|
||||||
|
"app": self.app_name,
|
||||||
|
}
|
||||||
else:
|
else:
|
||||||
# If the room doesn't have a name, say who the messages
|
# If the room doesn't have a name, say who the messages
|
||||||
# are from explicitly to avoid, "messages in the Bob room"
|
# are from explicitly to avoid, "messages in the Bob room"
|
||||||
|
@ -537,7 +526,7 @@ class Mailer(object):
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
return MESSAGES_FROM_PERSON % {
|
return self.email_subjects.messages_from_person % {
|
||||||
"person": descriptor_from_member_events(member_events.values()),
|
"person": descriptor_from_member_events(member_events.values()),
|
||||||
"app": self.app_name,
|
"app": self.app_name,
|
||||||
}
|
}
|
||||||
|
@ -546,7 +535,7 @@ class Mailer(object):
|
||||||
|
|
||||||
# ...but we still refer to the 'reason' room which triggered the mail
|
# ...but we still refer to the 'reason' room which triggered the mail
|
||||||
if reason["room_name"] is not None:
|
if reason["room_name"] is not None:
|
||||||
return MESSAGES_IN_ROOM_AND_OTHERS % {
|
return self.email_subjects.messages_in_room_and_others % {
|
||||||
"room": reason["room_name"],
|
"room": reason["room_name"],
|
||||||
"app": self.app_name,
|
"app": self.app_name,
|
||||||
}
|
}
|
||||||
|
@ -566,7 +555,7 @@ class Mailer(object):
|
||||||
[room_state_ids[room_id][("m.room.member", s)] for s in sender_ids]
|
[room_state_ids[room_id][("m.room.member", s)] for s in sender_ids]
|
||||||
)
|
)
|
||||||
|
|
||||||
return MESSAGES_FROM_PERSON_AND_OTHERS % {
|
return self.email_subjects.messages_from_person_and_others % {
|
||||||
"person": descriptor_from_member_events(member_events.values()),
|
"person": descriptor_from_member_events(member_events.values()),
|
||||||
"app": self.app_name,
|
"app": self.app_name,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue