summaryrefslogtreecommitdiff
path: root/Biz/EmailAgent.py
diff options
context:
space:
mode:
authorBen Sima <ben@bsima.me>2025-04-28 23:25:45 -0400
committerBen Sima <ben@bsima.me>2025-04-28 23:25:45 -0400
commit03d18a944f851c483792ee597dd1af9da42b8178 (patch)
tree1aa62ef0f2a638f118ccc4ba9824f65ecf2ccd3d /Biz/EmailAgent.py
parent05dabdc0e54196d9c1fccabbece9ba9a85c1ff81 (diff)
Create EmailAgent
I intend this to be a utility that agents or other automated jobs can use to send emails. EmailAgent isn't the right name... its really more of an LLM tool, or it could be if the right interface is put on top of it. Anyway, this works for now, I'll think about how to make it better later.
Diffstat (limited to 'Biz/EmailAgent.py')
-rwxr-xr-xBiz/EmailAgent.py151
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()