Email: add support for CC & BCC recipients

This commit is contained in:
Benjamin Renard 2023-03-14 17:00:31 +01:00
parent c93b3508ed
commit 73735b378f
Signed by: bn8
GPG key ID: 3E2E1CE1907115BC
4 changed files with 104 additions and 11 deletions

View file

@ -8,6 +8,8 @@ disable=invalid-name,
too-many-nested-blocks, too-many-nested-blocks,
too-many-instance-attributes, too-many-instance-attributes,
too-many-lines, too-many-lines,
too-many-statements,
logging-too-many-args,
duplicate-code, duplicate-code,
[FORMAT] [FORMAT]

View file

@ -187,6 +187,7 @@ class EmailClient(
sender_email=None, sender_email=None,
encoding=None, encoding=None,
template=None, template=None,
cc=None,
**template_vars, **template_vars,
): ):
""" """
@ -203,6 +204,8 @@ class EmailClient(
:param sender_email: Custom sender email (default: as defined on initialization) :param sender_email: Custom sender email (default: as defined on initialization)
:param encoding: Email content encoding (default: as defined on initialization) :param encoding: Email content encoding (default: as defined on initialization)
:param template: The name of a template to use to forge this email :param template: The name of a template to use to forge this email
:param cc: Optional list of CC recipient addresses.
List of tuple(name, email) or just the email of the recipients.
All other parameters will be consider as template variables. All other parameters will be consider as template variables.
""" """
@ -214,6 +217,16 @@ class EmailClient(
for recipient in recipients for recipient in recipients
] ]
) )
if cc:
cc = [cc] if not isinstance(cc, list) else cc
msg["Cc"] = ", ".join(
[
email.utils.formataddr(recipient) if isinstance(recipient, tuple) else recipient
for recipient in cc
]
)
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"),
@ -286,7 +299,9 @@ class EmailClient(
msg.attach(part) msg.attach(part)
return msg return msg
def send(self, recipients, msg=None, subject=None, just_try=False, **forge_args): def send(
self, recipients, msg=None, subject=None, just_try=False, cc=None, bcc=None, **forge_args
):
""" """
Send an email Send an email
@ -297,12 +312,41 @@ class EmailClient(
using msg parameter) using msg parameter)
:param just_try: Enable just try mode (do not really send email, default: as defined on :param just_try: Enable just try mode (do not really send email, default: as defined on
initialization) initialization)
:param cc: Optional list of CC recipient addresses. List of tuple(name, email) or
just the email of the recipients.
:param bcc: Optional list of BCC recipient addresses. List of tuple(name, email) or
just the email of the recipients.
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).
""" """
recipients = [recipients] if not isinstance(recipients, list) else recipients recipients = [recipients] if not isinstance(recipients, list) else recipients
msg = msg if msg else self.forge_message(recipients, subject, **forge_args) msg = msg if msg else self.forge_message(recipients, subject, cc=cc, **forge_args)
catch_addr = self._get_option("catch_all_addr")
if catch_addr:
log.debug(
"Catch email originaly send to %s (CC:%s, BCC:%s) to %s",
", ".join(recipients),
", ".join(cc) if isinstance(cc, list) else cc,
", ".join(bcc) if isinstance(bcc, list) else bcc,
catch_addr,
)
recipients = catch_addr if isinstance(catch_addr, list) else [catch_addr]
else:
if cc:
recipients.extend(
[
recipient[1] if isinstance(recipient, tuple) else recipient
for recipient in (cc if isinstance(cc, list) else [cc])
]
)
if bcc:
recipients.extend(
[
recipient[1] if isinstance(recipient, tuple) else recipient
for recipient in (bcc if isinstance(bcc, list) else [bcc])
]
)
if just_try or self._get_option("just_try"): if just_try or self._get_option("just_try"):
log.debug( log.debug(
@ -312,11 +356,6 @@ class EmailClient(
) )
return True return True
catch_addr = self._get_option("catch_all_addr")
if catch_addr:
log.debug("Catch email originaly send to %s to %s", ", ".join(recipients), 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")
try: try:

View file

@ -51,6 +51,24 @@ def main(argv=None): # pylint: disable=too-many-locals,too-many-statements
help="Test mako templating", help="Test mako templating",
) )
test_opts.add_argument(
"--cc",
action="store",
type=str,
dest="test_cc",
help="Test CC email recipient(s)",
nargs="+",
)
test_opts.add_argument(
"--bcc",
action="store",
type=str,
dest="test_bcc",
help="Test BCC email recipient(s)",
nargs="+",
)
options = parser.parse_args() options = parser.parse_args()
if not options.test_to: if not options.test_to:
@ -65,8 +83,19 @@ 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", ", ".join(options.test_to)) log.info(
if email_client.send(options.test_to, template="test", sent_date=datetime.datetime.now()): "Send a test email to %s (CC: %s / BCC: %s)",
", ".join(options.test_to),
", ".join(options.test_cc) if options.test_cc else None,
", ".join(options.test_bcc) if options.test_bcc else None,
)
if email_client.send(
options.test_to,
cc=options.test_cc,
bcc=options.test_bcc,
template="test",
sent_date=datetime.datetime.now(),
):
log.info("Test email sent") log.info("Test email sent")
sys.exit(0) sys.exit(0)
log.error("Fail to send test email") log.error("Fail to send test email")

View file

@ -56,14 +56,37 @@ def main(argv=None): # pylint: disable=too-many-locals,too-many-statements
help="Test mako templating", help="Test mako templating",
) )
test_opts.add_argument(
"--cc",
action="store",
type=str,
dest="test_cc",
help="Test CC email recipient(s)",
nargs="+",
)
test_opts.add_argument(
"--bcc",
action="store",
type=str,
dest="test_bcc",
help="Test BCC email recipient(s)",
nargs="+",
)
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 at least one 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", ", ".join(options.test_to)) if email_client.send(
if email_client.send(options.test_to, template="test", sent_date=datetime.datetime.now()): options.test_to,
cc=options.test_cc,
bcc=options.test_bcc,
template="test",
sent_date=datetime.datetime.now(),
):
logging.info("Test email sent") logging.info("Test email sent")
sys.exit(0) sys.exit(0)
logging.error("Fail to send test email") logging.error("Fail to send test email")