From 320acb525663766e1f218ae68ba493d87982150c Mon Sep 17 00:00:00 2001 From: AIIrondev Date: Thu, 17 Sep 2026 19:36:48 +0200 Subject: [PATCH] changes to the email sending --- Web/app.py | 8 +- Web/modules/emailservice/email.py | 117 ++++++++++++++++-------------- 2 files changed, 67 insertions(+), 58 deletions(-) diff --git a/Web/app.py b/Web/app.py index 29e3d45..1c45b66 100755 --- a/Web/app.py +++ b/Web/app.py @@ -48,7 +48,7 @@ import Web.modules.log.audit_log as al import push_notifications as pn import Web.modules.inventarsystem.pdf_export as pdf_export import Web.modules.inventarsystem.excel_export as excel_export -from Web.modules.emailservice.email import send, send_pdf +from Web.modules.emailservice.email import send import datetime from apscheduler.schedulers.background import BackgroundScheduler from bson.objectid import ObjectId, InvalidId @@ -5543,7 +5543,7 @@ def student_card_single_barcode_download(card_id): if not cfg.MODULES.is_enabled('mail'): flash('Das E-Mail-Add-on ist deaktiviert. Der Ausweis kann weiterhin als PDF heruntergeladen werden.', 'warning') return redirect(url_for('student_cards_admin')) - sent = send_pdf( + sent = send( recipient_email, f'Bibliotheksausweis {card["AusweisId"]}', f'Anbei erhalten Sie den Bibliotheksausweis für {card.get("SchülerName", "")} als PDF.', @@ -9153,7 +9153,7 @@ def admin_audit_export_pdf_official(): flash('Das E-Mail-Add-on ist deaktiviert. Der Auditbericht kann weiterhin als PDF heruntergeladen werden.', 'warning') return redirect(url_for('admin_audit_dashboard')) filename = f'audit-official-report-{datetime.datetime.now(ZoneInfo("Europe/Berlin")).strftime("%Y%m%d-%H%M%S")}.pdf' - sent = send_pdf( + sent = send( recipient_email, 'Amtlicher Auditbericht', 'Anbei erhalten Sie den aktuellen amtlichen Auditbericht des Inventarsystems als PDF.', @@ -9549,7 +9549,7 @@ def admin_create_invoice(borrow_id): if not cfg.MODULES.is_enabled('mail'): flash('Das E-Mail-Add-on ist deaktiviert. Die PDF-Rechnung kann weiterhin heruntergeladen werden.', 'warning') return redirect(url_for('admin_borrowings')) - sent = send_pdf( + sent = send( recipient_email, f'Rechnung {invoice_number} - {item_name}', f'Anbei erhalten Sie die Rechnung {invoice_number} zum Element {item_name}.', diff --git a/Web/modules/emailservice/email.py b/Web/modules/emailservice/email.py index 53322e0..892fb36 100644 --- a/Web/modules/emailservice/email.py +++ b/Web/modules/emailservice/email.py @@ -3,7 +3,7 @@ from email.mime.text import MIMEText from email.mime.application import MIMEApplication import smtplib import time - +import os import Web.modules.database.settings as cfg @@ -28,70 +28,89 @@ def _normalize_recipients(email: list | str) -> list[str]: return [str(recipient).strip() for recipient in (email or []) if str(recipient).strip()] -def _send_message(email: list | str, subject: str, note: str, sender: str, attachment=None) -> bool: - """Send a plain/HTML message, optionally with one PDF attachment.""" - if not cfg.MODULES.is_enabled("mail"): - print("Debug: Module not enabled") - return False +def send( + email: list | str, + subject: str, + text_body: str, + html_body: str = None, + attachments: list = None +) -> bool: + """Sends the email with Plain Text, HTML, and optional File Attachments support.""" + + if isinstance(email, str): + email = [email] - recipients = _normalize_recipients(email) - if not recipients: - return False + if attachments is None: + attachments = [] - body_message = note + TEXT_SIGNATURE = ( + "\n\n--\n" + "Mit freundlichen Grüßen\n" + "Ihr Invario Team\n\n" + "Invario UG\n" + "Am Sportplatz 10\n" + "83052 Bruckmühl" + ) - HTML_SIGNATURE = f""" - + HTML_SIGNATURE = """ +

+
-

Mit freundlichen Grüßen

-

Automatisierter Email Verteiler für die Schule: {cfg.get_school_info().get("name")}


-

Invario UG
Am Sportplatz 10
83052 Bruckmühl

+

Mit freundlichen Grüßen,

+

Ihr Invario Team


+

+ Invario UG
+ Am Sportplatz 10
+ 83052 Bruckmühl +

""" - text_content = f"{body_message}\n\nMit freundlichen Grüßen\n{sender}\n" - - html_content = f""" - - -

{body_message}

-
- {HTML_SIGNATURE} - - - """ + text_content = f"{text_body}{TEXT_SIGNATURE}" + + if html_body: + html_content = f"
{html_body}{HTML_SIGNATURE}
" + else: + # Fallback if only text is provided + html_safe_text = text_body.replace('\n', '
') + html_content = f"

{html_safe_text}

{HTML_SIGNATURE}" mails_per_second = 10 interval = 1.0 / mails_per_second - smtp = None + try: smtp = _build_smtp_client() - for i, recipient in enumerate(recipients): + for i, recipient in enumerate(email): start_time = time.time() - msg = MIMEMultipart("mixed" if attachment else "alternative") - msg["Subject"] = str(subject) - from_address = "no-reply@invario-software.de" - msg["From"] = f"{sender} <{from_address}>" + # Root message structure for email with attachments + msg = MIMEMultipart("mixed") + msg["Subject"] = subject + msg["From"] = "Invario Team " msg["To"] = str(recipient) - body = MIMEMultipart("alternative") if attachment else msg - body.attach(MIMEText(text_content, "plain")) - body.attach(MIMEText(html_content, "html")) - if attachment: - msg.attach(body) - attachment_payload, filename = attachment - pdf_part = MIMEApplication(attachment_payload, _subtype="pdf") - pdf_part.add_header("Content-Disposition", "attachment", filename=filename) - msg.attach(pdf_part) + # Sub-container for Plain Text and HTML bodies + msg_body = MIMEMultipart("alternative") + msg_body.attach(MIMEText(text_content, "plain")) + msg_body.attach(MIMEText(html_content, "html")) + msg.attach(msg_body) + + # Attach files if provided + for file_path in attachments: + if file_path and os.path.isfile(file_path): + filename = os.path.basename(file_path) + with open(file_path, "rb") as file: + part = MIMEApplication(file.read(), Name=filename) + part['Content-Disposition'] = f'attachment; filename="{filename}"' + msg.attach(part) smtp.sendmail( - from_addr=from_address, + from_addr="no-reply@invario-software.de", to_addrs=[recipient], msg=msg.as_string() ) @@ -99,7 +118,7 @@ def _send_message(email: list | str, subject: str, note: str, sender: str, attac elapsed_time = time.time() - start_time sleep_time = interval - elapsed_time - if sleep_time > 0 and i < len(recipients) - 1: + if sleep_time > 0 and i < len(email) - 1: time.sleep(sleep_time) return True @@ -111,14 +130,4 @@ def _send_message(email: list | str, subject: str, note: str, sender: str, attac try: smtp.quit() except Exception: - pass - - -def send(email: list | str, subject: str, note: str, sender: str) -> bool: - """Send an HTML/plain email without an attachment.""" - return _send_message(email, subject, note, sender) - - -def send_pdf(email: list | str, subject: str, note: str, sender: str, pdf_bytes: bytes, filename: str) -> bool: - """Send an email with a PDF attachment.""" - return _send_message(email, subject, note, sender, attachment=(pdf_bytes, filename)) \ No newline at end of file + pass \ No newline at end of file