diff options
Diffstat (limited to 'Biz')
-rwxr-xr-x | Biz/EmailAgent.py | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/Biz/EmailAgent.py b/Biz/EmailAgent.py new file mode 100755 index 0000000..6ac4c95 --- /dev/null +++ b/Biz/EmailAgent.py @@ -0,0 +1,151 @@ +#!/usr/bin/env run.sh +""" +Email sending utility that can be used as a script or imported as a library. + +Password is provided through systemd's LoadCredential feature. This is intended +to be used by automated agents in a systemd timer. +""" + +import argparse +import email.message +import email.utils +import errno +import os +import pathlib +import smtplib +import sys + + +# ruff: noqa: PLR0917, PLR0913 +def send_email( + to_addrs: list[str], + from_addr: str, + smtp_server: str, + password: str, + subject: str, + body_text: pathlib.Path, + body_html: pathlib.Path | None = None, + port: int = 587, +) -> dict[str, tuple[int, bytes]]: + """ + Send an email using the provided parameters. + + Args: + to_addr: Recipient email addresses + from_addr: Sender email address + smtp_server: SMTP server hostname + password: Password for authentication + subject: Email subject + body_text: File with email body text + body_html: File with email body html + port: SMTP server port (default: 587) + + """ + msg = email.message.EmailMessage() + msg["Subject"] = subject + msg["From"] = from_addr + msg["To"] = ", ".join(to_addrs) + msg["Message-ID"] = email.utils.make_msgid( + idstring=__name__, + domain=smtp_server, + ) + msg["Date"] = email.utils.formatdate(localtime=True) + with body_text.open(encoding="utf-8") as txt: + msg.set_content(txt.read()) + if body_html: + with body_html.open(encoding="utf-*") as html: + msg.add_alternative(html.read(), subtype="html") + with smtplib.SMTP(smtp_server, port) as server: + server.starttls() + server.login(from_addr, password) + return server.send_message( + msg, + from_addr=from_addr, + to_addrs=to_addrs, + ) + + +def main() -> None: + """Parse command line arguments and send email. + + Raises: + FileNotFoundError: if --password-file does not exist + """ + if "test" in sys.argv: + sys.exit(0) + parser = argparse.ArgumentParser( + description="Send an email", + ) + parser.add_argument( + "--to", + required=True, + help="Recipient email addresses, can be specified multiple times", + nargs="+", + action="extend", + ) + parser.add_argument( + "--from", + dest="from_addr", + required=True, + help="Sender email address", + ) + parser.add_argument( + "--smtp-server", + required=True, + help="SMTP server hostname", + ) + parser.add_argument("--subject", required=True, help="Email subject") + parser.add_argument( + "--body-text", + required=True, + help="File with email body text", + ) + parser.add_argument( + "--body-html", + help="File with email body html", + default=None, + ) + parser.add_argument( + "--port", + type=int, + default=587, + help="SMTP server port (default: 587)", + ) + parser.add_argument( + "--password-file", + default="smtp-password", + help="Where to find the password file", + ) + + args = parser.parse_args() + + credential_path = pathlib.Path(args.password_file) + if not credential_path.exists(): + raise FileNotFoundError( + errno.ENOENT, + os.strerror(errno.ENOENT), + credential_path, + ) + sys.exit(1) + + with pathlib.Path.open(credential_path, encoding="utf-8") as f: + password = f.read().strip() + + results = send_email( + to_addrs=args.to, + from_addr=args.from_addr, + smtp_server=args.smtp_server, + subject=args.subject, + body_text=pathlib.Path(args.body_text), + body_html=pathlib.Path(args.body_html) if args.body_html else None, + password=password, + port=args.port, + ) + if len(results) > 0: + sys.stdout.write(str(results)) + sys.stdout.flush() + sys.exit(1) + + +if __name__ == "__main__": + main() |