fix(synologychat): skip unsupported command menu registration

This commit is contained in:
jxxghp
2026-09-13 14:22:59 +08:00
parent 8072b7d78f
commit c868faf0cf
3 changed files with 44 additions and 3 deletions

View File

@@ -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)

View File

@@ -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

View 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()