""" Notification Manager ==================== Phase 6: leWOOOgo Output Plugins 管理所有 NotificationProvider,統一發送介面 """ from src.core.logging import get_logger from .base import ( NotificationMessage, NotificationProvider, NotificationResult, NotificationStatus, ) from .discord import DiscordWebhookProvider logger = get_logger("awoooi.notifications.manager") class NotificationManager: """ 通知管理器 管理多個 NotificationProvider,支援: - 同時發送至多個頻道 - 優雅降級 (單一 Provider 失敗不影響其他) - 結果追蹤 """ def __init__(self): self._providers: list[NotificationProvider] = [] self._initialized = False def register(self, provider: NotificationProvider) -> None: """註冊 Provider""" if provider.enabled: self._providers.append(provider) logger.info( "notification_provider_registered", provider=provider.name, enabled=provider.enabled, ) else: logger.warning( "notification_provider_disabled", provider=provider.name, ) def initialize(self) -> None: """初始化所有 Provider""" if self._initialized: return # 註冊 Discord discord = DiscordWebhookProvider() self.register(discord) # TODO: 註冊其他 Provider # slack = SlackWebhookProvider() # self.register(slack) self._initialized = True logger.info( "notification_manager_initialized", provider_count=len(self._providers), providers=[p.name for p in self._providers], ) async def send_all(self, message: NotificationMessage) -> list[NotificationResult]: """ 發送通知至所有已註冊的 Provider Returns: list[NotificationResult]: 各 Provider 的發送結果 """ if not self._initialized: self.initialize() if not self._providers: logger.warning("no_notification_providers_available") return [ NotificationResult( status=NotificationStatus.SKIPPED, provider="none", message="No notification providers configured", ) ] results = [] for provider in self._providers: try: result = await provider.send(message) results.append(result) logger.info( "notification_sent", provider=provider.name, status=result.status.value, ) except Exception as e: logger.exception( "notification_send_failed", provider=provider.name, error=str(e), ) results.append( NotificationResult( status=NotificationStatus.FAILED, provider=provider.name, message="Exception during send", error=str(e), ) ) return results async def test_all(self) -> dict[str, bool]: """ 測試所有 Provider 連線 Returns: dict[str, bool]: Provider 名稱 → 連線狀態 """ if not self._initialized: self.initialize() results = {} for provider in self._providers: try: results[provider.name] = await provider.test_connection() except Exception as e: logger.error( "notification_test_failed", provider=provider.name, error=str(e), ) results[provider.name] = False return results async def close(self) -> None: """關閉所有 Provider""" for provider in self._providers: if hasattr(provider, "close"): await provider.close() # ============================================================================= # Singleton Instance # ============================================================================= _notification_manager: NotificationManager | None = None def get_notification_manager() -> NotificationManager: """取得 NotificationManager 單例""" global _notification_manager if _notification_manager is None: _notification_manager = NotificationManager() _notification_manager.initialize() return _notification_manager async def close_notification_manager() -> None: """關閉 NotificationManager""" global _notification_manager if _notification_manager: await _notification_manager.close() _notification_manager = None