fix(email): SMTP-Versand mit 3 Versuchen und 20s-Timeout
Die IONOS-Farm (smtp.ionos.de, 10 Backends im DNS-Round-Robin) hatte am 10.07. teils tote/lahme Backends - ein einzelner Sendeversuch schlug dann fehl (Timed out waiting for server ready message). Jeder Retry oeffnet eine neue Verbindung und trifft per Round-Robin ein anderes Backend. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Dieser Commit ist enthalten in:
@@ -1,4 +1,5 @@
|
|||||||
"""Async E-Mail-Versand via SMTP."""
|
"""Async E-Mail-Versand via SMTP."""
|
||||||
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from email.mime.text import MIMEText
|
from email.mime.text import MIMEText
|
||||||
from email.mime.multipart import MIMEMultipart
|
from email.mime.multipart import MIMEMultipart
|
||||||
@@ -17,6 +18,12 @@ from config import (
|
|||||||
|
|
||||||
logger = logging.getLogger("osint.email")
|
logger = logging.getLogger("osint.email")
|
||||||
|
|
||||||
|
# Die IONOS-SMTP-Farm (smtp.ionos.de) besteht aus ~10 Backends im DNS-Round-Robin;
|
||||||
|
# einzelne Backends koennen tot/ueberlastet sein (beobachtet 2026-07-10). Jeder
|
||||||
|
# Versuch oeffnet eine neue Verbindung und wuerfelt damit ein neues Backend.
|
||||||
|
MAIL_VERSUCHE = 3
|
||||||
|
MAIL_TIMEOUT_S = 20
|
||||||
|
|
||||||
|
|
||||||
async def send_email(to_email: str, subject: str, html_body: str) -> bool:
|
async def send_email(to_email: str, subject: str, html_body: str) -> bool:
|
||||||
"""Sendet eine HTML-E-Mail.
|
"""Sendet eine HTML-E-Mail.
|
||||||
@@ -38,6 +45,8 @@ async def send_email(to_email: str, subject: str, html_body: str) -> bool:
|
|||||||
msg.attach(MIMEText(text_content, "plain", "utf-8"))
|
msg.attach(MIMEText(text_content, "plain", "utf-8"))
|
||||||
msg.attach(MIMEText(html_body, "html", "utf-8"))
|
msg.attach(MIMEText(html_body, "html", "utf-8"))
|
||||||
|
|
||||||
|
letzter_fehler = None
|
||||||
|
for versuch in range(1, MAIL_VERSUCHE + 1):
|
||||||
try:
|
try:
|
||||||
await aiosmtplib.send(
|
await aiosmtplib.send(
|
||||||
msg,
|
msg,
|
||||||
@@ -46,9 +55,20 @@ async def send_email(to_email: str, subject: str, html_body: str) -> bool:
|
|||||||
username=SMTP_USER if SMTP_USER else None,
|
username=SMTP_USER if SMTP_USER else None,
|
||||||
password=SMTP_PASSWORD if SMTP_PASSWORD else None,
|
password=SMTP_PASSWORD if SMTP_PASSWORD else None,
|
||||||
start_tls=SMTP_USE_TLS,
|
start_tls=SMTP_USE_TLS,
|
||||||
|
timeout=MAIL_TIMEOUT_S,
|
||||||
)
|
)
|
||||||
logger.info(f"E-Mail gesendet an {to_email}: {subject}")
|
logger.info(f"E-Mail gesendet an {to_email}: {subject}")
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"E-Mail-Versand fehlgeschlagen an {to_email}: {e}")
|
letzter_fehler = e
|
||||||
|
logger.warning(
|
||||||
|
f"E-Mail-Versand Versuch {versuch}/{MAIL_VERSUCHE} an {to_email} fehlgeschlagen: {e}"
|
||||||
|
)
|
||||||
|
if versuch < MAIL_VERSUCHE:
|
||||||
|
await asyncio.sleep(1.5)
|
||||||
|
|
||||||
|
logger.error(
|
||||||
|
f"E-Mail-Versand endgueltig fehlgeschlagen an {to_email} "
|
||||||
|
f"nach {MAIL_VERSUCHE} Versuchen: {letzter_fehler}"
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren