Changes to the mail sending system to allow for a more fluent proccessing of the mails in regarts to the rate limiting

This commit is contained in:
2026-07-28 19:03:15 +02:00
parent 70b108d841
commit ea402f3223
4 changed files with 91 additions and 38 deletions
+6
View File
@@ -278,6 +278,7 @@ INVENTORY_MODULE_ENABLED = _TenantAwareBool('inventory', _get(_conf, ['modules',
TERMINPLAN_MODULE_ENABLED = _TenantAwareBool('terminplan', _get(_conf, ['modules', 'terminplan', 'enabled'], DEFAULTS['modules']['terminplan']['enabled'])) TERMINPLAN_MODULE_ENABLED = _TenantAwareBool('terminplan', _get(_conf, ['modules', 'terminplan', 'enabled'], DEFAULTS['modules']['terminplan']['enabled']))
LIBRARY_MODULE_ENABLED = _TenantAwareBool('library', _get(_conf, ['modules', 'library', 'enabled'], DEFAULTS['modules']['library']['enabled'])) LIBRARY_MODULE_ENABLED = _TenantAwareBool('library', _get(_conf, ['modules', 'library', 'enabled'], DEFAULTS['modules']['library']['enabled']))
STUDENT_CARDS_MODULE_ENABLED = _TenantAwareBool('student_cards', _get(_conf, ['modules', 'student_cards', 'enabled'], DEFAULTS['modules']['student_cards']['enabled'])) STUDENT_CARDS_MODULE_ENABLED = _TenantAwareBool('student_cards', _get(_conf, ['modules', 'student_cards', 'enabled'], DEFAULTS['modules']['student_cards']['enabled']))
MAIL_ADD_ON_ENABLED = _TenantAwareBool('mail', _get(_conf, ['email', 'enabled'], False))
def _match_inventory(path): def _match_inventory(path):
if not path: return False if not path: return False
@@ -297,11 +298,16 @@ def _match_student_cards(path):
if not path: return False if not path: return False
return path.startswith(('/student_cards')) return path.startswith(('/student_cards'))
def _match_mail(path):
if not path: return False
return path.startswith(('/'))
# Register core modules into the pipeline # Register core modules into the pipeline
MODULES.register('inventory', INVENTORY_MODULE_ENABLED, _match_inventory) MODULES.register('inventory', INVENTORY_MODULE_ENABLED, _match_inventory)
MODULES.register('terminplan', TERMINPLAN_MODULE_ENABLED, _match_terminplan) MODULES.register('terminplan', TERMINPLAN_MODULE_ENABLED, _match_terminplan)
MODULES.register('library', LIBRARY_MODULE_ENABLED, _match_library) MODULES.register('library', LIBRARY_MODULE_ENABLED, _match_library)
MODULES.register('student_cards', STUDENT_CARDS_MODULE_ENABLED, _match_student_cards) MODULES.register('student_cards', STUDENT_CARDS_MODULE_ENABLED, _match_student_cards)
MODULES.register('mail', MAIL_ADD_ON_ENABLED, _match_mail)
STUDENT_DEFAULT_BORROW_DAYS = int(_get(_conf, ['modules', 'student_cards', 'default_borrow_days'], DEFAULTS['modules']['student_cards']['default_borrow_days'])) STUDENT_DEFAULT_BORROW_DAYS = int(_get(_conf, ['modules', 'student_cards', 'default_borrow_days'], DEFAULTS['modules']['student_cards']['default_borrow_days']))
STUDENT_MAX_BORROW_DAYS = int(_get(_conf, ["modules", "student_cards", "max_borrow_days"], DEFAULTS["modules"]["student_cards"]["max_borrow_days"])) STUDENT_MAX_BORROW_DAYS = int(_get(_conf, ["modules", "student_cards", "max_borrow_days"], DEFAULTS["modules"]["student_cards"]["max_borrow_days"]))
+72 -25
View File
@@ -1,51 +1,98 @@
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart from email.mime.multipart import MIMEMultipart
from modules.module_registry import ModuleRegistry as mr from email.mime.text import MIMEText
import smtplib import smtplib
import time
import Web.modules.database.settings as cfg import Web.modules.database.settings as cfg
def _build_smtp_client(): def _build_smtp_client():
smtp = smtplib.SMTP(cfg.EMAIL_SMTP_HOST, cfg.EMAIL_SMTP_PORT, timeout=cfg.EMAIL_TIMEOUT_SECONDS) smtp = smtplib.SMTP(
cfg.EMAIL_SMTP_HOST,
cfg.EMAIL_SMTP_PORT,
timeout=cfg.EMAIL_TIMEOUT_SECONDS,
)
smtp.ehlo() smtp.ehlo()
if cfg.EMAIL_USE_TLS: if cfg.EMAIL_USE_TLS:
smtp.starttls() smtp.starttls()
smtp.ehlo() smtp.ehlo()
if cfg.EMAIL_USERNAME: if cfg.EMAIL_USERNAME:
smtp.login(cfg.EMAIL_USERNAME, cfg.EMAIL_PASSWORD or '') smtp.login(cfg.EMAIL_USERNAME, cfg.EMAIL_PASSWORD or "")
return smtp return smtp
def send(email: list, subject: str, note: str, sender: str) -> bool:
"""
Sends the email with the link to the Clients
Input: def send(email: list | str, subject: str, note: str, sender: str) -> bool:
- email: Email list of all the addresses to send the link to ["","",""] """Sends the email with the link to the Clients."""
- subject: Subject of the email if not cfg.MODULES.is_enabled("mail"):
- note: Note that is send with the Emails print("Debug: Module not enabled")
Output:
- bool: true if the sending worked and false if it didnt
"""
if not mr.registry.is_enabled('mail'):
return False return False
else:
msg = MIMEMultipart() if isinstance(email, str):
msg['Subject'] = subject email = [email]
msg['From'] = sender or cfg.EMAIL_FROM_ADDRESS or cfg.EMAIL_USERNAME
msg['To'] = ', '.join(email) if isinstance(email, (list, tuple)) else str(email) body_message = note
msg.attach(MIMEText(note))
HTML_SIGNATURE = f"""
<table cellpadding="0" cellspacing="0" border="0" style="font-family: Arial, Helvetica, sans-serif; font-size: 13px; color: #333333; line-height: 1.5;">
<tr>
<td>
<p style="margin:0 0 12px 0;">Mit freundlichen Grüßen</p>
<p style="margin:0;"><strong style="font-size:16px;">Automatisierter Email Verteiler für die Schule: {cfg.get_school_info().get("name")}</strong><br></p>
<p style="margin:12px 0 0 0;"><strong>Invario UG</strong><br>Am Sportplatz 10<br>83052 Bruckmühl</p>
</td>
</tr>
</table>
"""
text_content = f"{body_message}\n\nMit freundlichen Grüßen\n{sender}\nInvario UG"
html_content = f"""
<html>
<body>
<p>{body_message}</p>
<br>
{HTML_SIGNATURE}
</body>
</html>
"""
mails_per_second = 10
interval = 1.0 / mails_per_second
smtp = None smtp = None
try: try:
smtp = _build_smtp_client() smtp = _build_smtp_client()
smtp.sendmail(from_addr=msg['From'], to_addrs=email, msg=msg.as_string())
for i, recipient in enumerate(email):
start_time = time.time()
msg = MIMEMultipart("alternative")
msg["Subject"] = str(subject)
msg["From"] = f"{sender} <{cfg.EMAIL_USERNAME}>"
msg["To"] = str(recipient)
msg.attach(MIMEText(text_content, "plain"))
msg.attach(MIMEText(html_content, "html"))
smtp.sendmail(
from_addr=cfg.EMAIL_USERNAME,
to_addrs=[recipient],
msg=msg.as_string()
)
elapsed_time = time.time() - start_time
sleep_time = interval - elapsed_time
if sleep_time > 0 and i < len(email) - 1:
time.sleep(sleep_time)
return True return True
except Exception: except Exception as e:
print(f"Debug: Fehler beim Senden der E-Mail: {e}")
return False return False
finally: finally:
try:
if smtp: if smtp:
try:
smtp.quit() smtp.quit()
except Exception: except Exception:
pass pass
+1 -1
View File
@@ -22,7 +22,7 @@ def _resolve_public_base_url() -> str:
subdomain = '' subdomain = ''
if tenant_context: if tenant_context:
subdomain = getattr(tenant_context, 'subdomain', '') or getattr(tenant_context, 'tenant_id', '') or '' subdomain = getattr(tenant_context, 'subdomain', '') or getattr(tenant_context, 'tenant_id', '') or ''
return f"https://{subdomain}.invario.eu" if subdomain else "https://invario.eu" return (f"https://{subdomain}.invario-software.de") if subdomain else "https://invario-software.de"
def _current_tenant_id() -> str: def _current_tenant_id() -> str:
+6 -6
View File
@@ -20,14 +20,14 @@
"key": "Web/certs/key.pem" "key": "Web/certs/key.pem"
}, },
"email": { "email": {
"enabled": false, "enabled": true,
"smtp_host": "smtp.gmail.com", "smtp_host": "mail.invario-software.de",
"smtp_port": 587, "smtp_port": 587,
"use_tls": true, "use_tls": true,
"username": "", "username": "no-reply@invario-software.de",
"password": "", "password": "#,EATwIn,68",
"from_address": "", "from_address": "no-reply@invario-software.de",
"default_sender_name": "Invario Inventarprogramm", "default_sender_name": "Invario Email Service",
"timeout_seconds": 30 "timeout_seconds": 30
}, },
"images": { "images": {