""" Email Action für Alerts Agent. Sendet E-Mail-Benachrichtigungen für Alerts. """ import logging from typing import Dict, Any, List from datetime import datetime from .base import ActionHandler, ActionResult, ActionType, AlertContext logger = logging.getLogger(__name__) # HTML-Template für Alert-E-Mails EMAIL_TEMPLATE = """

BreakPilot Alert

{topic_name}

{snippet}
{decision_badge} {score_display} {rule_display}
""" class EmailAction(ActionHandler): """ E-Mail-Benachrichtigungen für Alerts. Konfiguration: - to: E-Mail-Adresse(n) des Empfängers - subject_prefix: Optionaler Betreff-Prefix - include_snippet: Snippet einbinden (default: true) """ @property def action_type(self) -> ActionType: return ActionType.EMAIL def get_required_config_fields(self) -> List[str]: return ["to"] def validate_config(self, config: Dict[str, Any]) -> bool: to = config.get("to") if not to: return False if isinstance(to, str): return "@" in to if isinstance(to, list): return all("@" in email for email in to) return False async def execute( self, context: AlertContext, config: Dict[str, Any], ) -> ActionResult: """ Sendet eine E-Mail-Benachrichtigung. Args: context: Alert-Kontext config: E-Mail-Konfiguration (to, subject_prefix, etc.) Returns: ActionResult """ try: # Empfänger to = config.get("to") if isinstance(to, str): recipients = [to] else: recipients = to # Betreff subject_prefix = config.get("subject_prefix", "[BreakPilot Alert]") subject = f"{subject_prefix} {context.title[:50]}" # HTML-Body generieren html_body = self._render_email(context, config) # E-Mail senden sent = await self._send_email( recipients=recipients, subject=subject, html_body=html_body, ) if sent: return ActionResult( success=True, action_type=self.action_type, message=f"E-Mail an {len(recipients)} Empfänger gesendet", details={"recipients": recipients, "subject": subject}, ) else: return ActionResult( success=False, action_type=self.action_type, message="E-Mail konnte nicht gesendet werden", error="SMTP-Fehler", ) except Exception as e: logger.error(f"Email action error: {e}") return ActionResult( success=False, action_type=self.action_type, message="E-Mail-Fehler", error=str(e), ) def _render_email( self, context: AlertContext, config: Dict[str, Any], ) -> str: """Rendert das E-Mail-Template.""" # Decision Badge decision_badge = "" if context.relevance_decision: badge_class = "badge-keep" if context.relevance_decision == "KEEP" else "badge-review" decision_badge = f'{context.relevance_decision}' # Score score_display = "" if context.relevance_score is not None: score_display = f' | Score: {context.relevance_score:.0%}' # Matched Rule rule_display = "" if context.matched_rule: rule_display = f' | Regel: {context.matched_rule}' # Snippet snippet = context.snippet[:200] if context.snippet else "" if config.get("include_snippet", True) is False: snippet = "" # Dashboard URL dashboard_url = config.get("dashboard_url", "http://localhost:8000/studio#alerts") return EMAIL_TEMPLATE.format( topic_name=context.topic_name, title=context.title, url=context.url, snippet=snippet, decision_badge=decision_badge, score_display=score_display, rule_display=rule_display, dashboard_url=dashboard_url, ) async def _send_email( self, recipients: List[str], subject: str, html_body: str, ) -> bool: """ Sendet die E-Mail über SMTP. Verwendet aiosmtplib für async SMTP. """ import os smtp_host = os.getenv("SMTP_HOST", "localhost") smtp_port = int(os.getenv("SMTP_PORT", "587")) smtp_user = os.getenv("SMTP_USER", "") smtp_pass = os.getenv("SMTP_PASS", "") smtp_from = os.getenv("SMTP_FROM", "alerts@breakpilot.de") try: import aiosmtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart # E-Mail erstellen msg = MIMEMultipart("alternative") msg["Subject"] = subject msg["From"] = smtp_from msg["To"] = ", ".join(recipients) # HTML-Teil html_part = MIMEText(html_body, "html", "utf-8") msg.attach(html_part) # Senden await aiosmtplib.send( msg, hostname=smtp_host, port=smtp_port, username=smtp_user if smtp_user else None, password=smtp_pass if smtp_pass else None, start_tls=True if smtp_port == 587 else False, ) logger.info(f"Email sent to {recipients}") return True except ImportError: logger.warning("aiosmtplib not installed. Email not sent.") # Im Dev-Modus: Erfolg simulieren logger.info(f"[DEV] Would send email to {recipients}: {subject}") return True except Exception as e: logger.error(f"SMTP error: {e}") return False