""" Notification Provider Base Interface ===================================== Phase 6: leWOOOgo Output Plugins 設計原則: 1. 抽象介面 - 所有 Provider 必須實作 send() 2. 統一訊息格式 - NotificationMessage 3. 結果追蹤 - NotificationResult """ from abc import ABC, abstractmethod from dataclasses import dataclass, field from datetime import datetime from enum import Enum from typing import Any from src.utils.timezone import now_taipei class NotificationStatus(str, Enum): """通知狀態""" SUCCESS = "success" FAILED = "failed" SKIPPED = "skipped" class ExecutionStatus(str, Enum): """執行狀態""" SUCCESS = "success" FAILED = "failed" DRY_RUN_BLOCKED = "dry_run_blocked" PENDING = "pending" @dataclass class NotificationMessage: """ 通知訊息統一格式 所有 Provider 都從這個格式轉換成各自的 API 格式 """ # 執行結果 execution_status: ExecutionStatus # 核心資訊 action_title: str action_description: str approval_id: str # 簽核資訊 signers: list[dict[str, str]] = field(default_factory=list) # [{"name": "CTO", "comment": "..."}] required_signatures: int = 1 # 影響範圍 (Blast Radius) affected_pods: int = 0 estimated_downtime: str = "N/A" related_services: list[str] = field(default_factory=list) data_impact: str = "none" # 執行細節 namespace: str = "default" operation_type: str = "unknown" duration_ms: int | None = None error_message: str | None = None # AI 分析 risk_level: str = "medium" ai_provider: str = "unknown" confidence: float | None = None # 時間戳 timestamp: datetime = field(default_factory=lambda: now_taipei()) @property def status_emoji(self) -> str: """狀態 Emoji""" if self.execution_status == ExecutionStatus.SUCCESS: return "✅" elif self.execution_status == ExecutionStatus.FAILED: return "❌" elif self.execution_status == ExecutionStatus.DRY_RUN_BLOCKED: return "🛡️" return "⏳" @property def status_text(self) -> str: """狀態文字""" if self.execution_status == ExecutionStatus.SUCCESS: return "任務執行成功" elif self.execution_status == ExecutionStatus.FAILED: return "執行失敗" elif self.execution_status == ExecutionStatus.DRY_RUN_BLOCKED: return "Dry-Run 攔截" return "等待中" @property def risk_emoji(self) -> str: """風險等級 Emoji""" if self.risk_level == "critical": return "🔴" elif self.risk_level == "medium": return "🟡" return "🟢" @property def signers_display(self) -> str: """簽核者顯示文字""" if not self.signers: return "無" return ", ".join([s.get("name", "Unknown") for s in self.signers]) @dataclass class NotificationResult: """通知發送結果""" status: NotificationStatus provider: str message: str response_data: dict[str, Any] | None = None error: str | None = None timestamp: datetime = field(default_factory=lambda: now_taipei()) class NotificationProvider(ABC): """ 通知提供者抽象介面 所有 Output Plugin 必須實作此介面 """ @property @abstractmethod def name(self) -> str: """Provider 名稱""" pass @property @abstractmethod def enabled(self) -> bool: """是否啟用""" pass @abstractmethod async def send(self, message: NotificationMessage) -> NotificationResult: """ 發送通知 Args: message: 統一格式的通知訊息 Returns: NotificationResult: 發送結果 """ pass @abstractmethod async def test_connection(self) -> bool: """ 測試連線 Returns: bool: 是否連線成功 """ pass