Files
MoviePilot/app/modules/webpush/__init__.py
freeman f3ad81b39e style: apply safe ruff auto-fixes and exempt startup late imports
Run `ruff check app tests scripts --fix` with the configured rule set
(E4/E7/E9/F/I): import sorting (I001), f-strings without placeholders
(F541), lambda/variable/unused-import cleanups that ruff marks safe.
Nine files whose only ruff debt sits next to pre-existing pylint
import-resolution errors (lazy exports, sys.path script imports) or
whose "unused" imports are reached through attribute access by tests are
left untouched so the changed-file pylint gate and the benchmark tests
stay green; their diagnostics remain in the baseline.

app/startup/lifecycle/__init__.py and app/main.py import business
modules after cache composition, import-path and stdio setup on purpose
(the source comments document the contract), so declare E402
per-file-ignores for them instead of adding per-line noqa.

Ruff ratchet watermark drops from 498 to 100; sync the baseline fixture
and the optimization checklist figure.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-09 13:29:09 +08:00

120 lines
4.4 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.
import json
from typing import Any, Dict, Tuple, Union
from app.adapters.network.webpush import WebPushDeliveryError, send_webpush
from app.modules import _MessageBase, _ModuleBase
from app.runtime.log import logger
from app.runtime.settings import get_runtime_setting
from app.runtime.webpush import webpush_registry
from app.schemas.message import Message
from app.schemas.types import ModuleType, NotificationChannel
class WebPushModule(_ModuleBase, _MessageBase):
"""通过浏览器 Web Push 通道发送通知消息。"""
def init_module(self) -> None:
"""
初始化模块
"""
super().init_service(service_name=self.get_name().lower())
self._channel = NotificationChannel.WebPush
@staticmethod
def get_name() -> str:
return "WebPush"
@staticmethod
def get_type() -> ModuleType:
"""
获取模块类型
"""
return ModuleType.Notification
@staticmethod
def get_subtype() -> NotificationChannel:
"""
获取模块子类型
"""
return NotificationChannel.WebPush
@staticmethod
def get_priority() -> int:
"""
获取模块优先级,数字越小优先级越高,只有同一接口下优先级才生效
"""
return 6
def stop(self):
"""Web Push 模块没有需要释放的长连接资源。"""
pass
def test(self) -> Tuple[bool, str]:
"""
测试模块连接性
"""
return True, ""
def init_setting(self) -> Tuple[str, Union[str, bool]]:
"""Web Push 使用全局 VAPID 配置,不提供模块级设置。"""
pass
def post_message(self, message: Message, **kwargs) -> None:
"""
发送消息
:param message: 消息内容
:return: 成功或失败
"""
for conf in self.get_configs().values():
if not self.check_message(message, conf.name):
continue
webpush_users = conf.config.get("WEBPUSH_USERNAME") or ""
if webpush_users:
# 设定了接收用户时,非该用户的消息不接收
if not message.username or message.username not in webpush_users.split(","):
continue
if not message.title and not message.text:
logger.warn("标题和内容不能同时为空")
return
try:
if message.title:
caption = message.title
content = message.text
else:
caption = message.text
content = ""
for sub in webpush_registry.list():
logger.debug(f"给 {sub} 发送WebPush:{caption} {content}")
try:
endpoint = sub.get("endpoint")
webpush_options: Dict[str, Any] = {}
if endpoint and "notify.windows.com" in endpoint:
webpush_options = {
"ttl": 86400,
"headers": {"X-WNS-Cache-Policy": "cache"},
}
send_webpush(
subscription=sub,
data=json.dumps({
"title": caption,
"body": content,
"url": message.link or "/?shotcut=message"
}),
vapid_private_key=get_runtime_setting('VAPID').get("privateKey"),
vapid_subject=get_runtime_setting('VAPID').get("subject"),
**webpush_options,
)
except WebPushDeliveryError as err:
logger.error(f"WebPush发送失败: {str(err)}")
response = getattr(err, "response", None)
status_code = getattr(response, "status_code", None) or getattr(
response,
"status",
None,
)
if status_code in {404, 410} and webpush_registry.remove(sub):
logger.info(f"已移除失效WebPush订阅: {sub.get('endpoint')}")
except Exception as msg_e:
logger.error(f"发送消息失败:{msg_e}")