feat(notification): redesign system to support multiple providers

- Add NotificationProvider base class with send() and test() methods
- Add NotificationManager for handling multiple providers simultaneously
- Add new providers: Discord, Gotify, Pushover, generic Webhook
- Migrate existing providers (Telegram, Bark, Server Chan, WeChat Work) to new architecture
- Add API endpoints for testing providers (/notification/test, /notification/test-config)
- Auto-migrate legacy single-provider configs to new multi-provider format
- Update WebUI with card-based multi-provider settings UI
- Add test button for each provider in settings
- Generic webhook supports template variables: {{title}}, {{season}}, {{episode}}, {{poster_url}}

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Estrella Pan
2026-01-28 20:58:42 +01:00
parent 5eb21bfcfa
commit 48bf570697
25 changed files with 1865 additions and 190 deletions

View File

@@ -8,7 +8,8 @@ from pydantic import BaseModel, Field
from module.conf import VERSION, settings
from module.models import Config, ResponseModel
from module.network import RequestContent
from module.notification.notification import getClient
from module.notification import PROVIDER_REGISTRY
from module.models.config import NotificationProvider as ProviderConfig
from module.security.jwt import get_password_hash
logger = logging.getLogger(__name__)
@@ -202,8 +203,8 @@ async def test_notification(req: TestNotificationRequest):
"""Send a test notification."""
_require_setup_needed()
NotifierClass = getClient(req.type)
if NotifierClass is None:
provider_cls = PROVIDER_REGISTRY.get(req.type.lower())
if provider_cls is None:
return TestResultResponse(
success=False,
message_en=f"Unknown notification type: {req.type}",
@@ -211,30 +212,27 @@ async def test_notification(req: TestNotificationRequest):
)
try:
notifier = NotifierClass(token=req.token, chat_id=req.chat_id)
async with notifier:
# Send a simple test message
data = {"chat_id": req.chat_id, "text": "AutoBangumi 通知测试成功!"}
if req.type.lower() == "telegram":
resp = await notifier.post_data(notifier.message_url, data)
if resp.status_code == 200:
return TestResultResponse(
success=True,
message_en="Test notification sent successfully.",
message_zh="测试通知发送成功。",
)
else:
return TestResultResponse(
success=False,
message_en="Failed to send test notification.",
message_zh="测试通知发送失败。",
)
else:
# For other providers, just verify the notifier can be created
# Create provider config
config = ProviderConfig(
type=req.type,
enabled=True,
token=req.token,
chat_id=req.chat_id,
)
provider = provider_cls(config)
async with provider:
success, message = await provider.test()
if success:
return TestResultResponse(
success=True,
message_en="Notification configuration is valid.",
message_zh="通知配置有效",
message_en="Test notification sent successfully.",
message_zh="测试通知发送成功",
)
else:
return TestResultResponse(
success=False,
message_en=f"Failed to send test notification: {message}",
message_zh=f"测试通知发送失败:{message}",
)
except Exception as e:
logger.error(f"[Setup] Notification test failed: {e}")
@@ -275,9 +273,14 @@ async def complete_setup(req: SetupCompleteRequest):
if req.notification_enable:
config_dict["notification"] = {
"enable": True,
"type": req.notification_type,
"token": req.notification_token,
"chat_id": req.notification_chat_id,
"providers": [
{
"type": req.notification_type,
"enabled": True,
"token": req.notification_token,
"chat_id": req.notification_chat_id,
}
],
}
settings.save(config_dict)