1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
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()
|