# /// # plugin_notification_service.py # 描述:插件通知服务,支持向配置的地址发送插件告警通知 # 作者:User # 创建日期:2026-04-07 # /// import logging import httpx from typing import Any, Dict, List, Optional from datetime import datetime logger = logging.getLogger("plugin_notification") class PluginNotificationService: def __init__(self): self._notification_url: Optional[str] = None self._enabled: bool = False self._webhook_configs: List[Dict[str, Any]] = [] self._plugin_webhook_map: Dict[str, str] = {} def initialize(self): from database_service import get_db_service db_service = get_db_service() if not db_service: logger.warning("Database service not available, plugin notification disabled") return try: self._load_notification_config(db_service) self._load_webhook_configs(db_service) except Exception as e: logger.error(f"Failed to initialize plugin notification service: {e}") def _load_notification_config(self, db_service): try: from models import WebhookUrl webhooks = db_service.get_all_webhooks() for wh in webhooks: event_types = getattr(wh, 'event_types', '') or '' if 'plugin_notification' in event_types or event_types == '*': self._notification_url = wh.url self._enabled = wh.enabled logger.info(f"Plugin notification enabled: {wh.name} - {wh.url}") break except Exception as e: logger.warning(f"Failed to load notification config: {e}") def _load_webhook_configs(self, db_service): try: from models import WebhookUrl webhooks = db_service.get_all_webhooks() self._webhook_configs = [] for wh in webhooks: event_types = getattr(wh, 'event_types', '') or '' event_types_list = event_types.split(',') if isinstance(event_types, str) else [] self._webhook_configs.append({ 'id': wh.id, 'name': wh.name, 'url': wh.url, 'format': getattr(wh, 'format', 'bark'), 'enabled': wh.enabled, 'event_types': event_types_list }) if 'plugin_notification' in event_types_list or '*' in event_types_list: self._notification_url = wh.url self._enabled = wh.enabled except Exception as e: logger.warning(f"Failed to load webhook configs: {e}") def reload_config(self): self.initialize() def send_notification( self, plugin_name: str, title: str, message: str, level: str = "info" ) -> bool: logger.info(f"send_notification called: {plugin_name} - {title}, enabled={self._enabled}, url={self._notification_url}") if not self._enabled or not self._notification_url: logger.warning(f"Plugin notification disabled or no URL: enabled={self._enabled}, url={self._notification_url}") logger.info(f"Available webhook configs: {self._webhook_configs}") return False timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") content = f"""【{plugin_name} 插件通知】 级别: {level.upper()} 标题: {title} 消息: {message} 时间: {timestamp}""" payload = { "title": f"[{level.upper()}] {plugin_name}", "body": content, "icon": "https://is3-ssl.mzstatic.com/image/thumb/Purple122/v4/8e/1a/b5/8e1ab5a2-09c7-2c06-ae03-2056b0348d2c/AppIcon-Production-20x20@2x.png/0x0ss.png" } for config in self._webhook_configs: if not config['enabled']: continue event_types = config['event_types'] if 'plugin_notification' not in event_types and '*' not in event_types: continue try: url = config['url'] format_type = config.get('format', 'bark') if format_type == 'wechat': payload = { "msgtype": "text", "text": { "content": content } } result = self._send_webhook(url, payload) if result: logger.info(f"Plugin notification sent: {plugin_name} - {level} -> {config['name']}") return result except Exception as e: logger.error(f"Failed to send plugin notification: {e}") return False def _send_webhook(self, url: str, payload: Dict[str, Any]) -> bool: try: with httpx.Client(timeout=10) as client: response = client.post(url, json=payload) return response.status_code == 200 except Exception as e: logger.error(f"Webhook request failed: {e}") return False def is_enabled(self) -> bool: return self._enabled def get_notification_url(self) -> Optional[str]: return self._notification_url plugin_notification_service = PluginNotificationService()