148 lines
5.3 KiB
Python
148 lines
5.3 KiB
Python
# ///
|
|
# 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() |