Files
awoooi/apps/api/src/services/notifications/manager.py
OG T 6f049877fc fix(lint): ruff auto-fix + lewooogo-core src 加入 git
- Python: ruff --fix 修復 280 個 lint 錯誤
- lewooogo-core: src/ 目錄未追蹤,導致 CI eslint 失敗

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-23 23:51:37 +08:00

171 lines
4.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
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