Services: Admin-Lehrer, Backend-Lehrer, Studio v2, Website, Klausur-Service, School-Service, Voice-Service, Geo-Service, BreakPilot Drive, Agent-Core Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
233 lines
6.4 KiB
Python
233 lines
6.4 KiB
Python
"""
|
|
Action Dispatcher für Alerts Agent.
|
|
|
|
Verteilt Aktionen an die entsprechenden Handler.
|
|
"""
|
|
import logging
|
|
from typing import Dict, Any, List, Optional
|
|
from datetime import datetime
|
|
|
|
from .base import ActionHandler, ActionResult, ActionType, AlertContext
|
|
from .email_action import EmailAction
|
|
from .webhook_action import WebhookAction
|
|
from .slack_action import SlackAction
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class ActionDispatcher:
|
|
"""
|
|
Zentrale Verteilung von Aktionen an Handler.
|
|
|
|
Registriert Handler für verschiedene Aktionstypen und
|
|
führt Aktionen basierend auf Regel-Konfigurationen aus.
|
|
"""
|
|
|
|
def __init__(self):
|
|
"""Initialisiert den Dispatcher mit Standard-Handlern."""
|
|
self._handlers: Dict[ActionType, ActionHandler] = {}
|
|
|
|
# Standard-Handler registrieren
|
|
self.register_handler(EmailAction())
|
|
self.register_handler(WebhookAction())
|
|
self.register_handler(SlackAction())
|
|
|
|
def register_handler(self, handler: ActionHandler) -> None:
|
|
"""
|
|
Registriert einen Action-Handler.
|
|
|
|
Args:
|
|
handler: Handler-Instanz
|
|
"""
|
|
self._handlers[handler.action_type] = handler
|
|
logger.debug(f"Registered action handler: {handler.action_type.value}")
|
|
|
|
def get_handler(self, action_type: ActionType) -> Optional[ActionHandler]:
|
|
"""
|
|
Gibt den Handler für einen Aktionstyp zurück.
|
|
|
|
Args:
|
|
action_type: Aktionstyp
|
|
|
|
Returns:
|
|
Handler oder None
|
|
"""
|
|
return self._handlers.get(action_type)
|
|
|
|
def list_handlers(self) -> List[str]:
|
|
"""Gibt Liste der registrierten Handler zurück."""
|
|
return [at.value for at in self._handlers.keys()]
|
|
|
|
async def dispatch(
|
|
self,
|
|
action_type: str,
|
|
context: AlertContext,
|
|
config: Dict[str, Any],
|
|
) -> ActionResult:
|
|
"""
|
|
Führt eine Aktion aus.
|
|
|
|
Args:
|
|
action_type: Aktionstyp als String (email, webhook, slack)
|
|
context: Alert-Kontext
|
|
config: Aktionsspezifische Konfiguration
|
|
|
|
Returns:
|
|
ActionResult
|
|
"""
|
|
try:
|
|
# ActionType aus String
|
|
at = ActionType(action_type.lower())
|
|
except ValueError:
|
|
return ActionResult(
|
|
success=False,
|
|
action_type=ActionType.WEBHOOK, # Fallback
|
|
message=f"Unbekannter Aktionstyp: {action_type}",
|
|
error="Unknown action type",
|
|
)
|
|
|
|
handler = self.get_handler(at)
|
|
|
|
if not handler:
|
|
return ActionResult(
|
|
success=False,
|
|
action_type=at,
|
|
message=f"Kein Handler für {action_type} registriert",
|
|
error="No handler registered",
|
|
)
|
|
|
|
# Konfiguration validieren
|
|
if not handler.validate_config(config):
|
|
required = handler.get_required_config_fields()
|
|
return ActionResult(
|
|
success=False,
|
|
action_type=at,
|
|
message=f"Ungültige Konfiguration für {action_type}",
|
|
error=f"Required fields: {required}",
|
|
)
|
|
|
|
# Aktion ausführen
|
|
logger.info(f"Dispatching {action_type} action for alert {context.alert_id[:8]}")
|
|
result = await handler.execute(context, config)
|
|
|
|
return result
|
|
|
|
async def dispatch_multiple(
|
|
self,
|
|
actions: List[Dict[str, Any]],
|
|
context: AlertContext,
|
|
) -> List[ActionResult]:
|
|
"""
|
|
Führt mehrere Aktionen aus.
|
|
|
|
Args:
|
|
actions: Liste von Aktionen [{type, config}, ...]
|
|
context: Alert-Kontext
|
|
|
|
Returns:
|
|
Liste von ActionResults
|
|
"""
|
|
results = []
|
|
|
|
for action in actions:
|
|
action_type = action.get("type", action.get("action_type", ""))
|
|
config = action.get("config", action.get("action_config", {}))
|
|
|
|
result = await self.dispatch(action_type, context, config)
|
|
results.append(result)
|
|
|
|
return results
|
|
|
|
|
|
# Singleton-Instanz
|
|
_dispatcher: Optional[ActionDispatcher] = None
|
|
|
|
|
|
def get_dispatcher() -> ActionDispatcher:
|
|
"""Gibt den globalen ActionDispatcher zurück."""
|
|
global _dispatcher
|
|
if _dispatcher is None:
|
|
_dispatcher = ActionDispatcher()
|
|
return _dispatcher
|
|
|
|
|
|
async def execute_action(
|
|
action_type: str,
|
|
alert_id: str,
|
|
title: str,
|
|
url: str,
|
|
snippet: str,
|
|
topic_name: str,
|
|
config: Dict[str, Any],
|
|
relevance_score: Optional[float] = None,
|
|
relevance_decision: Optional[str] = None,
|
|
matched_rule: Optional[str] = None,
|
|
tags: Optional[List[str]] = None,
|
|
) -> ActionResult:
|
|
"""
|
|
Convenience-Funktion zum Ausführen einer Aktion.
|
|
|
|
Erstellt den Kontext und ruft den Dispatcher auf.
|
|
"""
|
|
context = AlertContext(
|
|
alert_id=alert_id,
|
|
title=title,
|
|
url=url,
|
|
snippet=snippet,
|
|
topic_name=topic_name,
|
|
relevance_score=relevance_score,
|
|
relevance_decision=relevance_decision,
|
|
matched_rule=matched_rule,
|
|
tags=tags or [],
|
|
)
|
|
|
|
dispatcher = get_dispatcher()
|
|
return await dispatcher.dispatch(action_type, context, config)
|
|
|
|
|
|
async def execute_rule_actions(
|
|
alert_id: str,
|
|
title: str,
|
|
url: str,
|
|
snippet: str,
|
|
topic_name: str,
|
|
rule_action: str,
|
|
rule_config: Dict[str, Any],
|
|
rule_name: str,
|
|
) -> ActionResult:
|
|
"""
|
|
Führt die Aktion einer gematschten Regel aus.
|
|
|
|
Args:
|
|
alert_id: Alert-ID
|
|
title: Alert-Titel
|
|
url: Alert-URL
|
|
snippet: Alert-Snippet
|
|
topic_name: Topic-Name
|
|
rule_action: Aktionstyp der Regel
|
|
rule_config: Aktions-Konfiguration
|
|
rule_name: Name der Regel
|
|
|
|
Returns:
|
|
ActionResult
|
|
"""
|
|
# Nur externe Aktionen (email, webhook, slack) hier behandeln
|
|
# keep/drop/tag werden direkt von der Rule Engine behandelt
|
|
if rule_action not in ["email", "webhook", "slack"]:
|
|
return ActionResult(
|
|
success=True,
|
|
action_type=ActionType.TAG, # Dummy
|
|
message=f"Interne Aktion {rule_action} von Rule Engine behandelt",
|
|
)
|
|
|
|
return await execute_action(
|
|
action_type=rule_action,
|
|
alert_id=alert_id,
|
|
title=title,
|
|
url=url,
|
|
snippet=snippet,
|
|
topic_name=topic_name,
|
|
config=rule_config,
|
|
matched_rule=rule_name,
|
|
)
|