Email: add possibility to specify more than one recipient

This commit is contained in:
Benjamin Renard 2023-03-14 16:04:53 +01:00
parent d75a61b4e8
commit c93b3508ed
Signed by: bn8
GPG key ID: 3E2E1CE1907115BC
3 changed files with 32 additions and 20 deletions

View file

@ -177,7 +177,7 @@ class EmailClient(
def forge_message( def forge_message(
self, self,
rcpt_to, recipients,
subject=None, subject=None,
html_body=None, html_body=None,
text_body=None, # pylint: disable=too-many-arguments,too-many-locals text_body=None, # pylint: disable=too-many-arguments,too-many-locals
@ -192,8 +192,8 @@ class EmailClient(
""" """
Forge a message Forge a message
:param rcpt_to: The recipient of the email. Could be a tuple(name, email) or :param recipients: The recipient(s) of the email. List of tuple(name, email) or
just the email of the recipient. just the email of the recipients.
:param subject: The subject of the email. :param subject: The subject of the email.
:param html_body: The HTML body of the email :param html_body: The HTML body of the email
:param text_body: The plain text body of the email :param text_body: The plain text body of the email
@ -206,8 +206,14 @@ class EmailClient(
All other parameters will be consider as template variables. All other parameters will be consider as template variables.
""" """
recipients = [recipients] if not isinstance(recipients, list) else recipients
msg = MIMEMultipart("alternative") msg = MIMEMultipart("alternative")
msg["To"] = email.utils.formataddr(rcpt_to) if isinstance(rcpt_to, tuple) else rcpt_to msg["To"] = ", ".join(
[
email.utils.formataddr(recipient) if isinstance(recipient, tuple) else recipient
for recipient in recipients
]
)
msg["From"] = email.utils.formataddr( msg["From"] = email.utils.formataddr(
( (
sender_name or self._get_option("sender_name"), sender_name or self._get_option("sender_name"),
@ -280,12 +286,12 @@ class EmailClient(
msg.attach(part) msg.attach(part)
return msg return msg
def send(self, rcpt_to, msg=None, subject=None, just_try=False, **forge_args): def send(self, recipients, msg=None, subject=None, just_try=False, **forge_args):
""" """
Send an email Send an email
:param rcpt_to: The recipient of the email. Could be a tuple(name, email) :param recipients: The recipient(s) of the email. List of tuple(name, email) or
or just the email of the recipient. just the email of the recipients.
:param msg: The message of this email (as MIMEBase or derivated classes) :param msg: The message of this email (as MIMEBase or derivated classes)
:param subject: The subject of the email (only if the message is not provided :param subject: The subject of the email (only if the message is not provided
using msg parameter) using msg parameter)
@ -295,20 +301,21 @@ class EmailClient(
All other parameters will be consider as parameters to forge the message All other parameters will be consider as parameters to forge the message
(only if the message is not provided using msg parameter). (only if the message is not provided using msg parameter).
""" """
msg = msg if msg else self.forge_message(rcpt_to, subject, **forge_args) recipients = [recipients] if not isinstance(recipients, list) else recipients
msg = msg if msg else self.forge_message(recipients, subject, **forge_args)
if just_try or self._get_option("just_try"): if just_try or self._get_option("just_try"):
log.debug( log.debug(
'Just-try mode: do not really send this email to %s (subject="%s")', 'Just-try mode: do not really send this email to %s (subject="%s")',
rcpt_to, ", ".join(recipients),
subject or msg.get("subject", "No subject"), subject or msg.get("subject", "No subject"),
) )
return True return True
catch_addr = self._get_option("catch_all_addr") catch_addr = self._get_option("catch_all_addr")
if catch_addr: if catch_addr:
log.debug("Catch email originaly send to %s to %s", rcpt_to, catch_addr) log.debug("Catch email originaly send to %s to %s", ", ".join(recipients), catch_addr)
rcpt_to = catch_addr recipients = catch_addr if isinstance(catch_addr, list) else [catch_addr]
smtp_host = self._get_option("smtp_host") smtp_host = self._get_option("smtp_host")
smtp_port = self._get_option("smtp_port") smtp_port = self._get_option("smtp_port")
@ -347,15 +354,18 @@ class EmailClient(
error = False error = False
try: try:
log.info("Sending email to %s", rcpt_to) log.info("Sending email to %s", ", ".join(recipients))
server.sendmail( server.sendmail(
self._get_option("sender_email"), self._get_option("sender_email"),
[rcpt_to[1] if isinstance(rcpt_to, tuple) else rcpt_to], [
recipient[1] if isinstance(recipient, tuple) else recipient
for recipient in recipients
],
msg.as_string(), msg.as_string(),
) )
except smtplib.SMTPException: except smtplib.SMTPException:
error = True error = True
log.error("Error sending email to %s", rcpt_to, exc_info=True) log.error("Error sending email to %s", ", ".join(recipients), exc_info=True)
finally: finally:
server.quit() server.quit()

View file

@ -30,7 +30,8 @@ def main(argv=None): # pylint: disable=too-many-locals,too-many-statements
action="store", action="store",
type=str, type=str,
dest="test_to", dest="test_to",
help="Test email recipient", help="Test email recipient(s)",
nargs="+",
) )
test_opts.add_argument( test_opts.add_argument(
@ -53,7 +54,7 @@ def main(argv=None): # pylint: disable=too-many-locals,too-many-statements
options = parser.parse_args() options = parser.parse_args()
if not options.test_to: if not options.test_to:
parser.error("You must specify test email recipient using -t/--to parameter") parser.error("You must specify at least one test email recipient using -t/--to parameter")
sys.exit(1) sys.exit(1)
# Initialize logs # Initialize logs
@ -64,7 +65,7 @@ def main(argv=None): # pylint: disable=too-many-locals,too-many-statements
email_client = init_email_client(options) email_client = init_email_client(options)
log.info("Send a test email to %s", options.test_to) log.info("Send a test email to %s", ", ".join(options.test_to))
if email_client.send(options.test_to, template="test", sent_date=datetime.datetime.now()): if email_client.send(options.test_to, template="test", sent_date=datetime.datetime.now()):
log.info("Test email sent") log.info("Test email sent")
sys.exit(0) sys.exit(0)

View file

@ -35,7 +35,8 @@ def main(argv=None): # pylint: disable=too-many-locals,too-many-statements
action="store", action="store",
type=str, type=str,
dest="test_to", dest="test_to",
help="Test email recipient", help="Test email recipient(s)",
nargs="+",
) )
test_opts.add_argument( test_opts.add_argument(
@ -58,10 +59,10 @@ def main(argv=None): # pylint: disable=too-many-locals,too-many-statements
options = config.parse_arguments_options() options = config.parse_arguments_options()
if not options.test_to: if not options.test_to:
parser.error("You must specify test email recipient using -t/--to parameter") parser.error("You must specify at least one test email recipient using -t/--to parameter")
sys.exit(1) sys.exit(1)
logging.info("Send a test email to %s", options.test_to) logging.info("Send a test email to %s", ", ".join(options.test_to))
if email_client.send(options.test_to, template="test", sent_date=datetime.datetime.now()): if email_client.send(options.test_to, template="test", sent_date=datetime.datetime.now()):
logging.info("Test email sent") logging.info("Test email sent")
sys.exit(0) sys.exit(0)