|
| 1 | +import re |
1 | 2 | import urllib.parse |
| 3 | +from html import unescape |
2 | 4 |
|
3 | 5 | from databases import Database |
4 | 6 | from fastapi_mail import MessageSchema, MessageType |
| 7 | +from fastapi_mail.schemas import MultipartSubtypeEnum |
5 | 8 | from itsdangerous import URLSafeTimedSerializer |
6 | 9 | from loguru import logger |
7 | 10 |
|
|
16 | 19 | ) |
17 | 20 |
|
18 | 21 |
|
| 22 | +def html_to_text(html_content: str) -> str: |
| 23 | + """Convert HTML to plain text for email alternative body.""" |
| 24 | + if not html_content: |
| 25 | + return "" |
| 26 | + text = re.sub( |
| 27 | + r"<(style|script)[^>]*>.*?</\1>", "", html_content, flags=re.DOTALL | re.I |
| 28 | + ) |
| 29 | + text = re.sub(r"<br\s*/?>|</p>|</div>|</tr>|</h[1-6]>", "\n", text, flags=re.I) |
| 30 | + text = re.sub(r"<[^>]+>", "", text) |
| 31 | + text = unescape(text) |
| 32 | + text = re.sub(r"[ \t]+", " ", text) |
| 33 | + text = re.sub(r"\n ", "\n", text) |
| 34 | + text = re.sub(r"\n{3,}", "\n\n", text) |
| 35 | + return text.strip() |
| 36 | + |
| 37 | + |
19 | 38 | class SMTPService: |
20 | 39 | @staticmethod |
21 | 40 | async def send_verification_email(to_address: str, username: str): |
@@ -183,11 +202,15 @@ async def _send_message( |
183 | 202 | from_address = settings.MAIL_DEFAULT_SENDER |
184 | 203 | if from_address is None: |
185 | 204 | raise ValueError("Missing TM_EMAIL_FROM_ADDRESS environment variable") |
| 205 | + if text_message is None: |
| 206 | + text_message = html_to_text(html_message) |
186 | 207 | msg = MessageSchema( |
187 | 208 | recipients=[to_address], |
188 | 209 | subject=subject, |
189 | | - body=html_message, |
190 | | - subtype=MessageType.html, |
| 210 | + body=text_message, |
| 211 | + alternative_body=html_message, |
| 212 | + subtype=MessageType.plain, |
| 213 | + multipart_subtype=MultipartSubtypeEnum.alternative, |
191 | 214 | ) |
192 | 215 | logger.debug(f"Sending email via SMTP {to_address}") |
193 | 216 | if settings.LOG_LEVEL == "DEBUG": |
|
0 commit comments