Files
Estrella Pan f2d93993f4 fix(plugins): apprise 的 urls 改为单个字符串秘密字段,回退 secret_field 的 default_factory
宿主只按位置还原列表型秘密的掩码,删除首个 URL 会保留错误的值;
插件注册表 CI 用基线分支的 ab_sdk 校验,插件不能依赖同一 PR 中的 SDK 改动。
2026-10-21 11:28:07 +02:00

94 lines
3.2 KiB
Python
Raw Permalink 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.
"""Apprise 通知渠道。
不依赖 apprise 库,而是调用用户自建的 Apprise API 服务器(caronc/apprise 镜像):
- 填了 ``urls``:无状态调用 ``POST {server}/notify/``,请求体带上 Apprise URL;
- 否则用 ``config_key``:``POST {server}/notify/{config_key}``,URL 存在服务器上。
HTTP 2xx 即成功。接口文档:https://github.com/caronc/apprise-api
"""
from collections.abc import Callable
from typing import Any
import httpx2
from pydantic import BaseModel, Field
from ab_sdk import Plugin, points, provider, secret_field
from ab_sdk.notify import NotificationMessage, NotifierFactory, NotifierSettings
class Options(BaseModel):
server: str = Field(
"http://apprise:8000", title="服务器", description="Apprise API 服务器地址"
)
# URL 里带着令牌,按秘密字段掩码
urls: str = secret_field(
title="Apprise URL",
description="多个 Apprise URL 用逗号或换行分隔",
)
config_key: str = Field(
"",
title="配置键",
description="未填 Apprise URL 时,使用服务器上保存的这份配置",
)
class Channel:
def __init__(
self,
options: Options,
settings: NotifierSettings,
client_factory: Callable[[], httpx2.AsyncClient],
) -> None:
self.options = options
self.settings = settings
self.client_factory = client_factory
async def _post(self, message: NotificationMessage) -> tuple[bool, str]:
opts = self.options
server = opts.server.rstrip("/")
payload: dict[str, Any] = {"title": message.title, "body": message.body}
urls = [u.strip() for u in opts.urls.replace("\n", ",").split(",")]
urls = [u for u in urls if u]
if urls:
url = f"{server}/notify/"
# Apprise 接受逗号分隔的 URL 字符串
payload["urls"] = ",".join(urls)
elif opts.config_key:
url = f"{server}/notify/{opts.config_key}"
else:
return False, "需填写 Apprise URL 或配置键"
try:
async with self.client_factory() as client:
resp = await client.post(url, json=payload)
except httpx2.HTTPError as e:
return False, f"请求失败:{e}"
if resp.is_success:
return True, "ok"
return False, f"Apprise 返回 HTTP {resp.status_code}:{resp.text[:200]}"
async def send(self, message: NotificationMessage) -> bool:
ok, _ = await self._post(message)
return ok
async def test(self) -> tuple[bool, str]:
return await self._post(
NotificationMessage(
kind="event", title="AutoBangumi 通知测试", body="通知测试成功!"
)
)
class AppriseNotifier(Plugin[Options]):
config_model = Options
# 测试可替换为带 MockTransport 的客户端
client_factory: Callable[[], httpx2.AsyncClient] = staticmethod(
lambda: httpx2.AsyncClient(timeout=10)
)
@provider(points.NOTIFIER, id="apprise")
def channel(self) -> NotifierFactory:
return lambda settings: Channel(self.config, settings, self.client_factory)