|
| 1 | +from attrs import define, field |
1 | 2 | import smtplib
|
2 | 3 | from email.mime.multipart import MIMEMultipart
|
3 | 4 | from email.mime.text import MIMEText
|
|
15 | 16 | logger = AppLogger().get_logger()
|
16 | 17 |
|
17 | 18 |
|
| 19 | +@define |
18 | 20 | class SMTPEmailService(metaclass=SingletonMetaNoArgs):
|
19 |
| - def __init__(self): |
20 |
| - self.server = smtplib.SMTP( |
21 |
| - global_settings.smtp.server, global_settings.smtp.port |
22 |
| - ) |
| 21 | + # SMTP configuration |
| 22 | + server_host: str = field(default=global_settings.smtp.server) |
| 23 | + server_port: int = field(default=global_settings.smtp.port) |
| 24 | + username: str = field(default=global_settings.smtp.username) |
| 25 | + password: str = field(default=global_settings.smtp.password) |
| 26 | + |
| 27 | + # Dependencies |
| 28 | + templates: Jinja2Templates = field( |
| 29 | + factory=lambda: Jinja2Templates(global_settings.templates_dir) |
| 30 | + ) |
| 31 | + server: smtplib.SMTP = field(init=False) # Deferred initialization in post-init |
| 32 | + |
| 33 | + def __attrs_post_init__(self): |
| 34 | + """Initialize the SMTP server connection after object creation.""" |
| 35 | + self.server = smtplib.SMTP(self.server_host, self.server_port) |
23 | 36 | self.server.starttls()
|
24 |
| - self.server.login(global_settings.smtp.username, global_settings.smtp.password) |
25 |
| - self.templates = Jinja2Templates("templates") |
| 37 | + self.server.login(self.username, self.password) |
26 | 38 |
|
27 |
| - def send_email( |
| 39 | + def _prepare_email( |
28 | 40 | self,
|
29 | 41 | sender: EmailStr,
|
30 | 42 | recipients: list[EmailStr],
|
31 | 43 | subject: str,
|
32 |
| - body_text: str = "", |
33 |
| - body_html=None, |
34 |
| - ): |
| 44 | + body_text: str, |
| 45 | + body_html: str, |
| 46 | + ) -> MIMEMultipart: |
| 47 | + """Prepare the email message.""" |
35 | 48 | msg = MIMEMultipart()
|
36 | 49 | msg["From"] = sender
|
37 | 50 | msg["To"] = ",".join(recipients)
|
38 | 51 | msg["Subject"] = subject
|
39 | 52 | msg.attach(MIMEText(body_text, "plain"))
|
40 | 53 | if body_html:
|
41 | 54 | msg.attach(MIMEText(body_html, "html"))
|
| 55 | + return msg |
| 56 | + |
| 57 | + def send_email( |
| 58 | + self, |
| 59 | + sender: EmailStr, |
| 60 | + recipients: list[EmailStr], |
| 61 | + subject: str, |
| 62 | + body_text: str = "", |
| 63 | + body_html: str = None, |
| 64 | + ): |
| 65 | + """Send a regular email (plain text or HTML).""" |
| 66 | + msg = self._prepare_email(sender, recipients, subject, body_text, body_html) |
42 | 67 | self.server.sendmail(sender, recipients, msg.as_string())
|
43 | 68 |
|
44 | 69 | def send_template_email(
|
45 | 70 | self,
|
46 | 71 | recipients: list[EmailStr],
|
47 | 72 | subject: str,
|
48 |
| - template: str = None, |
49 |
| - context: dict = None, |
50 |
| - sender: EmailStr = global_settings.smtp.from_email, |
| 73 | + template: str, |
| 74 | + context: dict, |
| 75 | + sender: EmailStr, |
51 | 76 | ):
|
| 77 | + """Send an email using a template with the provided context.""" |
52 | 78 | template_str = self.templates.get_template(template)
|
53 | 79 | body_html = template_str.render(context)
|
54 | 80 | self.send_email(sender, recipients, subject, body_html=body_html)
|
0 commit comments