""" BreakPilot Email Service Ermoeglicht den Versand von Emails via SMTP. Verwendet Mailpit im Entwicklungsmodus. """ import os import smtplib import logging from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.utils import formataddr from typing import Optional, List from dataclasses import dataclass from datetime import datetime logger = logging.getLogger(__name__) # SMTP Konfiguration aus Umgebungsvariablen SMTP_HOST = os.getenv("SMTP_HOST", "localhost") SMTP_PORT = int(os.getenv("SMTP_PORT", "1025")) SMTP_USERNAME = os.getenv("SMTP_USERNAME", "") SMTP_PASSWORD = os.getenv("SMTP_PASSWORD", "") SMTP_FROM_NAME = os.getenv("SMTP_FROM_NAME", "BreakPilot") SMTP_FROM_ADDR = os.getenv("SMTP_FROM_ADDR", "noreply@breakpilot.app") SMTP_USE_TLS = os.getenv("SMTP_USE_TLS", "false").lower() == "true" @dataclass class EmailResult: """Ergebnis eines Email-Versands.""" success: bool message_id: Optional[str] = None error: Optional[str] = None recipient: Optional[str] = None sent_at: Optional[str] = None class EmailService: """Service fuer den Email-Versand.""" def __init__( self, host: str = SMTP_HOST, port: int = SMTP_PORT, username: str = SMTP_USERNAME, password: str = SMTP_PASSWORD, from_name: str = SMTP_FROM_NAME, from_addr: str = SMTP_FROM_ADDR, use_tls: bool = SMTP_USE_TLS ): self.host = host self.port = port self.username = username self.password = password self.from_name = from_name self.from_addr = from_addr self.use_tls = use_tls def _get_connection(self): """Erstellt eine SMTP-Verbindung.""" if self.use_tls: smtp = smtplib.SMTP_SSL(self.host, self.port) else: smtp = smtplib.SMTP(self.host, self.port) if self.username and self.password: smtp.login(self.username, self.password) return smtp def send_email( self, to_email: str, subject: str, body_text: str, body_html: Optional[str] = None, reply_to: Optional[str] = None, cc: Optional[List[str]] = None, bcc: Optional[List[str]] = None ) -> EmailResult: """ Sendet eine Email. Args: to_email: Empfaenger-Email subject: Betreff body_text: Plaintext-Inhalt body_html: Optional HTML-Inhalt reply_to: Optional Reply-To Adresse cc: Optional CC-Empfaenger bcc: Optional BCC-Empfaenger Returns: EmailResult mit Erfolg/Fehler """ try: # Message erstellen if body_html: msg = MIMEMultipart("alternative") msg.attach(MIMEText(body_text, "plain", "utf-8")) msg.attach(MIMEText(body_html, "html", "utf-8")) else: msg = MIMEText(body_text, "plain", "utf-8") msg["Subject"] = subject msg["From"] = formataddr((self.from_name, self.from_addr)) msg["To"] = to_email if reply_to: msg["Reply-To"] = reply_to if cc: msg["Cc"] = ", ".join(cc) # Alle Empfaenger sammeln recipients = [to_email] if cc: recipients.extend(cc) if bcc: recipients.extend(bcc) # Senden with self._get_connection() as smtp: smtp.sendmail(self.from_addr, recipients, msg.as_string()) logger.info(f"Email sent to {to_email}: {subject}") return EmailResult( success=True, recipient=to_email, sent_at=datetime.utcnow().isoformat() ) except smtplib.SMTPException as e: logger.error(f"SMTP error sending to {to_email}: {e}") return EmailResult( success=False, error=f"SMTP Fehler: {str(e)}", recipient=to_email ) except Exception as e: logger.error(f"Error sending email to {to_email}: {e}") return EmailResult( success=False, error=str(e), recipient=to_email ) def send_messenger_notification( self, to_email: str, to_name: str, sender_name: str, message_content: str, reply_link: Optional[str] = None ) -> EmailResult: """ Sendet eine Messenger-Benachrichtigung per Email. Args: to_email: Empfaenger-Email to_name: Name des Empfaengers sender_name: Name des Absenders message_content: Nachrichteninhalt reply_link: Optional Link zum Antworten Returns: EmailResult """ subject = f"Neue Nachricht von {sender_name} - BreakPilot" # Plaintext Version body_text = f"""Hallo {to_name}, Sie haben eine neue Nachricht von {sender_name} erhalten: --- {message_content} --- """ if reply_link: body_text += f"Um zu antworten, klicken Sie hier: {reply_link}\n\n" body_text += """Mit freundlichen Gruessen Ihr BreakPilot Team --- Diese E-Mail wurde automatisch versendet. Bitte antworten Sie nicht direkt auf diese E-Mail. """ # HTML Version body_html = f"""

Neue Nachricht

Hallo {to_name},

Sie haben eine neue Nachricht von {sender_name} erhalten:

{message_content.replace(chr(10), '
')}
""" if reply_link: body_html += f'

Nachricht beantworten

' body_html += """
""" return self.send_email( to_email=to_email, subject=subject, body_text=body_text, body_html=body_html ) def send_jitsi_invitation( self, to_email: str, to_name: str, organizer_name: str, meeting_title: str, meeting_date: str, meeting_time: str, jitsi_url: str, additional_info: Optional[str] = None ) -> EmailResult: """ Sendet eine Jitsi-Meeting-Einladung per Email. Args: to_email: Empfaenger-Email to_name: Name des Empfaengers organizer_name: Name des Organisators meeting_title: Titel des Meetings meeting_date: Datum des Meetings (z.B. "20. Dezember 2024") meeting_time: Uhrzeit des Meetings (z.B. "14:00 Uhr") jitsi_url: Der Jitsi-Meeting-Link additional_info: Optional zusaetzliche Informationen Returns: EmailResult """ subject = f"Einladung: {meeting_title} - {meeting_date}" # Plaintext Version body_text = f"""Hallo {to_name}, {organizer_name} laedt Sie zu einem Videogespraech ein. TERMIN: {meeting_title} DATUM: {meeting_date} UHRZEIT: {meeting_time} Treten Sie dem Meeting bei: {jitsi_url} """ if additional_info: body_text += f"HINWEISE:\n{additional_info}\n\n" body_text += """TECHNISCHE VORAUSSETZUNGEN: - Aktueller Webbrowser (Chrome, Firefox, Safari oder Edge) - Keine Installation erforderlich - Optional: Kopfhoerer fuer bessere Audioqualitaet Bei technischen Problemen wenden Sie sich bitte an den Organisator. Mit freundlichen Gruessen Ihr BreakPilot Team """ # HTML Version body_html = f"""

Einladung zum Videogespraech

{meeting_title}

Hallo {to_name},

{organizer_name} laedt Sie zu einem Videogespraech ein.

Termin: {meeting_title}
Datum: {meeting_date}
Uhrzeit: {meeting_time}
Meeting beitreten """ if additional_info: body_html += f"""

Hinweise:

{additional_info}

""" body_html += """

Technische Voraussetzungen:

  • Aktueller Webbrowser (Chrome, Firefox, Safari oder Edge)
  • Keine Installation erforderlich
  • Optional: Kopfhoerer fuer bessere Audioqualitaet

Bei technischen Problemen wenden Sie sich bitte an den Organisator.

""" return self.send_email( to_email=to_email, subject=subject, body_text=body_text, body_html=body_html ) # Globale Instanz email_service = EmailService()