Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions backend/services/messaging/smtp_service.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import re
import urllib.parse
from html import unescape

from databases import Database
from fastapi_mail import MessageSchema, MessageType
from fastapi_mail.schemas import MultipartSubtypeEnum
from itsdangerous import URLSafeTimedSerializer
from loguru import logger

Expand All @@ -16,6 +19,22 @@
)


def html_to_text(html_content: str) -> str:
"""Convert HTML to plain text for email alternative body."""
if not html_content:
return ""
text = re.sub(
r"<(style|script)[^>]*>.*?</\1>", "", html_content, flags=re.DOTALL | re.I
)
text = re.sub(r"<br\s*/?>|</p>|</div>|</tr>|</h[1-6]>", "\n", text, flags=re.I)
text = re.sub(r"<[^>]+>", "", text)
text = unescape(text)
text = re.sub(r"[ \t]+", " ", text)
text = re.sub(r"\n ", "\n", text)
text = re.sub(r"\n{3,}", "\n\n", text)
return text.strip()


class SMTPService:
@staticmethod
async def send_verification_email(to_address: str, username: str):
Expand Down Expand Up @@ -183,11 +202,15 @@ async def _send_message(
from_address = settings.MAIL_DEFAULT_SENDER
if from_address is None:
raise ValueError("Missing TM_EMAIL_FROM_ADDRESS environment variable")
if text_message is None:
text_message = html_to_text(html_message)
msg = MessageSchema(
recipients=[to_address],
subject=subject,
body=html_message,
subtype=MessageType.html,
body=text_message,
alternative_body=html_message,
subtype=MessageType.plain,
multipart_subtype=MultipartSubtypeEnum.alternative,
)
logger.debug(f"Sending email via SMTP {to_address}")
if settings.LOG_LEVEL == "DEBUG":
Expand Down
Loading