mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-10-30 20:05:42 +08:00
fix(synologychat): skip unsupported command menu registration
This commit is contained in:
@@ -25,6 +25,8 @@ register_channel_admin_resolver(
|
||||
|
||||
|
||||
class SynologyChatModule(_MessageChannelModuleBase[SynologyChat]):
|
||||
"""负责 Synology Chat 消息收发、附件解析和管理员命令校验。"""
|
||||
|
||||
# 管理员配置键,与渠道 resolver 保持一致
|
||||
_admin_config_key = "SYNOLOGYCHAT_ADMINS"
|
||||
_IMAGE_SUFFIXES = (
|
||||
@@ -62,6 +64,7 @@ class SynologyChatModule(_MessageChannelModuleBase[SynologyChat]):
|
||||
|
||||
@staticmethod
|
||||
def get_name() -> str:
|
||||
"""获取模块显示名称"""
|
||||
return "Synology Chat"
|
||||
|
||||
@staticmethod
|
||||
@@ -85,10 +88,16 @@ class SynologyChatModule(_MessageChannelModuleBase[SynologyChat]):
|
||||
"""
|
||||
return 5
|
||||
|
||||
def _commands_enabled(self, config: Optional[dict[str, Any]]) -> bool:
|
||||
"""客户端不提供命令菜单注册或删除 API,跳过基类的菜单注册流程。"""
|
||||
return False
|
||||
|
||||
def stop(self):
|
||||
"""客户端没有需要在模块停止时释放的后台资源。"""
|
||||
pass
|
||||
|
||||
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
||||
"""实例由通知服务配置管理,不额外声明环境开关。"""
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
@@ -167,6 +176,7 @@ class SynologyChatModule(_MessageChannelModuleBase[SynologyChat]):
|
||||
def _extract_images(
|
||||
cls, message: dict
|
||||
) -> Optional[List[IncomingMessage.MessageImage]]:
|
||||
"""从消息字段和附件中提取并去重图片引用"""
|
||||
images = []
|
||||
for key in ("file_url", "image_url", "pic_url"):
|
||||
value = message.get(key)
|
||||
@@ -206,6 +216,7 @@ class SynologyChatModule(_MessageChannelModuleBase[SynologyChat]):
|
||||
|
||||
@classmethod
|
||||
def _extract_audio_refs(cls, message: dict) -> Optional[List[str]]:
|
||||
"""从消息字段和附件中提取并去重音频下载引用"""
|
||||
audio_refs = []
|
||||
for key in ("audio_url", "voice_url", "file_url"):
|
||||
value = message.get(key)
|
||||
@@ -249,6 +260,7 @@ class SynologyChatModule(_MessageChannelModuleBase[SynologyChat]):
|
||||
|
||||
@classmethod
|
||||
def _looks_like_image(cls, value: str) -> bool:
|
||||
"""判断 HTTP 地址是否包含支持的图片扩展名"""
|
||||
if not value or not isinstance(value, str):
|
||||
return False
|
||||
lowered = value.lower()
|
||||
@@ -258,6 +270,7 @@ class SynologyChatModule(_MessageChannelModuleBase[SynologyChat]):
|
||||
|
||||
@classmethod
|
||||
def _looks_like_audio(cls, value: str) -> bool:
|
||||
"""判断 HTTP 地址是否包含支持的音频扩展名"""
|
||||
if not value or not isinstance(value, str):
|
||||
return False
|
||||
lowered = value.lower()
|
||||
@@ -269,6 +282,7 @@ class SynologyChatModule(_MessageChannelModuleBase[SynologyChat]):
|
||||
def _extract_files(
|
||||
cls, message: dict
|
||||
) -> Optional[List[IncomingMessage.MessageAttachment]]:
|
||||
"""提取普通附件下载引用,排除已归类的图片和音频"""
|
||||
files = []
|
||||
for key in ("attachments", "files"):
|
||||
raw_value = message.get(key)
|
||||
|
||||
@@ -296,9 +296,6 @@
|
||||
"app/modules/listenbrainz/__init__.py": {
|
||||
"I001": 1
|
||||
},
|
||||
"app/modules/lrclib/__init__.py": {
|
||||
"I001": 1
|
||||
},
|
||||
"app/modules/navidrome/__init__.py": {
|
||||
"F401": 2,
|
||||
"I001": 1
|
||||
|
||||
30
tests/test_synologychat_command_registration.py
Normal file
30
tests/test_synologychat_command_registration.py
Normal file
@@ -0,0 +1,30 @@
|
||||
"""Synology Chat 不支持命令菜单时的渠道注册回归测试。"""
|
||||
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from app.modules.synologychat import SynologyChatModule
|
||||
from app.modules.synologychat.synologychat import SynologyChat
|
||||
|
||||
|
||||
@pytest.mark.parametrize("commands", [{}, {"/version": {"description": "当前版本"}}])
|
||||
def test_synologychat_skips_command_menu_registration(commands):
|
||||
"""空菜单和非空菜单均应跳过客户端调用及菜单事件,重复注册也不报错。"""
|
||||
module = SynologyChatModule()
|
||||
client = SynologyChat.__new__(SynologyChat)
|
||||
configs = {
|
||||
name: SimpleNamespace(name=name, config={"SYNOLOGYCHAT_TOKEN": "test-token"})
|
||||
for name in ("synology-main", "synology-other")
|
||||
}
|
||||
with (
|
||||
patch.object(module, "get_configs", return_value=configs),
|
||||
patch.object(module, "get_instance", return_value=client) as get_instance,
|
||||
patch("app.modules._base.notification.eventmanager.send_event", return_value=None) as send_event,
|
||||
):
|
||||
module.register_commands(commands)
|
||||
module.register_commands(commands)
|
||||
|
||||
get_instance.assert_not_called()
|
||||
send_event.assert_not_called()
|
||||
Reference in New Issue
Block a user