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']))
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']))
MAIL_ADD_ON_ENABLED = _TenantAwareBool('mail', _get(_conf, ['email', 'enabled'], False))
def _match_inventory(path):
if not path: return False
@@ -297,11 +298,16 @@ def _match_student_cards(path):
if not path: return False
return path.startswith(('/student_cards'))
def _match_mail(path):
if not path: return False
return path.startswith(('/'))
# Register core modules into the pipeline
MODULES.register('inventory', INVENTORY_MODULE_ENABLED, _match_inventory)
MODULES.register('terminplan', TERMINPLAN_MODULE_ENABLED, _match_terminplan)
MODULES.register('library', LIBRARY_MODULE_ENABLED, _match_library)
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_MAX_BORROW_DAYS = int(_get(_conf, ["modules", "student_cards", "max_borrow_days"], DEFAULTS["modules"]["student_cards"]["max_borrow_days"]))
+78 -31
View File
@@ -1,51 +1,98 @@
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from modules.module_registry import ModuleRegistry as mr
from email.mime.text import MIMEText
import smtplib
import time
import Web.modules.database.settings as cfg
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()
if cfg.EMAIL_USE_TLS:
smtp.starttls()
smtp.ehlo()
if cfg.EMAIL_USERNAME:
smtp.login(cfg.EMAIL_USERNAME, cfg.EMAIL_PASSWORD or '')
smtp.login(cfg.EMAIL_USERNAME, cfg.EMAIL_PASSWORD or "")
return smtp
def send(email: list, subject: str, note: str, sender: str) -> bool:
"""
Sends the email with the link to the Clients
Input:
- email: Email list of all the addresses to send the link to ["","",""]
- subject: Subject of the email
- note: Note that is send with the Emails
def send(email: list | str, subject: str, note: str, sender: str) -> bool:
"""Sends the email with the link to the Clients."""
if not cfg.MODULES.is_enabled("mail"):
print("Debug: Module not enabled")
return False
if isinstance(email, str):
email = [email]
body_message = 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
Output:
- bool: true if the sending worked and false if it didnt
"""
if not mr.registry.is_enabled('mail'):
return False
else:
msg = MIMEMultipart()
msg['Subject'] = subject
msg['From'] = sender or cfg.EMAIL_FROM_ADDRESS or cfg.EMAIL_USERNAME
msg['To'] = ', '.join(email) if isinstance(email, (list, tuple)) else str(email)
msg.attach(MIMEText(note))
smtp = None
try:
smtp = _build_smtp_client()
smtp.sendmail(from_addr=msg['From'], to_addrs=email, msg=msg.as_string())
return True
except Exception:
return False
smtp = _build_smtp_client()
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
except Exception as e:
print(f"Debug: Fehler beim Senden der E-Mail: {e}")
return False
finally:
try:
if smtp:
smtp.quit()
except Exception:
pass
try:
smtp.quit()
except Exception:
pass
+1 -1
View File
@@ -22,7 +22,7 @@ def _resolve_public_base_url() -> str:
subdomain = ''
if tenant_context:
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: