first commit
This commit is contained in:
commit
cce9a8c9ba
73 changed files with 8665 additions and 0 deletions
107
backend/app/services/mail_sender.py
Normal file
107
backend/app/services/mail_sender.py
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
import email.policy
|
||||
import email.utils
|
||||
from email.message import EmailMessage
|
||||
|
||||
import aiosmtplib
|
||||
import dkim
|
||||
|
||||
from app.config import get_settings
|
||||
from app.models import MailDomain
|
||||
|
||||
SMTP_POLICY = email.policy.SMTP.clone(max_line_length=998)
|
||||
|
||||
|
||||
def build_message(
|
||||
*,
|
||||
sender: str,
|
||||
recipients: list[str],
|
||||
cc: list[str],
|
||||
subject: str,
|
||||
body_text: str,
|
||||
body_html: str | None,
|
||||
) -> EmailMessage:
|
||||
message = EmailMessage(policy=SMTP_POLICY)
|
||||
message["From"] = sender
|
||||
message["To"] = ", ".join(recipients)
|
||||
if cc:
|
||||
message["Cc"] = ", ".join(cc)
|
||||
message["Subject"] = subject
|
||||
message["Date"] = email.utils.formatdate(localtime=True)
|
||||
message["Message-ID"] = email.utils.make_msgid(domain=sender.split("@")[-1])
|
||||
message.set_content(body_text)
|
||||
if body_html:
|
||||
message.add_alternative(body_html, subtype="html")
|
||||
return message
|
||||
|
||||
|
||||
def sign_message(message: EmailMessage, domain: MailDomain) -> bytes:
|
||||
raw_message = message.as_bytes(policy=SMTP_POLICY)
|
||||
signed_headers = [
|
||||
b"from",
|
||||
b"to",
|
||||
b"subject",
|
||||
b"date",
|
||||
b"message-id",
|
||||
b"mime-version",
|
||||
b"content-type",
|
||||
]
|
||||
if message.get("Cc"):
|
||||
signed_headers.insert(3, b"cc")
|
||||
signature = dkim.sign(
|
||||
raw_message,
|
||||
domain.dkim_selector.encode(),
|
||||
domain.domain_name.encode(),
|
||||
domain.dkim_private_key.encode(),
|
||||
include_headers=signed_headers,
|
||||
canonicalize=(b"relaxed", b"relaxed"),
|
||||
)
|
||||
if not signature.endswith(b"\r\n"):
|
||||
signature += b"\r\n"
|
||||
return signature + raw_message
|
||||
|
||||
|
||||
async def send_via_smtp(
|
||||
message_bytes: bytes,
|
||||
*,
|
||||
sender: str,
|
||||
recipients: list[str],
|
||||
) -> None:
|
||||
settings = get_settings()
|
||||
await aiosmtplib.send(
|
||||
message_bytes,
|
||||
sender=sender,
|
||||
recipients=recipients,
|
||||
hostname=settings.smtp_host,
|
||||
port=settings.smtp_port,
|
||||
username=settings.smtp_username or None,
|
||||
password=settings.smtp_password or None,
|
||||
start_tls=settings.smtp_use_tls,
|
||||
timeout=settings.smtp_timeout,
|
||||
)
|
||||
|
||||
|
||||
async def deliver_email(
|
||||
*,
|
||||
sender: str,
|
||||
recipients: list[str],
|
||||
cc: list[str],
|
||||
bcc: list[str],
|
||||
subject: str,
|
||||
body_text: str,
|
||||
body_html: str | None,
|
||||
domain: MailDomain,
|
||||
) -> None:
|
||||
message = build_message(
|
||||
sender=sender,
|
||||
recipients=recipients,
|
||||
cc=cc,
|
||||
subject=subject,
|
||||
body_text=body_text,
|
||||
body_html=body_html,
|
||||
)
|
||||
signed = sign_message(message, domain)
|
||||
await send_via_smtp(
|
||||
signed,
|
||||
sender=sender,
|
||||
recipients=[*recipients, *cc, *bcc],
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue