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