Coverage for src/kwai/core/mail/smtp_mailer.py: 78%
59 statements
« prev ^ index » next coverage.py v7.6.10, created at 2024-01-01 00:00 +0000
« prev ^ index » next coverage.py v7.6.10, created at 2024-01-01 00:00 +0000
1"""Module that defines a mailer service with SMTP."""
3import smtplib
4import ssl
6from email.headerregistry import Address
7from email.message import EmailMessage
9from .mailer import Mailer, MailerException
10from .message import Message
13class SmtpMailer(Mailer):
14 """A mailer service that uses an SMTP server."""
16 def __init__(
17 self, host: str, port: int | None = None, ssl_: bool = False, tls: bool = True
18 ):
19 self._host = host
20 if port is None:
21 self._port = 456 if ssl_ else 587
22 else:
23 self._port = port
24 self._ssl = ssl_
25 self._server = None
26 self._tls = tls
28 def __del__(self):
29 """Disconnect from the mailer."""
30 self.disconnect()
32 def connect(self):
33 """Connect to the SMTP server."""
34 context = ssl.create_default_context()
35 if self._ssl:
36 self._server = smtplib.SMTP_SSL(self._host, self._port, context=context)
37 else:
38 self._server = smtplib.SMTP(self._host, self._port)
40 if self._tls:
41 try:
42 self._server.starttls(context=context)
43 except smtplib.SMTPException as exc:
44 raise MailerException("STARTTLS failed.") from exc
46 def login(self, user: str, password: str):
47 """Login to the SMTP server."""
48 assert self._server is not None, "There is no connection to an SMTP server!"
50 try:
51 self._server.login(user, password)
52 except smtplib.SMTPException as exc:
53 raise MailerException("Login failed.") from exc
55 def disconnect(self):
56 """Disconnect from the SMTP server."""
57 if self._server:
58 try:
59 self._server.quit()
60 except smtplib.SMTPException:
61 pass # Ignore exceptions here
62 self._server = None
64 def send(self, message: Message):
65 """Send a message to the SMTP server."""
66 assert self._server is not None, "There is no connection to an SMTP server!"
68 email_message = EmailMessage()
70 email_message["Subject"] = message.subject
71 email_message.set_content(message.text)
72 for header_name, header_value in message.headers.items():
73 email_message.add_header(header_name, header_value)
75 email_message["From"] = Address(
76 display_name=message.recipients.from_.name,
77 addr_spec=str(message.recipients.from_.email),
78 )
80 email_message["To"] = [
81 Address(display_name=to.name, addr_spec=str(to.email))
82 for to in message.recipients.to
83 ]
84 if len(message.recipients.cc) > 0:
85 email_message["Cc"] = [
86 Address(display_name=cc.name, addr_spec=str(cc.email))
87 for cc in message.recipients.cc
88 ]
89 if len(message.recipients.bcc) > 0:
90 email_message["Bcc"] = [
91 Address(display_name=bcc.name, addr_spec=str(bcc.email))
92 for bcc in message.recipients.bcc
93 ]
95 if len(message.html) > 0:
96 email_message.add_alternative(message.html, subtype="html")
98 try:
99 self._server.send_message(email_message)
100 except smtplib.SMTPException as exc:
101 raise MailerException("Sendmail failed") from exc