mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 15:38:19 +08:00
refactor(schemas): 统一 message/notification 命名边界,旧名收敛至兼容映射表
- notification 域:渠道能力(MessageChannel→NotificationChannel、ChannelCapability* 迁入 notification.py) - message 域:消息收发(Notification→Message、NotificationType→MessageType、CommingMessage→IncomingMessage、NotificationHistoryItem→MessageHistoryItem、NotificationClear*→MessageClear*) - Agent 工具契约:send_notification_message→send_message、notification_callback→message_callback - 源码不保留旧名物理别名,旧导入经 app/runtime/compat/manifest.py SYMBOL_ALIASES 惰性解析 - API 路径与持久化键冻结不变,前端零改动 - 新增兼容守护测试与 docs/rules/07 命名边界规范
This commit is contained in:
@@ -3,7 +3,7 @@ import unittest
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from app.agent.tools.impl.add_subscribe import AddSubscribeTool
|
||||
from app.schemas.types import MessageChannel
|
||||
from app.schemas.types import NotificationChannel
|
||||
|
||||
|
||||
class TestAgentAddSubscribeTool(unittest.TestCase):
|
||||
@@ -19,7 +19,7 @@ class TestAgentAddSubscribeTool(unittest.TestCase):
|
||||
def test_tv_subscription_without_season_reports_default_first_season(self):
|
||||
tool = AddSubscribeTool(session_id="session-1", user_id="10001")
|
||||
tool.set_message_attr(
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-main",
|
||||
username="tg_display_name",
|
||||
)
|
||||
@@ -46,7 +46,7 @@ class TestAgentAddSubscribeTool(unittest.TestCase):
|
||||
def test_subscription_falls_back_to_channel_username_when_no_binding_exists(self):
|
||||
tool = AddSubscribeTool(session_id="session-1", user_id="10001")
|
||||
tool.set_message_attr(
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-main",
|
||||
username="tg_display_name",
|
||||
)
|
||||
@@ -72,7 +72,7 @@ class TestAgentAddSubscribeTool(unittest.TestCase):
|
||||
def test_feishu_subscription_uses_pre_resolved_username_when_openid_lookup_misses(self):
|
||||
tool = AddSubscribeTool(session_id="session-1", user_id="ou_feishu_user")
|
||||
tool.set_message_attr(
|
||||
channel=MessageChannel.Feishu.value,
|
||||
channel=NotificationChannel.Feishu.value,
|
||||
source="feishu-main",
|
||||
username="moviepilot-user",
|
||||
)
|
||||
|
||||
@@ -15,7 +15,7 @@ from app.modules.vocechat import VoceChatModule
|
||||
from app.modules.wechat import WechatModule
|
||||
from app.modules.wechat.wechatbot import WeChatBot
|
||||
from app.modules.wechatclawbot import WechatClawBotModule
|
||||
from app.schemas.types import MessageChannel
|
||||
from app.schemas.types import NotificationChannel
|
||||
|
||||
|
||||
def _parse_module_message(module, *, config: dict, body, client=None, form=None):
|
||||
@@ -52,19 +52,19 @@ def test_resolve_config_principal_ids_uses_nonempty_stable_values(config, expect
|
||||
("channel", "config", "principal_ids", "expected"),
|
||||
[
|
||||
(
|
||||
MessageChannel.Telegram,
|
||||
NotificationChannel.Telegram,
|
||||
{"TELEGRAM_ADMINS": "other", "TELEGRAM_CHAT_ID": "10001"},
|
||||
(10001,),
|
||||
True,
|
||||
),
|
||||
(
|
||||
MessageChannel.Feishu,
|
||||
NotificationChannel.Feishu,
|
||||
{"FEISHU_ADMINS": "other", "FEISHU_OPEN_ID": "ou_owner"},
|
||||
("ou_owner",),
|
||||
True,
|
||||
),
|
||||
(
|
||||
MessageChannel.Wechat,
|
||||
NotificationChannel.Wechat,
|
||||
{
|
||||
"WECHAT_MODE": "bot",
|
||||
"WECHAT_ADMINS": "other",
|
||||
@@ -74,7 +74,7 @@ def test_resolve_config_principal_ids_uses_nonempty_stable_values(config, expect
|
||||
True,
|
||||
),
|
||||
(
|
||||
MessageChannel.Wechat,
|
||||
NotificationChannel.Wechat,
|
||||
{
|
||||
"WECHAT_MODE": "app",
|
||||
"WECHAT_ADMINS": "other",
|
||||
@@ -84,7 +84,7 @@ def test_resolve_config_principal_ids_uses_nonempty_stable_values(config, expect
|
||||
False,
|
||||
),
|
||||
(
|
||||
MessageChannel.WechatClawBot,
|
||||
NotificationChannel.WechatClawBot,
|
||||
{
|
||||
"WECHATCLAWBOT_ADMINS": "other",
|
||||
"WECHATCLAWBOT_DEFAULT_TARGET": "wxid_owner",
|
||||
@@ -93,25 +93,25 @@ def test_resolve_config_principal_ids_uses_nonempty_stable_values(config, expect
|
||||
True,
|
||||
),
|
||||
(
|
||||
MessageChannel.QQ,
|
||||
NotificationChannel.QQ,
|
||||
{"QQBOT_ADMINS": "other", "QQ_OPENID": "qq_owner"},
|
||||
("qq_owner",),
|
||||
True,
|
||||
),
|
||||
(
|
||||
MessageChannel.Telegram,
|
||||
NotificationChannel.Telegram,
|
||||
{"TELEGRAM_ADMINS": "other", "TELEGRAM_CHAT_ID": "-10001"},
|
||||
(10001,),
|
||||
False,
|
||||
),
|
||||
(
|
||||
MessageChannel.Feishu,
|
||||
NotificationChannel.Feishu,
|
||||
{"FEISHU_ADMINS": "other", "FEISHU_CHAT_ID": "oc_group"},
|
||||
("ou_user",),
|
||||
False,
|
||||
),
|
||||
(
|
||||
MessageChannel.QQ,
|
||||
NotificationChannel.QQ,
|
||||
{"QQBOT_ADMINS": "other", "QQ_GROUP_OPENID": "qq_group"},
|
||||
("qq_member",),
|
||||
False,
|
||||
|
||||
@@ -4,7 +4,7 @@ from app.agent import MoviePilotAgent
|
||||
from app.agent.llm import AgentCapabilityManager, LLMHelper
|
||||
from app.chain.message import MessageChain
|
||||
from app.runtime.config import settings
|
||||
from app.schemas.types import MessageChannel
|
||||
from app.schemas.types import NotificationChannel
|
||||
|
||||
|
||||
def test_llm_supports_image_input_uses_model_catalog_text_only(monkeypatch):
|
||||
@@ -76,7 +76,7 @@ def test_handle_ai_message_routes_text_only_model_images_to_files(monkeypatch):
|
||||
):
|
||||
chain._handle_ai_message(
|
||||
text="/ai 帮我看看这张图",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
|
||||
@@ -27,8 +27,8 @@ from app.modules.synologychat import SynologyChatModule
|
||||
from app.modules.vocechat import VoceChatModule
|
||||
from app.modules.wechat import WechatModule
|
||||
from app.modules.wechat.wechatbot import WeChatBot
|
||||
from app.schemas import CommingMessage, Notification
|
||||
from app.schemas.types import MessageChannel, NotificationType
|
||||
from app.schemas import IncomingMessage, Message
|
||||
from app.schemas.types import NotificationChannel, MessageType
|
||||
|
||||
|
||||
class AgentImageSupportTest(unittest.TestCase):
|
||||
@@ -135,8 +135,8 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
|
||||
def test_process_allows_image_only_message(self):
|
||||
chain = MessageChain()
|
||||
message = CommingMessage(
|
||||
channel=MessageChannel.Telegram,
|
||||
message = IncomingMessage(
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -154,8 +154,8 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
|
||||
def test_process_allows_audio_only_message(self):
|
||||
chain = MessageChain()
|
||||
message = CommingMessage(
|
||||
channel=MessageChannel.Telegram,
|
||||
message = IncomingMessage(
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -173,13 +173,13 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
|
||||
def test_process_allows_file_only_message(self):
|
||||
chain = MessageChain()
|
||||
message = CommingMessage(
|
||||
channel=MessageChannel.Telegram,
|
||||
message = IncomingMessage(
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
files=[
|
||||
CommingMessage.MessageAttachment(
|
||||
IncomingMessage.MessageAttachment(
|
||||
ref="tg://document_file_id/doc-1",
|
||||
name="note.txt",
|
||||
mime_type="text/plain",
|
||||
@@ -210,7 +210,7 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
settings, "AI_AGENT_GLOBAL", False
|
||||
):
|
||||
chain.handle_message(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -232,7 +232,7 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
settings, "AI_AGENT_GLOBAL", False
|
||||
):
|
||||
chain.handle_message(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -258,13 +258,13 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
settings, "AI_AGENT_GLOBAL", False
|
||||
):
|
||||
chain.handle_message(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
text="",
|
||||
files=[
|
||||
CommingMessage.MessageAttachment(
|
||||
IncomingMessage.MessageAttachment(
|
||||
ref="tg://document_file_id/doc-1",
|
||||
name="report.txt",
|
||||
mime_type="text/plain",
|
||||
@@ -306,7 +306,7 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
) as transcribe_bytes:
|
||||
result = chain._transcribe_audio_refs(
|
||||
audio_refs=audio_refs,
|
||||
channel=MessageChannel.Slack,
|
||||
channel=NotificationChannel.Slack,
|
||||
source="mixed-source",
|
||||
)
|
||||
|
||||
@@ -341,7 +341,7 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
agent = MoviePilotAgent(
|
||||
session_id="session-1",
|
||||
user_id="user-1",
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -362,7 +362,7 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
agent = MoviePilotAgent(
|
||||
session_id="session-1",
|
||||
user_id="user-1",
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -400,7 +400,7 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
agent = MoviePilotAgent(
|
||||
session_id="session-1",
|
||||
user_id="user-1",
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -458,7 +458,7 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
) as run_coroutine_threadsafe:
|
||||
chain._handle_ai_message(
|
||||
text="/ai 帮我看看这张图",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -491,7 +491,7 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
):
|
||||
chain._handle_ai_message(
|
||||
text="帮我推荐一部电影",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -510,7 +510,7 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
) as run_module:
|
||||
images = chain._download_attachments_to_data_urls(
|
||||
attachments=["https://files.slack.com/files-pri/T1-F1/test.png"],
|
||||
channel=MessageChannel.Slack,
|
||||
channel=NotificationChannel.Slack,
|
||||
source="slack-test",
|
||||
)
|
||||
|
||||
@@ -574,7 +574,7 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
async def _run():
|
||||
tool = SendMessageTool(session_id="session-1", user_id="10001")
|
||||
tool.set_message_attr(
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -594,8 +594,8 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
notification = async_post_message.await_args.args[0]
|
||||
|
||||
self.assertEqual(result, "消息已发送")
|
||||
self.assertEqual(notification.mtype, NotificationType.Other)
|
||||
self.assertEqual(notification.channel, MessageChannel.Telegram)
|
||||
self.assertEqual(notification.mtype, MessageType.Other)
|
||||
self.assertEqual(notification.channel, NotificationChannel.Telegram)
|
||||
self.assertEqual(notification.source, "telegram-test")
|
||||
self.assertEqual(notification.title, "智能体通知")
|
||||
self.assertEqual(notification.text, "处理完成")
|
||||
@@ -608,7 +608,7 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
async def _run():
|
||||
tool = SendMessageTool(session_id="session-1", user_id="10001")
|
||||
tool.set_message_attr(
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -638,7 +638,7 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
agent_context = {}
|
||||
tool.set_agent_context(agent_context)
|
||||
tool.set_message_attr(
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -738,7 +738,7 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
module, "get_instance", return_value=client
|
||||
):
|
||||
response = module.send_direct_message(
|
||||
Notification(title="hi", userid="user-1")
|
||||
Message(title="hi", userid="user-1")
|
||||
)
|
||||
|
||||
self.assertIsNotNone(response)
|
||||
@@ -755,7 +755,7 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
) as run_module:
|
||||
images = chain._download_attachments_to_data_urls(
|
||||
attachments=["wxwork://media_id/media-1"],
|
||||
channel=MessageChannel.Wechat,
|
||||
channel=NotificationChannel.Wechat,
|
||||
source="wechat-test",
|
||||
)
|
||||
|
||||
@@ -776,12 +776,12 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
) as run_module:
|
||||
data_urls = chain._download_attachments_to_data_urls(
|
||||
attachments=[
|
||||
CommingMessage.MessageImage(
|
||||
IncomingMessage.MessageImage(
|
||||
ref="feishu://image/img_v2_xxx",
|
||||
mime_type="image/png",
|
||||
)
|
||||
],
|
||||
channel=MessageChannel.Feishu,
|
||||
channel=NotificationChannel.Feishu,
|
||||
source="feishu-test",
|
||||
)
|
||||
|
||||
@@ -798,7 +798,7 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
with patch.object(chain, "run_module", return_value=b"feishu-file") as run_module:
|
||||
content = chain._download_message_file_bytes(
|
||||
file_ref="feishu://file/file_xxx/report.pdf",
|
||||
channel=MessageChannel.Feishu,
|
||||
channel=NotificationChannel.Feishu,
|
||||
source="feishu-test",
|
||||
)
|
||||
|
||||
@@ -1064,7 +1064,7 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
module, "get_instance", return_value=client
|
||||
):
|
||||
module.post_message(
|
||||
Notification(
|
||||
Message(
|
||||
title="poster",
|
||||
image="https://example.com/poster.png",
|
||||
targets={"vocechat_userid": "UID#100"},
|
||||
@@ -1097,7 +1097,7 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
module, "get_instance", return_value=client
|
||||
):
|
||||
module.post_message(
|
||||
Notification(
|
||||
Message(
|
||||
title="手册",
|
||||
text="请下载",
|
||||
file_path=str(file_path),
|
||||
@@ -1132,7 +1132,7 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
module, "get_instance", return_value=client
|
||||
):
|
||||
module.post_message(
|
||||
Notification(
|
||||
Message(
|
||||
title="手册",
|
||||
text="请下载",
|
||||
file_path=str(file_path),
|
||||
@@ -1336,13 +1336,13 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
prepared = chain._prepare_agent_files(
|
||||
session_id="session-1",
|
||||
files=[
|
||||
CommingMessage.MessageAttachment(
|
||||
IncomingMessage.MessageAttachment(
|
||||
ref="tg://document_file_id/doc-1",
|
||||
name="note.txt",
|
||||
mime_type="text/plain",
|
||||
)
|
||||
],
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
)
|
||||
|
||||
@@ -1367,7 +1367,7 @@ class AgentImageSupportTest(unittest.TestCase):
|
||||
module, "get_instance", return_value=client
|
||||
):
|
||||
module.post_message(
|
||||
Notification(
|
||||
Message(
|
||||
title="报告",
|
||||
text="请下载",
|
||||
file_path=str(file_path),
|
||||
|
||||
@@ -17,7 +17,7 @@ from app.application.messaging.agent import (
|
||||
from app.application.messaging.interaction import InteractionContext
|
||||
from app.chain.message import MessageChain
|
||||
from app.runtime.config import settings
|
||||
from app.schemas.types import MessageChannel
|
||||
from app.schemas.types import NotificationChannel
|
||||
|
||||
|
||||
class TestAgentInteraction(unittest.TestCase):
|
||||
@@ -26,13 +26,13 @@ class TestAgentInteraction(unittest.TestCase):
|
||||
|
||||
def test_prompt_injects_choice_tool_hint_only_for_button_channels(self):
|
||||
telegram_prompt = prompt_manager.get_agent_prompt(
|
||||
channel=MessageChannel.Telegram.value
|
||||
channel=NotificationChannel.Telegram.value
|
||||
)
|
||||
web_agent_prompt = prompt_manager.get_agent_prompt(
|
||||
channel=MessageChannel.WebAgent.value
|
||||
channel=NotificationChannel.WebAgent.value
|
||||
)
|
||||
wechat_prompt = prompt_manager.get_agent_prompt(
|
||||
channel=MessageChannel.Wechat.value
|
||||
channel=NotificationChannel.Wechat.value
|
||||
)
|
||||
|
||||
self.assertIn("ask_user_choice", telegram_prompt)
|
||||
@@ -43,10 +43,10 @@ class TestAgentInteraction(unittest.TestCase):
|
||||
|
||||
def test_prompt_does_not_inject_send_message_html_hint(self):
|
||||
telegram_prompt = prompt_manager.get_agent_prompt(
|
||||
channel=MessageChannel.Telegram.value
|
||||
channel=NotificationChannel.Telegram.value
|
||||
)
|
||||
wechat_prompt = prompt_manager.get_agent_prompt(
|
||||
channel=MessageChannel.Wechat.value
|
||||
channel=NotificationChannel.Wechat.value
|
||||
)
|
||||
|
||||
self.assertNotIn("parse_mode=\"HTML\"", telegram_prompt)
|
||||
@@ -61,21 +61,21 @@ class TestAgentInteraction(unittest.TestCase):
|
||||
telegram_tools = MoviePilotToolFactory.create_tools(
|
||||
session_id="session-1",
|
||||
user_id="10001",
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
web_agent_tools = MoviePilotToolFactory.create_tools(
|
||||
session_id="session-web",
|
||||
user_id="10001",
|
||||
channel=MessageChannel.WebAgent.value,
|
||||
channel=NotificationChannel.WebAgent.value,
|
||||
source="web-agent",
|
||||
username="tester",
|
||||
)
|
||||
wechat_tools = MoviePilotToolFactory.create_tools(
|
||||
session_id="session-2",
|
||||
user_id="10001",
|
||||
channel=MessageChannel.Wechat.value,
|
||||
channel=NotificationChannel.Wechat.value,
|
||||
source="wechat-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -101,7 +101,7 @@ class TestAgentInteraction(unittest.TestCase):
|
||||
def test_choice_tool_sends_buttons_and_registers_pending_request(self):
|
||||
tool = AskUserChoiceTool(session_id="session-1", user_id="10001")
|
||||
tool.set_message_attr(
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -141,7 +141,7 @@ class TestAgentInteraction(unittest.TestCase):
|
||||
def test_choice_tool_blocks_after_feedback_quality_rejection(self):
|
||||
tool = AskUserChoiceTool(session_id="session-feedback", user_id="10001")
|
||||
tool.set_message_attr(
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -177,7 +177,7 @@ class TestAgentInteraction(unittest.TestCase):
|
||||
request = agent_interaction_manager.create_request(
|
||||
session_id="session-choice",
|
||||
user_id="10001",
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
title="需要你的选择",
|
||||
@@ -204,7 +204,7 @@ class TestAgentInteraction(unittest.TestCase):
|
||||
handled = chain._handle_callback(
|
||||
callback_data=f"agent_interaction:choice:{request.request_id}:1",
|
||||
context=InteractionContext(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
user_id="10001",
|
||||
username="tester",
|
||||
@@ -215,7 +215,7 @@ class TestAgentInteraction(unittest.TestCase):
|
||||
|
||||
self.assertTrue(handled)
|
||||
edit_message.assert_called_once_with(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
message_id=123,
|
||||
chat_id="456",
|
||||
@@ -226,7 +226,7 @@ class TestAgentInteraction(unittest.TestCase):
|
||||
kwargs = process_message.call_args.kwargs
|
||||
self.assertEqual(kwargs["message"], "我选择电影")
|
||||
self.assertEqual(kwargs["session_id"], "session-choice")
|
||||
self.assertEqual(kwargs["channel"], MessageChannel.Telegram.value)
|
||||
self.assertEqual(kwargs["channel"], NotificationChannel.Telegram.value)
|
||||
self.assertEqual(kwargs["source"], "telegram-test")
|
||||
self.assertNotIn("processing_status", kwargs)
|
||||
message_put.assert_not_called()
|
||||
@@ -237,7 +237,7 @@ class TestAgentInteraction(unittest.TestCase):
|
||||
request = agent_interaction_manager.create_request(
|
||||
session_id="session-choice",
|
||||
user_id="10001",
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
title=None,
|
||||
@@ -251,7 +251,7 @@ class TestAgentInteraction(unittest.TestCase):
|
||||
chain._handle_callback(
|
||||
callback_data=f"agent_choice:{request.request_id}:1",
|
||||
context=InteractionContext(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
user_id="10001",
|
||||
username="tester",
|
||||
@@ -266,7 +266,7 @@ class TestAgentInteraction(unittest.TestCase):
|
||||
MessageChain._user_sessions["10001"] = ("session-secret", datetime.now())
|
||||
|
||||
try:
|
||||
for channel in (MessageChannel.Telegram, MessageChannel.Feishu):
|
||||
for channel in (NotificationChannel.Telegram, NotificationChannel.Feishu):
|
||||
with patch(
|
||||
"app.chain.message.agent_manager.matches_secret_confirmation",
|
||||
return_value=True,
|
||||
|
||||
@@ -6,8 +6,8 @@ from types import SimpleNamespace
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from app.runtime.config import settings
|
||||
from app.schemas.message import ChannelCapability, ChannelCapabilityManager
|
||||
from app.schemas.types import MessageChannel
|
||||
from app.schemas.notification import ChannelCapability, ChannelCapabilityManager
|
||||
from app.schemas.types import NotificationChannel
|
||||
|
||||
from app.agent.llm import capability as capability_module
|
||||
from app.agent.llm.capability import (
|
||||
@@ -171,12 +171,12 @@ class AgentCapabilityManagerTest(unittest.TestCase):
|
||||
)
|
||||
self.assertTrue(
|
||||
AgentCapabilityManager.supports_native_voice_reply(
|
||||
MessageChannel.Telegram.value, None
|
||||
NotificationChannel.Telegram.value, None
|
||||
)
|
||||
)
|
||||
self.assertTrue(
|
||||
AgentCapabilityManager.supports_native_voice_reply(
|
||||
MessageChannel.Feishu.value, None
|
||||
NotificationChannel.Feishu.value, None
|
||||
)
|
||||
)
|
||||
self.assertTrue(
|
||||
@@ -184,7 +184,7 @@ class AgentCapabilityManagerTest(unittest.TestCase):
|
||||
)
|
||||
self.assertTrue(
|
||||
AgentCapabilityManager.supports_native_voice_reply(
|
||||
MessageChannel.WebAgent.value, None
|
||||
NotificationChannel.WebAgent.value, None
|
||||
)
|
||||
)
|
||||
self.assertFalse(
|
||||
@@ -204,27 +204,27 @@ class AgentCapabilityManagerTest(unittest.TestCase):
|
||||
):
|
||||
self.assertTrue(
|
||||
AgentCapabilityManager.supports_native_voice_reply(
|
||||
MessageChannel.Wechat.value, "wechat-app"
|
||||
NotificationChannel.Wechat.value, "wechat-app"
|
||||
)
|
||||
)
|
||||
self.assertFalse(
|
||||
AgentCapabilityManager.supports_native_voice_reply(
|
||||
MessageChannel.Wechat.value, "wechat-bot"
|
||||
NotificationChannel.Wechat.value, "wechat-bot"
|
||||
)
|
||||
)
|
||||
self.assertFalse(
|
||||
AgentCapabilityManager.supports_native_voice_reply(
|
||||
MessageChannel.Wechat.value, "missing"
|
||||
NotificationChannel.Wechat.value, "missing"
|
||||
)
|
||||
)
|
||||
|
||||
def test_channel_capability_marks_voice_output_channels(self):
|
||||
"""校验消息渠道能力显式声明原生语音输出支持。"""
|
||||
for channel in (
|
||||
MessageChannel.Telegram,
|
||||
MessageChannel.Feishu,
|
||||
MessageChannel.Wechat,
|
||||
MessageChannel.WebAgent,
|
||||
NotificationChannel.Telegram,
|
||||
NotificationChannel.Feishu,
|
||||
NotificationChannel.Wechat,
|
||||
NotificationChannel.WebAgent,
|
||||
):
|
||||
self.assertTrue(
|
||||
ChannelCapabilityManager.supports_capability(
|
||||
@@ -233,7 +233,7 @@ class AgentCapabilityManagerTest(unittest.TestCase):
|
||||
)
|
||||
self.assertFalse(
|
||||
ChannelCapabilityManager.supports_capability(
|
||||
MessageChannel.Slack, ChannelCapability.AUDIO_OUTPUT
|
||||
NotificationChannel.Slack, ChannelCapability.AUDIO_OUTPUT
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ from app.db.models.message import Message
|
||||
from app.application.messaging.agent import AgentInteractionOption, agent_interaction_manager
|
||||
from app.application.messaging.interaction import InteractionContext
|
||||
from app.application.messaging.media import media_interaction_manager
|
||||
from app.schemas.types import MessageChannel, NotificationType
|
||||
from app.schemas.types import NotificationChannel, MessageType
|
||||
|
||||
|
||||
def _clear_messages() -> None:
|
||||
@@ -31,7 +31,7 @@ def test_explicit_ai_message_bypasses_pending_media_interaction():
|
||||
media_interaction_manager.clear()
|
||||
media_interaction_manager.create_or_replace(
|
||||
user_id="10001",
|
||||
channel=MessageChannel.Wechat,
|
||||
channel=NotificationChannel.Wechat,
|
||||
source="wechat-test",
|
||||
username="tester",
|
||||
action="Search",
|
||||
@@ -47,7 +47,7 @@ def test_explicit_ai_message_bypasses_pending_media_interaction():
|
||||
chain, "_handle_ai_message", return_value=True
|
||||
) as handle_ai_message:
|
||||
chain.handle_message(
|
||||
channel=MessageChannel.Wechat,
|
||||
channel=NotificationChannel.Wechat,
|
||||
source="wechat-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -74,7 +74,7 @@ def test_explicit_ai_message_is_not_recorded_to_message_history():
|
||||
side_effect=lambda coro, _loop: (coro.close(), Mock())[1],
|
||||
):
|
||||
chain.handle_message(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -97,7 +97,7 @@ def test_message_chain_passes_stable_channel_admin_principal_to_agent():
|
||||
side_effect=lambda coro, _loop: (coro.close(), Mock())[1],
|
||||
):
|
||||
chain.handle_message(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="renamed-user",
|
||||
@@ -120,7 +120,7 @@ def test_message_chain_does_not_trust_channel_display_username():
|
||||
side_effect=lambda coro, _loop: (coro.close(), Mock())[1],
|
||||
):
|
||||
chain.handle_message(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10002",
|
||||
username="admin",
|
||||
@@ -143,7 +143,7 @@ def test_message_chain_uses_same_admin_contract_for_slack():
|
||||
side_effect=lambda coro, _loop: (coro.close(), Mock())[1],
|
||||
):
|
||||
chain.handle_message(
|
||||
channel=MessageChannel.Slack,
|
||||
channel=NotificationChannel.Slack,
|
||||
source="slack-test",
|
||||
userid="UADMIN",
|
||||
username="renamed-user",
|
||||
@@ -159,7 +159,7 @@ def test_ask_user_choice_message_is_not_recorded_to_message_history():
|
||||
_clear_messages()
|
||||
tool = AskUserChoiceTool(session_id="session-choice", user_id="10001")
|
||||
tool.set_message_attr(
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -198,7 +198,7 @@ def test_agent_final_reply_disables_notification_history():
|
||||
agent = MoviePilotAgent(
|
||||
session_id="session-agent-reply",
|
||||
user_id="10001",
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -210,7 +210,7 @@ def test_agent_final_reply_disables_notification_history():
|
||||
asyncio.run(agent.send_agent_message("已完成处理"))
|
||||
|
||||
notification = async_post_message.await_args.args[0]
|
||||
assert notification.mtype == NotificationType.Agent
|
||||
assert notification.mtype == MessageType.Agent
|
||||
assert notification.save_history is False
|
||||
|
||||
|
||||
@@ -218,7 +218,7 @@ def test_send_message_tool_disables_notification_history():
|
||||
"""Agent 主动发消息工具发送的通知不保存通知历史。"""
|
||||
tool = SendMessageTool(session_id="session-send-message", user_id="10001")
|
||||
tool.set_message_attr(
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -242,7 +242,7 @@ def test_agent_choice_callback_is_not_recorded_to_message_history():
|
||||
request = agent_interaction_manager.create_request(
|
||||
session_id="session-choice",
|
||||
user_id="10001",
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
title="需要你的选择",
|
||||
@@ -268,7 +268,7 @@ def test_agent_choice_callback_is_not_recorded_to_message_history():
|
||||
chain._handle_callback(
|
||||
callback_data=f"agent_interaction:choice:{request.request_id}:1",
|
||||
context=InteractionContext(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
user_id="10001",
|
||||
username="tester",
|
||||
|
||||
@@ -2,18 +2,18 @@ from unittest.mock import patch
|
||||
|
||||
from app.agent.prompt import prompt_manager
|
||||
from app.runtime.config import settings
|
||||
from app.schemas.types import MessageChannel
|
||||
from app.schemas.types import NotificationChannel
|
||||
|
||||
|
||||
def test_progress_prompt_is_independent_from_tool_display_mode() -> None:
|
||||
"""进度沟通规则不应随工具逐条或汇总展示模式变化。"""
|
||||
with patch.object(settings, "AI_AGENT_VERBOSE", False):
|
||||
summary_mode_prompt = prompt_manager.get_agent_prompt(
|
||||
channel=MessageChannel.WebAgent.value
|
||||
channel=NotificationChannel.WebAgent.value
|
||||
)
|
||||
with patch.object(settings, "AI_AGENT_VERBOSE", True):
|
||||
verbose_mode_prompt = prompt_manager.get_agent_prompt(
|
||||
channel=MessageChannel.WebAgent.value
|
||||
channel=NotificationChannel.WebAgent.value
|
||||
)
|
||||
|
||||
assert summary_mode_prompt == verbose_mode_prompt
|
||||
|
||||
@@ -17,7 +17,7 @@ from app.agent import MoviePilotAgent
|
||||
from app.runtime.config import settings
|
||||
from app.modules.feishu import FeishuModule
|
||||
from app.modules.telegram import TelegramModule
|
||||
from app.schemas.types import MessageChannel
|
||||
from app.schemas.types import NotificationChannel
|
||||
|
||||
|
||||
# 渠道模块在导入时注册管理员解析器,权限回查测试需显式加载对应模块。
|
||||
@@ -350,7 +350,7 @@ def test_channel_agent_admin_user_id_does_not_bypass_user_lookup():
|
||||
agent = MoviePilotAgent(
|
||||
session_id="session-1",
|
||||
user_id="admin",
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-main",
|
||||
username="normal-user",
|
||||
)
|
||||
@@ -371,7 +371,7 @@ def test_channel_agent_rejects_local_admin_username_without_trusted_principal():
|
||||
agent = MoviePilotAgent(
|
||||
session_id="session-1",
|
||||
user_id="10002",
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-main",
|
||||
username="admin",
|
||||
)
|
||||
@@ -394,7 +394,7 @@ def test_channel_agent_accepts_trusted_admin_principal_without_local_user():
|
||||
agent = MoviePilotAgent(
|
||||
session_id="session-1",
|
||||
user_id="10001",
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-main",
|
||||
username="renamed-user",
|
||||
)
|
||||
@@ -413,7 +413,7 @@ def test_tool_explicit_non_admin_context_does_not_fallback_to_channel_lookup():
|
||||
"""Agent 已判定为非管理员时,工具不得通过旧权限查询重新授权。"""
|
||||
tool = QuerySitesTool(session_id="session-1", user_id="10002")
|
||||
tool.set_message_attr(
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-main",
|
||||
username="admin",
|
||||
)
|
||||
@@ -434,7 +434,7 @@ def test_channel_primary_id_defaults_to_admin_without_admin_list():
|
||||
"""渠道主ID未配置到管理员名单时仍默认为管理员。"""
|
||||
tool = QuerySitesTool(session_id="session-1", user_id="10001")
|
||||
tool.set_message_attr(
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-main",
|
||||
username="owner",
|
||||
)
|
||||
@@ -457,7 +457,7 @@ def test_channel_primary_id_mismatch_remains_non_admin():
|
||||
"""非主ID用户且不在管理员名单时不能获得管理员权限。"""
|
||||
tool = QuerySitesTool(session_id="session-1", user_id="10002")
|
||||
tool.set_message_attr(
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-main",
|
||||
username="other",
|
||||
)
|
||||
@@ -480,7 +480,7 @@ def test_feishu_primary_open_id_defaults_to_admin():
|
||||
"""飞书渠道主ID使用默认接收人 OPEN_ID 判断管理员身份。"""
|
||||
tool = QuerySitesTool(session_id="session-1", user_id="ou_owner")
|
||||
tool.set_message_attr(
|
||||
channel=MessageChannel.Feishu.value,
|
||||
channel=NotificationChannel.Feishu.value,
|
||||
source="feishu-main",
|
||||
username="owner",
|
||||
)
|
||||
@@ -503,7 +503,7 @@ def test_channel_primary_id_still_prefers_admin_list():
|
||||
"""管理员名单命中优先于主ID兜底。"""
|
||||
tool = QuerySitesTool(session_id="session-1", user_id="10001")
|
||||
tool.set_message_attr(
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-main",
|
||||
username="owner",
|
||||
)
|
||||
|
||||
@@ -13,7 +13,7 @@ from app.agent import MoviePilotAgent, ReplyMode, agent_manager
|
||||
from app.agent.middleware.policy import AgentPolicyMiddleware
|
||||
from app.agent.policy import AuthSource, PrincipalType, ToolOrigin, ToolPolicyContext
|
||||
from app.agent.tools.impl.query_system_settings import QuerySystemSettingsTool
|
||||
from app.schemas.types import MessageChannel
|
||||
from app.schemas.types import NotificationChannel
|
||||
|
||||
|
||||
class _ToolCallingFakeModel(FakeMessagesListChatModel):
|
||||
@@ -33,7 +33,7 @@ def _policy_context(agent_context: dict) -> ToolPolicyContext:
|
||||
principal_type=PrincipalType.HUMAN,
|
||||
auth_source=AuthSource.CHANNEL,
|
||||
agent_context=agent_context,
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-test",
|
||||
)
|
||||
|
||||
@@ -175,7 +175,7 @@ def test_confirm_executes_once_without_model_or_history() -> None:
|
||||
agent = MoviePilotAgent(
|
||||
session_id="session-secret",
|
||||
user_id="1",
|
||||
channel=MessageChannel.WebAgent.value,
|
||||
channel=NotificationChannel.WebAgent.value,
|
||||
source="web-agent",
|
||||
username="admin",
|
||||
replay_mode=ReplyMode.CAPTURE_ONLY,
|
||||
@@ -184,7 +184,7 @@ def test_confirm_executes_once_without_model_or_history() -> None:
|
||||
)
|
||||
tool = QuerySystemSettingsTool(session_id="session-secret", user_id="1")
|
||||
tool.set_message_attr(
|
||||
channel=MessageChannel.WebAgent.value,
|
||||
channel=NotificationChannel.WebAgent.value,
|
||||
source="web-agent",
|
||||
username="admin",
|
||||
)
|
||||
@@ -229,7 +229,7 @@ def test_cancel_clears_pending_without_executing_tool() -> None:
|
||||
agent = MoviePilotAgent(
|
||||
session_id="session-secret",
|
||||
user_id="1",
|
||||
channel=MessageChannel.WebAgent.value,
|
||||
channel=NotificationChannel.WebAgent.value,
|
||||
source="web-agent",
|
||||
username="admin",
|
||||
replay_mode=ReplyMode.CAPTURE_ONLY,
|
||||
@@ -266,7 +266,7 @@ def test_expired_confirmation_reaches_agent_expiry_receipt() -> None:
|
||||
agent = MoviePilotAgent(
|
||||
session_id="session-expired-secret",
|
||||
user_id="1",
|
||||
channel=MessageChannel.WebAgent.value,
|
||||
channel=NotificationChannel.WebAgent.value,
|
||||
source="web-agent",
|
||||
username="admin",
|
||||
replay_mode=ReplyMode.CAPTURE_ONLY,
|
||||
@@ -287,7 +287,7 @@ def test_expired_confirmation_reaches_agent_expiry_receipt() -> None:
|
||||
assert agent_manager.matches_secret_confirmation(
|
||||
agent.session_id,
|
||||
"1",
|
||||
channel=MessageChannel.WebAgent.value,
|
||||
channel=NotificationChannel.WebAgent.value,
|
||||
source="web-agent",
|
||||
)
|
||||
return await agent.process("确认")
|
||||
@@ -328,7 +328,7 @@ def test_message_channel_receives_confirmation_prompt_once() -> None:
|
||||
agent = MoviePilotAgent(
|
||||
session_id="session-secret",
|
||||
user_id="1",
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-main",
|
||||
username="admin",
|
||||
original_chat_id="chat-1",
|
||||
@@ -362,7 +362,7 @@ def test_message_channel_does_not_register_pending_when_private_delivery_fails()
|
||||
agent = MoviePilotAgent(
|
||||
session_id="session-secret",
|
||||
user_id="1",
|
||||
channel=MessageChannel.Feishu.value,
|
||||
channel=NotificationChannel.Feishu.value,
|
||||
source="feishu-main",
|
||||
username="admin",
|
||||
original_chat_id="group-1",
|
||||
@@ -394,7 +394,7 @@ def test_private_delivery_requests_literal_plain_text() -> None:
|
||||
agent = MoviePilotAgent(
|
||||
session_id="session-secret",
|
||||
user_id="1",
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-main",
|
||||
username="admin",
|
||||
original_chat_id="group-1",
|
||||
@@ -423,7 +423,7 @@ def test_pending_secret_read_keeps_actor_and_action_across_chat_targets() -> Non
|
||||
agent = MoviePilotAgent(
|
||||
session_id="session-secret",
|
||||
user_id="1",
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-main",
|
||||
username="admin",
|
||||
original_chat_id="chat-1",
|
||||
@@ -475,7 +475,7 @@ def test_confirm_reports_result_delivery_failure_without_secret() -> None:
|
||||
agent = MoviePilotAgent(
|
||||
session_id="session-secret",
|
||||
user_id="1",
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-main",
|
||||
username="admin",
|
||||
original_chat_id="group-1",
|
||||
@@ -524,7 +524,7 @@ def test_web_protected_callback_failure_returns_ordinary_safe_notice() -> None:
|
||||
agent = MoviePilotAgent(
|
||||
session_id="session-secret",
|
||||
user_id="1",
|
||||
channel=MessageChannel.WebAgent.value,
|
||||
channel=NotificationChannel.WebAgent.value,
|
||||
source="web-agent",
|
||||
username="admin",
|
||||
replay_mode=ReplyMode.CAPTURE_ONLY,
|
||||
@@ -562,7 +562,7 @@ def test_confirm_reuses_policy_lifecycle() -> None:
|
||||
agent = MoviePilotAgent(
|
||||
session_id="session-secret",
|
||||
user_id="1",
|
||||
channel=MessageChannel.WebAgent.value,
|
||||
channel=NotificationChannel.WebAgent.value,
|
||||
source="web-agent",
|
||||
username="admin",
|
||||
replay_mode=ReplyMode.CAPTURE_ONLY,
|
||||
@@ -613,7 +613,7 @@ def test_confirm_respects_policy_denial_without_running_tool() -> None:
|
||||
agent = MoviePilotAgent(
|
||||
session_id="session-secret",
|
||||
user_id="1",
|
||||
channel=MessageChannel.WebAgent.value,
|
||||
channel=NotificationChannel.WebAgent.value,
|
||||
source="web-agent",
|
||||
username="admin",
|
||||
replay_mode=ReplyMode.CAPTURE_ONLY,
|
||||
@@ -647,7 +647,7 @@ def test_policy_denial_delivery_failure_reports_not_executed() -> None:
|
||||
agent = MoviePilotAgent(
|
||||
session_id="session-secret",
|
||||
user_id="1",
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-main",
|
||||
username="admin",
|
||||
original_chat_id="group-1",
|
||||
@@ -690,7 +690,7 @@ def test_confirm_records_policy_failure_and_returns_protected_error() -> None:
|
||||
agent = MoviePilotAgent(
|
||||
session_id="session-secret",
|
||||
user_id="1",
|
||||
channel=MessageChannel.WebAgent.value,
|
||||
channel=NotificationChannel.WebAgent.value,
|
||||
source="web-agent",
|
||||
username="admin",
|
||||
replay_mode=ReplyMode.CAPTURE_ONLY,
|
||||
@@ -748,7 +748,7 @@ def test_execution_failure_delivery_failure_reports_safe_notice() -> None:
|
||||
agent = MoviePilotAgent(
|
||||
session_id="session-secret",
|
||||
user_id="1",
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-main",
|
||||
username="admin",
|
||||
original_chat_id="group-1",
|
||||
|
||||
@@ -10,7 +10,7 @@ from langchain_core.messages import AIMessage
|
||||
from app.agent.middleware.usage import UsageMiddleware
|
||||
from app.agent import AgentManager
|
||||
from app.chain.message import MessageChain
|
||||
from app.schemas.types import MessageChannel
|
||||
from app.schemas.types import NotificationChannel
|
||||
|
||||
|
||||
class TestAgentSessionStatus(unittest.TestCase):
|
||||
@@ -100,7 +100,7 @@ class TestAgentSessionStatus(unittest.TestCase):
|
||||
patch.object(chain, "post_message") as post_message,
|
||||
):
|
||||
chain.remote_session_status(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
userid="10001",
|
||||
source="telegram-test",
|
||||
)
|
||||
@@ -121,7 +121,7 @@ class TestAgentSessionStatus(unittest.TestCase):
|
||||
|
||||
with patch.object(chain, "post_message") as post_message:
|
||||
chain.remote_session_status(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
userid="10001",
|
||||
source="telegram-test",
|
||||
)
|
||||
|
||||
@@ -8,11 +8,11 @@ from app.agent.tools.impl.ask_user_choice import (
|
||||
UserChoiceOptionInput,
|
||||
)
|
||||
from app.agent.tools.impl.send_message import SendMessageTool
|
||||
from app.schemas import Notification
|
||||
from app.schemas.types import MessageChannel
|
||||
from app.schemas import Message
|
||||
from app.schemas.types import NotificationChannel
|
||||
|
||||
|
||||
def _run_choice_tool(agent_context: dict, channel: str, source: str) -> Notification:
|
||||
def _run_choice_tool(agent_context: dict, channel: str, source: str) -> Message:
|
||||
"""运行按钮选择工具并返回其发送的通知。"""
|
||||
tool = AskUserChoiceTool(session_id="session-1", user_id="ou_xxx")
|
||||
tool.set_message_attr(
|
||||
@@ -41,7 +41,7 @@ def test_choice_tool_backfills_original_chat_id_from_session_context():
|
||||
"""群聊场景下按钮选择通知应回填会话上下文中的 original_chat_id。"""
|
||||
notification = _run_choice_tool(
|
||||
agent_context={"original_chat_id": "oc_group_123"},
|
||||
channel=MessageChannel.Feishu.value,
|
||||
channel=NotificationChannel.Feishu.value,
|
||||
source="feishu-test",
|
||||
)
|
||||
|
||||
@@ -53,7 +53,7 @@ def test_choice_tool_keeps_explicit_original_chat_id():
|
||||
"""按钮选择通知已显式携带原会话 ID 时不应被上下文覆盖。"""
|
||||
notification = _run_choice_tool(
|
||||
agent_context={"original_chat_id": "oc_group_zzz"},
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-test",
|
||||
)
|
||||
|
||||
@@ -64,7 +64,7 @@ def test_choice_tool_no_context_does_not_backfill():
|
||||
"""会话上下文未携带原会话 ID 时,通知保持原有发送目标。"""
|
||||
notification = _run_choice_tool(
|
||||
agent_context={},
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-test",
|
||||
)
|
||||
|
||||
@@ -92,7 +92,7 @@ def test_send_tool_message_backfills_original_chat_id():
|
||||
"""send_tool_message 工具消息同样应回填原会话 ID。"""
|
||||
tool = SendMessageTool(session_id="session-1", user_id="ou_xxx")
|
||||
tool.set_message_attr(
|
||||
channel=MessageChannel.Feishu.value,
|
||||
channel=NotificationChannel.Feishu.value,
|
||||
source="feishu-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -115,7 +115,7 @@ def test_tool_context_includes_original_chat_id():
|
||||
agent = MoviePilotAgent(
|
||||
session_id="session-1",
|
||||
user_id="ou_xxx",
|
||||
channel=MessageChannel.Feishu.value,
|
||||
channel=NotificationChannel.Feishu.value,
|
||||
source="feishu-test",
|
||||
username="tester",
|
||||
original_chat_id="oc_group_123",
|
||||
|
||||
@@ -33,7 +33,7 @@ from app.agent.tools.impl.query_system_settings import QuerySystemSettingsTool
|
||||
from app.agent.tools.impl.send_local_file import SendLocalFileTool
|
||||
from app.agent.tools.impl.send_voice_message import SendVoiceMessageTool
|
||||
from app.agent.tools.manager import MoviePilotToolsManager
|
||||
from app.schemas.types import MessageChannel
|
||||
from app.schemas.types import NotificationChannel
|
||||
|
||||
|
||||
class _EchoInput(BaseModel):
|
||||
@@ -270,43 +270,43 @@ def test_policy_context_maps_trusted_host_origins() -> None:
|
||||
"""各入口必须由宿主稳定映射 origin、主体类型和认证来源。"""
|
||||
cases = [
|
||||
(
|
||||
{"channel": MessageChannel.Web.value, "source": "openai"},
|
||||
{"channel": NotificationChannel.Web.value, "source": "openai"},
|
||||
ToolOrigin.AGENT_API,
|
||||
PrincipalType.SYSTEM_ADMIN_INTEGRATION,
|
||||
AuthSource.API_TOKEN,
|
||||
),
|
||||
(
|
||||
{"channel": MessageChannel.Web.value, "source": "openai.responses"},
|
||||
{"channel": NotificationChannel.Web.value, "source": "openai.responses"},
|
||||
ToolOrigin.AGENT_API,
|
||||
PrincipalType.SYSTEM_ADMIN_INTEGRATION,
|
||||
AuthSource.API_TOKEN,
|
||||
),
|
||||
(
|
||||
{"channel": MessageChannel.Web.value, "source": "anthropic"},
|
||||
{"channel": NotificationChannel.Web.value, "source": "anthropic"},
|
||||
ToolOrigin.AGENT_API,
|
||||
PrincipalType.SYSTEM_ADMIN_INTEGRATION,
|
||||
AuthSource.API_TOKEN,
|
||||
),
|
||||
(
|
||||
{"channel": MessageChannel.Web.value, "source": "browser"},
|
||||
{"channel": NotificationChannel.Web.value, "source": "browser"},
|
||||
ToolOrigin.AGENT_INTERACTIVE,
|
||||
PrincipalType.HUMAN,
|
||||
AuthSource.WEB_SESSION,
|
||||
),
|
||||
(
|
||||
{"channel": MessageChannel.WebAgent.value, "source": "web-agent"},
|
||||
{"channel": NotificationChannel.WebAgent.value, "source": "web-agent"},
|
||||
ToolOrigin.AGENT_INTERACTIVE,
|
||||
PrincipalType.HUMAN,
|
||||
AuthSource.WEB_SESSION,
|
||||
),
|
||||
(
|
||||
{"channel": MessageChannel.Telegram.value, "source": "telegram"},
|
||||
{"channel": NotificationChannel.Telegram.value, "source": "telegram"},
|
||||
ToolOrigin.AGENT_INTERACTIVE,
|
||||
PrincipalType.HUMAN,
|
||||
AuthSource.CHANNEL,
|
||||
),
|
||||
(
|
||||
{"channel": MessageChannel.Feishu.value, "source": "feishu"},
|
||||
{"channel": NotificationChannel.Feishu.value, "source": "feishu"},
|
||||
ToolOrigin.AGENT_INTERACTIVE,
|
||||
PrincipalType.HUMAN,
|
||||
AuthSource.CHANNEL,
|
||||
@@ -333,7 +333,7 @@ def test_policy_context_maps_trusted_host_origins() -> None:
|
||||
subagent_context = agent_module.MoviePilotAgent(
|
||||
session_id="subagent-session",
|
||||
user_id="user-1",
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram",
|
||||
)._build_policy_context().for_subagent()
|
||||
assert subagent_context.origin is ToolOrigin.SUBAGENT
|
||||
@@ -571,7 +571,7 @@ def test_agent_admin_safe_read_keeps_legacy_authorization_authority(
|
||||
user_id="user-1",
|
||||
)
|
||||
tool.set_message_attr(
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="user",
|
||||
username="member",
|
||||
)
|
||||
@@ -806,7 +806,7 @@ def test_main_agent_preserves_activity_log_middleware_order() -> None:
|
||||
agent = agent_module.MoviePilotAgent(
|
||||
session_id="session-1",
|
||||
user_id="user-1",
|
||||
channel=MessageChannel.WebAgent.value,
|
||||
channel=NotificationChannel.WebAgent.value,
|
||||
source="web-agent",
|
||||
)
|
||||
fake_llm = _AgentFactoryLLM()
|
||||
|
||||
@@ -15,7 +15,7 @@ from app.agent.tools.impl.send_voice_message import SendVoiceMessageTool
|
||||
from app.api.endpoints.openai import _OpenAIStreamingHandler
|
||||
from app.runtime.config import settings
|
||||
from app.schemas.message import MessageResponse
|
||||
from app.schemas.types import MessageChannel, NotificationType
|
||||
from app.schemas.types import NotificationChannel, MessageType
|
||||
|
||||
|
||||
def test_think_tag_stripper_waits_for_partial_open_tag():
|
||||
@@ -344,7 +344,7 @@ class TestAgentToolStreaming:
|
||||
def test_flush_sends_direct_message_via_threadpool(self):
|
||||
"""校验刷新时通过线程池发送首条直连消息。"""
|
||||
handler = StreamingHandler()
|
||||
handler._channel = MessageChannel.Telegram.value
|
||||
handler._channel = NotificationChannel.Telegram.value
|
||||
handler._source = "telegram"
|
||||
handler._user_id = "10001"
|
||||
handler._username = "tester"
|
||||
@@ -365,13 +365,13 @@ class TestAgentToolStreaming:
|
||||
|
||||
assert run_in_threadpool_mock.await_count == 1
|
||||
assert run_in_threadpool_mock.await_args.args[0].__name__ == "send_direct_message"
|
||||
assert run_in_threadpool_mock.await_args.args[1].mtype == NotificationType.Agent
|
||||
assert run_in_threadpool_mock.await_args.args[1].mtype == MessageType.Agent
|
||||
assert handler.has_sent_message
|
||||
|
||||
def test_flush_edits_message_via_threadpool(self):
|
||||
"""校验刷新时通过线程池编辑已有消息。"""
|
||||
handler = StreamingHandler()
|
||||
handler._channel = MessageChannel.Telegram.value
|
||||
handler._channel = NotificationChannel.Telegram.value
|
||||
handler._source = "telegram"
|
||||
handler._streaming_enabled = True
|
||||
handler._message_response = MessageResponse(
|
||||
@@ -398,7 +398,7 @@ class TestAgentToolStreaming:
|
||||
"""校验停止流式输出会等待首条消息发送完成再编辑。"""
|
||||
async def _run():
|
||||
handler = StreamingHandler()
|
||||
handler._channel = MessageChannel.Feishu.value
|
||||
handler._channel = NotificationChannel.Feishu.value
|
||||
handler._source = "feishu-main"
|
||||
handler._user_id = "ou_user"
|
||||
handler._streaming_enabled = True
|
||||
@@ -416,7 +416,7 @@ class TestAgentToolStreaming:
|
||||
return MessageResponse(
|
||||
message_id="om_stream",
|
||||
chat_id="oc_stream",
|
||||
channel=MessageChannel.Feishu,
|
||||
channel=NotificationChannel.Feishu,
|
||||
source="feishu-main",
|
||||
success=True,
|
||||
)
|
||||
@@ -459,7 +459,7 @@ class TestAgentToolStreaming:
|
||||
handler._message_response = MessageResponse(
|
||||
message_id="om_stream",
|
||||
chat_id="oc_stream",
|
||||
channel=MessageChannel.Feishu,
|
||||
channel=NotificationChannel.Feishu,
|
||||
source="feishu-main",
|
||||
metadata={"feishu_streaming": {"card_id": "card_stream", "sequence": 2}},
|
||||
success=True,
|
||||
@@ -523,7 +523,7 @@ class TestAgentToolStreaming:
|
||||
def test_flush_passes_original_message_context_to_send_direct_message(self):
|
||||
"""校验刷新发送时保留原始消息上下文。"""
|
||||
handler = StreamingHandler()
|
||||
handler._channel = MessageChannel.Feishu.value
|
||||
handler._channel = NotificationChannel.Feishu.value
|
||||
handler._source = "feishu-main"
|
||||
handler._user_id = "ou_user"
|
||||
handler._username = "tester"
|
||||
@@ -603,7 +603,7 @@ class TestAgentToolStreaming:
|
||||
def test_send_voice_message_uses_native_voice_for_supported_channels(self):
|
||||
"""校验支持语音输出的渠道会发送原生语音消息。"""
|
||||
|
||||
async def _run(channel: MessageChannel):
|
||||
async def _run(channel: NotificationChannel):
|
||||
"""运行指定渠道的语音发送工具。"""
|
||||
tool = SendVoiceMessageTool(session_id="session-1", user_id="10001")
|
||||
tool.set_message_attr(
|
||||
@@ -625,22 +625,22 @@ class TestAgentToolStreaming:
|
||||
) as synthesize_speech,
|
||||
patch.object(
|
||||
SendVoiceMessageTool,
|
||||
"send_notification_message",
|
||||
"send_message",
|
||||
new_callable=AsyncMock,
|
||||
) as send_notification_message,
|
||||
) as send_message,
|
||||
):
|
||||
result = await tool.run("你好")
|
||||
return result, synthesize_speech, send_notification_message
|
||||
return result, synthesize_speech, send_message
|
||||
|
||||
for channel in (MessageChannel.Telegram, MessageChannel.Feishu, MessageChannel.WebAgent):
|
||||
result, synthesize_speech, send_notification_message = asyncio.run(
|
||||
for channel in (NotificationChannel.Telegram, NotificationChannel.Feishu, NotificationChannel.WebAgent):
|
||||
result, synthesize_speech, send_message = asyncio.run(
|
||||
_run(channel)
|
||||
)
|
||||
notification = send_notification_message.await_args.args[-1]
|
||||
notification = send_message.await_args.args[-1]
|
||||
|
||||
assert result == "语音回复已发送"
|
||||
synthesize_speech.assert_called_once_with("你好")
|
||||
send_notification_message.assert_awaited_once()
|
||||
send_message.assert_awaited_once()
|
||||
assert notification.channel == channel
|
||||
assert notification.voice_path == "/tmp/reply.opus"
|
||||
assert notification.voice_caption == "你好"
|
||||
@@ -655,7 +655,7 @@ class TestAgentToolStreaming:
|
||||
"""运行不支持语音输出渠道的语音发送工具。"""
|
||||
tool = SendVoiceMessageTool(session_id="session-1", user_id="10001")
|
||||
tool.set_message_attr(
|
||||
channel=MessageChannel.Slack.value, source="slack-main", username="tester"
|
||||
channel=NotificationChannel.Slack.value, source="slack-main", username="tester"
|
||||
)
|
||||
|
||||
with (
|
||||
@@ -669,18 +669,18 @@ class TestAgentToolStreaming:
|
||||
) as synthesize_speech,
|
||||
patch.object(
|
||||
SendVoiceMessageTool,
|
||||
"send_notification_message",
|
||||
"send_message",
|
||||
new_callable=AsyncMock,
|
||||
) as send_notification_message,
|
||||
) as send_message,
|
||||
):
|
||||
result = await tool.run("你好")
|
||||
return result, synthesize_speech, send_notification_message
|
||||
return result, synthesize_speech, send_message
|
||||
|
||||
result, synthesize_speech, send_notification_message = asyncio.run(_run())
|
||||
notification = send_notification_message.await_args.args[-1]
|
||||
result, synthesize_speech, send_message = asyncio.run(_run())
|
||||
notification = send_message.await_args.args[-1]
|
||||
|
||||
assert result == "当前渠道不支持语音回复,已自动回退为文字回复"
|
||||
synthesize_speech.assert_not_called()
|
||||
send_notification_message.assert_awaited_once()
|
||||
send_message.assert_awaited_once()
|
||||
assert notification.text == "你好"
|
||||
assert notification.voice_path is None
|
||||
|
||||
@@ -44,7 +44,7 @@ def _load_downloader_base():
|
||||
schema_types_module.ModuleType = Enum("ModuleType", {"Downloader": "downloader"})
|
||||
schema_types_module.DownloaderType = Enum("DownloaderType", {"Qbittorrent": "Qbittorrent"})
|
||||
schema_types_module.MediaServerType = Enum("MediaServerType", {"Emby": "Emby"})
|
||||
schema_types_module.MessageChannel = Enum("MessageChannel", {"Telegram": "telegram"})
|
||||
schema_types_module.NotificationChannel = Enum("NotificationChannel", {"Telegram": "telegram"})
|
||||
schema_types_module.OtherModulesType = Enum("OtherModulesType", {"Subtitle": "subtitle"})
|
||||
schema_types_module.MediaRecognizeType = Enum(
|
||||
"MediaRecognizeType", {"TheMovieDb": "themoviedb"}
|
||||
@@ -60,7 +60,7 @@ def _load_downloader_base():
|
||||
|
||||
service_module.ServiceConfigHelper = _ServiceConfigHelper
|
||||
mixins_module.ConfigReloadMixin = _ConfigReloadMixin
|
||||
schemas_module.Notification = object
|
||||
schemas_module.Message = object
|
||||
schemas_module.NotificationConf = object
|
||||
schemas_module.MediaServerConf = object
|
||||
schemas_module.DownloaderConf = object
|
||||
|
||||
+41
-41
@@ -14,13 +14,13 @@ ensure_optional_stub("Pinyin2Hanzi", is_pinyin=lambda value: False)
|
||||
|
||||
from app.modules.feishu import FeishuModule
|
||||
from app.modules.feishu.feishu import Feishu
|
||||
from app.schemas import Notification
|
||||
from app.schemas import Message
|
||||
from app.schemas.message import (
|
||||
ChannelCapability,
|
||||
ChannelCapabilityManager,
|
||||
MessageResponse,
|
||||
)
|
||||
from app.schemas.types import MessageChannel, NotificationType
|
||||
from app.schemas.types import NotificationChannel, MessageType
|
||||
|
||||
|
||||
class TestFeishu(unittest.TestCase):
|
||||
@@ -156,7 +156,7 @@ class TestFeishu(unittest.TestCase):
|
||||
)
|
||||
|
||||
self.assertIsNotNone(result)
|
||||
self.assertEqual(result.channel, MessageChannel.Feishu)
|
||||
self.assertEqual(result.channel, NotificationChannel.Feishu)
|
||||
self.assertEqual(result.userid, "ou_user_1")
|
||||
self.assertEqual(result.text, "CALLBACK:approve")
|
||||
self.assertTrue(result.is_callback)
|
||||
@@ -281,7 +281,7 @@ class TestFeishu(unittest.TestCase):
|
||||
)
|
||||
|
||||
result = client.send_notification(
|
||||
Notification(
|
||||
Message(
|
||||
title="测试标题",
|
||||
text="测试正文",
|
||||
buttons=[[{"text": "确认", "callback_data": "confirm"}]],
|
||||
@@ -316,7 +316,7 @@ class TestFeishu(unittest.TestCase):
|
||||
)
|
||||
|
||||
result = client.send_notification(
|
||||
Notification(
|
||||
Message(
|
||||
title="普通通知",
|
||||
text="海报:",
|
||||
),
|
||||
@@ -348,7 +348,7 @@ class TestFeishu(unittest.TestCase):
|
||||
with patch("app.modules.feishu.feishu.RequestUtils") as request_utils:
|
||||
request_utils.return_value.get_res.return_value = response
|
||||
result = client.send_notification(
|
||||
Notification(
|
||||
Message(
|
||||
title="测试标题",
|
||||
text="测试正文",
|
||||
image="https://example.com/poster.png",
|
||||
@@ -381,7 +381,7 @@ class TestFeishu(unittest.TestCase):
|
||||
)
|
||||
|
||||
client.send_notification(
|
||||
Notification(title="测试标题", text="测试正文"),
|
||||
Message(title="测试标题", text="测试正文"),
|
||||
userid="u_user_4",
|
||||
receive_id_type="user_id",
|
||||
)
|
||||
@@ -426,7 +426,7 @@ class TestFeishu(unittest.TestCase):
|
||||
)
|
||||
|
||||
result = client.send_notification(
|
||||
Notification(title="回复标题", text="回复正文"),
|
||||
Message(title="回复标题", text="回复正文"),
|
||||
userid="ou_user_9",
|
||||
original_message_id="om_origin",
|
||||
)
|
||||
@@ -475,8 +475,8 @@ class TestFeishu(unittest.TestCase):
|
||||
)
|
||||
|
||||
result = client.send_notification(
|
||||
Notification(
|
||||
mtype=NotificationType.Agent,
|
||||
Message(
|
||||
mtype=MessageType.Agent,
|
||||
title="MoviePilot助手",
|
||||
text="第一帧内容",
|
||||
),
|
||||
@@ -523,8 +523,8 @@ class TestFeishu(unittest.TestCase):
|
||||
with patch("app.modules.feishu.feishu.RequestUtils") as request_utils:
|
||||
request_utils.return_value.get_res.return_value = response
|
||||
result = client.send_notification(
|
||||
Notification(
|
||||
mtype=NotificationType.Agent,
|
||||
Message(
|
||||
mtype=MessageType.Agent,
|
||||
title="MoviePilot助手",
|
||||
text="找到海报 \n[详情](https://example.com/detail)",
|
||||
),
|
||||
@@ -568,8 +568,8 @@ class TestFeishu(unittest.TestCase):
|
||||
with patch("app.modules.feishu.feishu.RequestUtils") as request_utils:
|
||||
request_utils.return_value.get_res.return_value = response
|
||||
result = client.send_notification(
|
||||
Notification(
|
||||
mtype=NotificationType.Agent,
|
||||
Message(
|
||||
mtype=MessageType.Agent,
|
||||
title="MoviePilot助手",
|
||||
text="第一帧内容",
|
||||
image="https://example.com/agent.png",
|
||||
@@ -606,8 +606,8 @@ class TestFeishu(unittest.TestCase):
|
||||
)
|
||||
|
||||
result = client.send_notification(
|
||||
Notification(
|
||||
mtype=NotificationType.Agent,
|
||||
Message(
|
||||
mtype=MessageType.Agent,
|
||||
title="MoviePilot助手",
|
||||
text="第一帧内容",
|
||||
),
|
||||
@@ -988,13 +988,13 @@ class TestFeishu(unittest.TestCase):
|
||||
def test_feishu_channel_capabilities_enable_images_and_files(self):
|
||||
self.assertTrue(
|
||||
ChannelCapabilityManager.supports_capability(
|
||||
MessageChannel.Feishu,
|
||||
NotificationChannel.Feishu,
|
||||
ChannelCapability.IMAGES,
|
||||
)
|
||||
)
|
||||
self.assertTrue(
|
||||
ChannelCapabilityManager.supports_capability(
|
||||
MessageChannel.Feishu,
|
||||
NotificationChannel.Feishu,
|
||||
ChannelCapability.FILE_SENDING,
|
||||
)
|
||||
)
|
||||
@@ -1121,7 +1121,7 @@ class TestFeishu(unittest.TestCase):
|
||||
|
||||
def test_module_send_direct_message_prefers_open_id_target(self):
|
||||
module = FeishuModule()
|
||||
module._channel = MessageChannel.Feishu
|
||||
module._channel = NotificationChannel.Feishu
|
||||
conf = SimpleNamespace(name="feishu-main")
|
||||
client = MagicMock()
|
||||
client.send_notification.return_value = {
|
||||
@@ -1136,7 +1136,7 @@ class TestFeishu(unittest.TestCase):
|
||||
patch.object(module, "get_instance", return_value=client),
|
||||
):
|
||||
response = module.send_direct_message(
|
||||
Notification(
|
||||
Message(
|
||||
targets={
|
||||
"feishu_userid": "u_target",
|
||||
"feishu_openid": "ou_target",
|
||||
@@ -1158,7 +1158,7 @@ class TestFeishu(unittest.TestCase):
|
||||
def test_module_plain_direct_message_uses_literal_text_transport(self):
|
||||
"""纯文本直发不得进入会解释密钥字符的 Markdown 卡片路径。"""
|
||||
module = FeishuModule()
|
||||
module._channel = MessageChannel.Feishu
|
||||
module._channel = NotificationChannel.Feishu
|
||||
conf = SimpleNamespace(name="feishu-main")
|
||||
client = MagicMock()
|
||||
client.send_text.return_value = {
|
||||
@@ -1174,8 +1174,8 @@ class TestFeishu(unittest.TestCase):
|
||||
patch.object(module, "get_instance", return_value=client),
|
||||
):
|
||||
response = module.send_direct_message(
|
||||
Notification(
|
||||
channel=MessageChannel.Feishu,
|
||||
Message(
|
||||
channel=NotificationChannel.Feishu,
|
||||
source="feishu-main",
|
||||
userid="ou_target",
|
||||
text=literal_text,
|
||||
@@ -1336,7 +1336,7 @@ class TestFeishu(unittest.TestCase):
|
||||
|
||||
def test_module_processing_status_uses_reaction_helpers(self):
|
||||
module = FeishuModule()
|
||||
module._channel = MessageChannel.Feishu
|
||||
module._channel = NotificationChannel.Feishu
|
||||
|
||||
with (
|
||||
patch.object(
|
||||
@@ -1351,7 +1351,7 @@ class TestFeishu(unittest.TestCase):
|
||||
) as delete_reaction,
|
||||
):
|
||||
status = module.mark_message_processing_started(
|
||||
channel=MessageChannel.Feishu,
|
||||
channel=NotificationChannel.Feishu,
|
||||
source="feishu-main",
|
||||
userid="ou_x",
|
||||
message_id="om_x",
|
||||
@@ -1359,7 +1359,7 @@ class TestFeishu(unittest.TestCase):
|
||||
text="hello",
|
||||
)
|
||||
deleted = module.mark_message_processing_finished(
|
||||
channel=MessageChannel.Feishu,
|
||||
channel=NotificationChannel.Feishu,
|
||||
source="feishu-main",
|
||||
userid="ou_x",
|
||||
status=status,
|
||||
@@ -1380,7 +1380,7 @@ class TestFeishu(unittest.TestCase):
|
||||
|
||||
def test_module_finalize_message_closes_streaming_card(self):
|
||||
module = FeishuModule()
|
||||
module._channel = MessageChannel.Feishu
|
||||
module._channel = NotificationChannel.Feishu
|
||||
client = MagicMock()
|
||||
client.close_streaming_card.return_value = True
|
||||
|
||||
@@ -1394,7 +1394,7 @@ class TestFeishu(unittest.TestCase):
|
||||
MessageResponse(
|
||||
message_id="om_stream",
|
||||
chat_id="oc_stream",
|
||||
channel=MessageChannel.Feishu,
|
||||
channel=NotificationChannel.Feishu,
|
||||
source="feishu-main",
|
||||
metadata={
|
||||
"feishu_streaming": {
|
||||
@@ -1422,7 +1422,7 @@ class TestFeishu(unittest.TestCase):
|
||||
patch.object(module, "get_instance", return_value=client),
|
||||
):
|
||||
module.post_message(
|
||||
Notification(
|
||||
Message(
|
||||
file_path="/tmp/demo.txt",
|
||||
text="说明",
|
||||
title="标题",
|
||||
@@ -1430,7 +1430,7 @@ class TestFeishu(unittest.TestCase):
|
||||
)
|
||||
)
|
||||
module.post_message(
|
||||
Notification(
|
||||
Message(
|
||||
voice_path="/tmp/demo.opus",
|
||||
voice_caption="语音说明",
|
||||
userid="ou_user",
|
||||
@@ -1451,7 +1451,7 @@ class TestFeishu(unittest.TestCase):
|
||||
patch.object(module, "get_instance", return_value=client),
|
||||
):
|
||||
module.post_message(
|
||||
Notification(
|
||||
Message(
|
||||
file_path="/tmp/demo.txt",
|
||||
file_name="demo.txt",
|
||||
image="https://example.com/poster.png",
|
||||
@@ -1471,7 +1471,7 @@ class TestFeishu(unittest.TestCase):
|
||||
|
||||
def test_module_send_direct_message_sends_image_card_before_file_attachment(self):
|
||||
module = FeishuModule()
|
||||
module._channel = MessageChannel.Feishu
|
||||
module._channel = NotificationChannel.Feishu
|
||||
conf = SimpleNamespace(name="feishu-main")
|
||||
client = MagicMock()
|
||||
client.send_notification.return_value = {
|
||||
@@ -1487,8 +1487,8 @@ class TestFeishu(unittest.TestCase):
|
||||
patch.object(module, "get_instance", return_value=client),
|
||||
):
|
||||
response = module.send_direct_message(
|
||||
Notification(
|
||||
channel=MessageChannel.Feishu,
|
||||
Message(
|
||||
channel=NotificationChannel.Feishu,
|
||||
source="feishu-main",
|
||||
file_path="/tmp/demo.txt",
|
||||
file_name="demo.txt",
|
||||
@@ -1515,7 +1515,7 @@ class TestFeishu(unittest.TestCase):
|
||||
patch.object(module, "get_instance", return_value=client),
|
||||
):
|
||||
module.post_message(
|
||||
Notification(
|
||||
Message(
|
||||
title="标题",
|
||||
text="正文",
|
||||
userid="ou_user",
|
||||
@@ -1533,7 +1533,7 @@ class TestFeishu(unittest.TestCase):
|
||||
def test_module_resolve_message_target_prefers_original_chat_id(self):
|
||||
"""群聊@回复必须优先发回原会话,而不是按 open_id 发到私聊。"""
|
||||
userid, chat_id, receive_id_type = FeishuModule._resolve_message_target(
|
||||
Notification(userid="ou_user", original_chat_id="oc_group")
|
||||
Message(userid="ou_user", original_chat_id="oc_group")
|
||||
)
|
||||
self.assertIsNone(userid)
|
||||
self.assertEqual(chat_id, "oc_group")
|
||||
@@ -1541,7 +1541,7 @@ class TestFeishu(unittest.TestCase):
|
||||
|
||||
# 非回复类消息仍按原有逻辑优先 open_id。
|
||||
userid, chat_id, receive_id_type = FeishuModule._resolve_message_target(
|
||||
Notification(userid="ou_user")
|
||||
Message(userid="ou_user")
|
||||
)
|
||||
self.assertEqual(userid, "ou_user")
|
||||
self.assertIsNone(chat_id)
|
||||
@@ -1549,7 +1549,7 @@ class TestFeishu(unittest.TestCase):
|
||||
|
||||
# 无用户ID时回退 targets 中的飞书字段。
|
||||
userid, chat_id, receive_id_type = FeishuModule._resolve_message_target(
|
||||
Notification(targets={"feishu_chat_id": "oc_config"})
|
||||
Message(targets={"feishu_chat_id": "oc_config"})
|
||||
)
|
||||
self.assertIsNone(userid)
|
||||
self.assertEqual(chat_id, "oc_config")
|
||||
@@ -1557,7 +1557,7 @@ class TestFeishu(unittest.TestCase):
|
||||
def test_module_private_delivery_ignores_original_group_chat(self):
|
||||
"""私聊投递只保留用户身份,并让客户端按已记录 ID 类型发送。"""
|
||||
userid, chat_id, receive_id_type = FeishuModule._resolve_message_target(
|
||||
Notification(
|
||||
Message(
|
||||
userid="user_target",
|
||||
original_chat_id="oc_group",
|
||||
private_delivery=True,
|
||||
@@ -1580,7 +1580,7 @@ class TestFeishu(unittest.TestCase):
|
||||
patch.object(module, "get_instance", return_value=client),
|
||||
):
|
||||
module.post_message(
|
||||
Notification(
|
||||
Message(
|
||||
title="标题",
|
||||
text="正文",
|
||||
userid="ou_user",
|
||||
@@ -1609,7 +1609,7 @@ class TestFeishu(unittest.TestCase):
|
||||
}
|
||||
|
||||
result = client.send_notification(
|
||||
Notification(title="插件通知", text="无目标通知")
|
||||
Message(title="插件通知", text="无目标通知")
|
||||
)
|
||||
|
||||
self.assertTrue(result["success"])
|
||||
|
||||
@@ -8,7 +8,7 @@ ensure_optional_stub("Pinyin2Hanzi", is_pinyin=lambda value: False)
|
||||
|
||||
from app.domain.context import MediaInfo
|
||||
from app.modules.feishu.feishu import Feishu
|
||||
from app.schemas import Notification
|
||||
from app.schemas import Message
|
||||
|
||||
|
||||
def _build_feishu_client() -> Feishu:
|
||||
@@ -39,7 +39,7 @@ def test_send_medias_message_passes_first_available_image() -> None:
|
||||
return_value={"success": True},
|
||||
) as send_notification:
|
||||
result = client.send_medias_message(
|
||||
message=Notification(title="搜索结果", userid="ou_test"),
|
||||
message=Message(title="搜索结果", userid="ou_test"),
|
||||
medias=[first_media, second_media],
|
||||
)
|
||||
|
||||
|
||||
@@ -22,13 +22,13 @@ from app.application.messaging.router import (
|
||||
)
|
||||
from app.application.messaging.site import site_interaction_manager
|
||||
from app.application.messaging.skill import skill_interaction_manager
|
||||
from app.schemas.types import MessageChannel
|
||||
from app.schemas.types import NotificationChannel
|
||||
|
||||
|
||||
def _context(user_id="10001") -> InteractionContext:
|
||||
"""构造最小交互上下文。"""
|
||||
return InteractionContext(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
user_id=user_id,
|
||||
username="tester",
|
||||
@@ -181,7 +181,7 @@ class TestHasPendingInteraction(unittest.TestCase):
|
||||
site_interaction_manager.create_or_replace(
|
||||
user_id="10001",
|
||||
command="/sites",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
|
||||
@@ -20,6 +20,7 @@ from app.runtime.compat.manifest import (
|
||||
SYMBOL_ALIASES,
|
||||
VIRTUAL_PACKAGES,
|
||||
ModuleAlias,
|
||||
_MESSAGE_NOTIFICATION_SYMBOL_ALIASES,
|
||||
)
|
||||
|
||||
|
||||
@@ -316,7 +317,7 @@ def test_plugin_scan_reports_moved_symbol_import(tmp_path: Path):
|
||||
|
||||
|
||||
def test_symbol_alias_manifest_covers_all_moved_public_symbols():
|
||||
"""符号级映射清单应覆盖媒体身份与整理工作项的旧入口。"""
|
||||
"""符号级映射清单应覆盖媒体身份、整理工作项与消息/通知命名统一的旧入口。"""
|
||||
assert set(SYMBOL_ALIASES["app.domain.media"]) == {
|
||||
"MEDIA_SOURCE_ALIASES",
|
||||
"MEDIA_SOURCE_PREFIXES",
|
||||
@@ -329,8 +330,15 @@ def test_symbol_alias_manifest_covers_all_moved_public_symbols():
|
||||
assert set(SYMBOL_ALIASES["app.schemas"]) == {
|
||||
"TransferTask",
|
||||
"TransferQueue",
|
||||
}
|
||||
} | set(_MESSAGE_NOTIFICATION_SYMBOL_ALIASES)
|
||||
assert set(SYMBOL_ALIASES["app.schemas.transfer"]) == {
|
||||
"TransferTask",
|
||||
"TransferQueue",
|
||||
}
|
||||
assert set(SYMBOL_ALIASES["app.schemas.types"]) == {
|
||||
"MessageChannel",
|
||||
"NotificationType",
|
||||
}
|
||||
assert set(SYMBOL_ALIASES["app.schemas.message"]) == set(
|
||||
_MESSAGE_NOTIFICATION_SYMBOL_ALIASES
|
||||
)
|
||||
|
||||
+140
-140
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,113 @@
|
||||
"""
|
||||
message/notification 命名统一的兼容守护测试
|
||||
|
||||
边界定义:notification 表示通知渠道能力,message 表示各渠道发送或接收的消息。
|
||||
canonical 源码不保留任何旧名别名,旧名一律经由 runtime/compat 映射表
|
||||
(SYMBOL_ALIASES)在导入器层惰性解析,供存量插件继续使用。
|
||||
"""
|
||||
import importlib
|
||||
|
||||
from app.runtime.compat.manifest import SYMBOL_ALIASES
|
||||
from app.schemas.message import (
|
||||
IncomingMessage,
|
||||
Message,
|
||||
MessageClearBefore,
|
||||
MessageClearData,
|
||||
MessageClearScope,
|
||||
MessageHistoryItem,
|
||||
)
|
||||
from app.schemas.notification import (
|
||||
ChannelCapabilities,
|
||||
ChannelCapability,
|
||||
ChannelCapabilityManager,
|
||||
)
|
||||
from app.schemas.types import MessageType, NotificationChannel
|
||||
|
||||
|
||||
# 旧名 -> canonical 对象的期望映射,覆盖全部登记的兼容入口
|
||||
_EXPECTED_RESOLUTIONS = {
|
||||
("app.schemas.types", "MessageChannel"): NotificationChannel,
|
||||
("app.schemas.types", "NotificationType"): MessageType,
|
||||
("app.schemas.message", "Notification"): Message,
|
||||
("app.schemas.message", "CommingMessage"): IncomingMessage,
|
||||
("app.schemas.message", "NotificationHistoryItem"): MessageHistoryItem,
|
||||
("app.schemas.message", "NotificationClearScope"): MessageClearScope,
|
||||
("app.schemas.message", "NotificationClearBefore"): MessageClearBefore,
|
||||
("app.schemas.message", "NotificationClearData"): MessageClearData,
|
||||
("app.schemas.message", "ChannelCapability"): ChannelCapability,
|
||||
("app.schemas.message", "ChannelCapabilities"): ChannelCapabilities,
|
||||
("app.schemas.message", "ChannelCapabilityManager"): ChannelCapabilityManager,
|
||||
}
|
||||
|
||||
|
||||
def test_canonical_modules_do_not_define_legacy_names():
|
||||
"""canonical 模块自身不得保留旧名物理别名,旧名只能来自兼容映射。"""
|
||||
types_module = importlib.import_module("app.schemas.types")
|
||||
message_module = importlib.import_module("app.schemas.message")
|
||||
|
||||
for legacy_name in ("MessageChannel", "NotificationType"):
|
||||
assert legacy_name not in types_module.__dict__
|
||||
for legacy_name in (
|
||||
"Notification",
|
||||
"CommingMessage",
|
||||
"NotificationHistoryItem",
|
||||
"NotificationClearScope",
|
||||
"NotificationClearBefore",
|
||||
"NotificationClearData",
|
||||
):
|
||||
assert legacy_name not in message_module.__dict__
|
||||
|
||||
|
||||
def test_manifest_registers_all_legacy_message_notification_symbols():
|
||||
"""映射表必须登记全部旧名,且目标指向当前 canonical 符号。"""
|
||||
for (module_name, legacy_name), canonical in _EXPECTED_RESOLUTIONS.items():
|
||||
alias = SYMBOL_ALIASES[module_name][legacy_name]
|
||||
target = getattr(importlib.import_module(alias.target_module), alias.target_name)
|
||||
assert target is canonical, (module_name, legacy_name)
|
||||
|
||||
|
||||
def test_legacy_symbol_imports_resolve_through_compat_hook():
|
||||
"""插件旧导入路径应经兼容钩子解析到 canonical 对象。"""
|
||||
for (module_name, legacy_name), canonical in _EXPECTED_RESOLUTIONS.items():
|
||||
module = importlib.import_module(module_name)
|
||||
assert getattr(module, legacy_name) is canonical, (module_name, legacy_name)
|
||||
|
||||
|
||||
def test_schemas_package_level_legacy_imports_resolve():
|
||||
"""from app.schemas import 旧名 的插件写法应继续可用。"""
|
||||
schemas_package = importlib.import_module("app.schemas")
|
||||
|
||||
assert schemas_package.MessageChannel is NotificationChannel
|
||||
assert schemas_package.NotificationType is MessageType
|
||||
assert schemas_package.Notification is Message
|
||||
assert schemas_package.CommingMessage is IncomingMessage
|
||||
assert schemas_package.NotificationHistoryItem is MessageHistoryItem
|
||||
assert schemas_package.ChannelCapabilityManager is ChannelCapabilityManager
|
||||
|
||||
|
||||
def test_legacy_from_import_statement_works():
|
||||
"""from ... import 语句形式的旧导入应正常执行。"""
|
||||
scope: dict = {}
|
||||
exec(
|
||||
"from app.schemas import Notification, MessageChannel, NotificationType\n"
|
||||
"from app.schemas.message import CommingMessage\n"
|
||||
"from app.schemas.types import MessageChannel as LegacyChannel\n",
|
||||
scope,
|
||||
)
|
||||
|
||||
assert scope["Notification"] is Message
|
||||
assert scope["MessageChannel"] is NotificationChannel
|
||||
assert scope["NotificationType"] is MessageType
|
||||
assert scope["CommingMessage"] is IncomingMessage
|
||||
assert scope["LegacyChannel"] is NotificationChannel
|
||||
|
||||
|
||||
def test_legacy_and_canonical_instances_interchangeable():
|
||||
"""旧名构造的实例应与 canonical 类型互相兼容(同一类对象)。"""
|
||||
legacy_message = importlib.import_module("app.schemas").Notification(title="t")
|
||||
|
||||
assert isinstance(legacy_message, Message)
|
||||
assert isinstance(
|
||||
Message(title="t"),
|
||||
importlib.import_module("app.schemas.message").Notification,
|
||||
)
|
||||
@@ -8,11 +8,11 @@ from app.domain.context import Context, MediaInfo, TorrentInfo
|
||||
from app.domain.meta.metabase import MetaBase
|
||||
from app.db import AsyncSessionFactory, SessionFactory
|
||||
from app.db.oper.message import MessageOper
|
||||
from app.db.models.message import Message
|
||||
from app.db.models.message import Message as MessageModel
|
||||
from app.db.oper.systemconfig import SystemConfigOper
|
||||
from app.application.messaging.message import MessageHelper
|
||||
from app.schemas import Notification, NotificationClearScope
|
||||
from app.schemas.types import MediaType, NotificationType, SystemConfigKey
|
||||
from app.schemas import Message, MessageClearScope
|
||||
from app.schemas.types import MediaType, MessageType, SystemConfigKey
|
||||
|
||||
|
||||
def _clear_messages() -> None:
|
||||
@@ -20,7 +20,7 @@ def _clear_messages() -> None:
|
||||
清空消息表,隔离通知测试数据。
|
||||
"""
|
||||
with SessionFactory() as db:
|
||||
db.query(Message).delete()
|
||||
db.query(MessageModel).delete()
|
||||
db.commit()
|
||||
SystemConfigOper().delete(SystemConfigKey.NotificationClearBefore)
|
||||
|
||||
@@ -39,7 +39,7 @@ def _set_message_time(title: str, reg_time: str) -> None:
|
||||
调整测试消息时间,避免消息写入时的当前秒影响清理边界断言。
|
||||
"""
|
||||
with SessionFactory() as db:
|
||||
db.query(Message).filter(Message.title == title).update({"reg_time": reg_time})
|
||||
db.query(MessageModel).filter(MessageModel.title == title).update({"reg_time": reg_time})
|
||||
db.commit()
|
||||
|
||||
|
||||
@@ -49,9 +49,9 @@ def test_notification_history_only_lists_sent_messages() -> None:
|
||||
"""
|
||||
_clear_messages()
|
||||
oper = MessageOper()
|
||||
oper.add(title="系统通知", text="下载完成", action=1, mtype=NotificationType.Download)
|
||||
oper.add(title="系统通知", text="下载完成", action=1, mtype=MessageType.Download)
|
||||
oper.add(title="用户消息", text="帮我搜索", action=0)
|
||||
oper.add(title="智能体回复", text="已处理", action=1, mtype=NotificationType.Agent)
|
||||
oper.add(title="智能体回复", text="已处理", action=1, mtype=MessageType.Agent)
|
||||
|
||||
messages = MessageOper().list_by_page(page=1, count=10)
|
||||
assert [message.title for message in messages if message.action == 1] == ["智能体回复", "系统通知"]
|
||||
@@ -63,9 +63,9 @@ def test_web_message_history_returns_all_messages() -> None:
|
||||
"""
|
||||
_clear_messages()
|
||||
oper = MessageOper()
|
||||
oper.add(title="智能体回复", text="已处理", action=1, mtype=NotificationType.Agent)
|
||||
oper.add(title="智能体回复", text="已处理", action=1, mtype=MessageType.Agent)
|
||||
oper.add(title="用户消息", text="/ai 帮我处理", action=0)
|
||||
oper.add(title="普通通知", text="下载完成", action=1, mtype=NotificationType.Download)
|
||||
oper.add(title="普通通知", text="下载完成", action=1, mtype=MessageType.Download)
|
||||
|
||||
messages = MessageOper().list_by_page(page=1, count=10)
|
||||
assert [message.title for message in messages] == ["普通通知", "用户消息", "智能体回复"]
|
||||
@@ -81,7 +81,7 @@ def test_notification_clear_marker_filters_history_across_requests() -> None:
|
||||
title="旧系统通知",
|
||||
text="任务失败",
|
||||
action=1,
|
||||
mtype=NotificationType.Other,
|
||||
mtype=MessageType.Other,
|
||||
)
|
||||
oper.add(
|
||||
title="旧媒体通知",
|
||||
@@ -92,7 +92,7 @@ def test_notification_clear_marker_filters_history_across_requests() -> None:
|
||||
_set_message_time("旧系统通知", "2026-01-01 00:00:00")
|
||||
_set_message_time("旧媒体通知", "2026-01-01 00:00:00")
|
||||
|
||||
asyncio.run(clear_notification_message(scope=NotificationClearScope.Media))
|
||||
asyncio.run(clear_notification_message(scope=MessageClearScope.Media))
|
||||
|
||||
oper.add(
|
||||
title="新媒体通知",
|
||||
@@ -183,8 +183,8 @@ def test_notification_post_message_is_persisted_without_sse_queue() -> None:
|
||||
chain.eventmanager.send_event = Mock()
|
||||
|
||||
chain.post_message(
|
||||
Notification(
|
||||
mtype=NotificationType.Download,
|
||||
Message(
|
||||
mtype=MessageType.Download,
|
||||
title="下载完成",
|
||||
text="影片已加入下载器",
|
||||
)
|
||||
@@ -193,7 +193,7 @@ def test_notification_post_message_is_persisted_without_sse_queue() -> None:
|
||||
messages = MessageOper().list_by_page(page=1, count=10)
|
||||
assert len(messages) == 1
|
||||
assert messages[0].title == "下载完成"
|
||||
assert messages[0].mtype == NotificationType.Download.value
|
||||
assert messages[0].mtype == MessageType.Download.value
|
||||
assert helper.get() is None
|
||||
chain.messagequeue.send_message.assert_called_once()
|
||||
|
||||
@@ -211,8 +211,8 @@ def test_agent_notification_post_message_is_persisted_without_sse_queue() -> Non
|
||||
chain.eventmanager.send_event = Mock()
|
||||
|
||||
chain.post_message(
|
||||
Notification(
|
||||
mtype=NotificationType.Agent,
|
||||
Message(
|
||||
mtype=MessageType.Agent,
|
||||
title="MoviePilot助手",
|
||||
text="已完成处理",
|
||||
)
|
||||
@@ -221,7 +221,7 @@ def test_agent_notification_post_message_is_persisted_without_sse_queue() -> Non
|
||||
messages = MessageOper().list_by_page(page=1, count=10)
|
||||
assert len(messages) == 1
|
||||
assert messages[0].title == "MoviePilot助手"
|
||||
assert messages[0].mtype == NotificationType.Agent.value
|
||||
assert messages[0].mtype == MessageType.Agent.value
|
||||
assert helper.get() is None
|
||||
chain.messagequeue.send_message.assert_called_once()
|
||||
|
||||
@@ -237,7 +237,7 @@ def test_transient_notification_post_message_skips_history_but_dispatches() -> N
|
||||
chain.eventmanager.send_event = Mock()
|
||||
|
||||
chain.post_message(
|
||||
Notification(
|
||||
Message(
|
||||
title="请选择下载目录",
|
||||
text="1. 默认目录",
|
||||
save_history=False,
|
||||
@@ -270,11 +270,11 @@ def test_transient_media_and_torrent_lists_skip_history_but_dispatch() -> None:
|
||||
chain.messagequeue.send_message = Mock()
|
||||
|
||||
chain.post_medias_message(
|
||||
Notification(title="请选择媒体", save_history=False),
|
||||
Message(title="请选择媒体", save_history=False),
|
||||
medias=[media],
|
||||
)
|
||||
chain.post_torrents_message(
|
||||
Notification(title="请选择资源", save_history=False),
|
||||
Message(title="请选择资源", save_history=False),
|
||||
torrents=[torrent],
|
||||
)
|
||||
|
||||
|
||||
@@ -9,20 +9,20 @@ from app.agent import _finish_processing_status
|
||||
from app.modules.discord import DiscordModule
|
||||
from app.modules.discord.discord import Discord
|
||||
from app.modules.slack import SlackModule
|
||||
from app.schemas.message import ChannelCapability, ChannelCapabilityManager
|
||||
from app.schemas.types import MessageChannel
|
||||
from app.schemas.notification import ChannelCapability, ChannelCapabilityManager
|
||||
from app.schemas.types import NotificationChannel
|
||||
|
||||
|
||||
class TestMessageProcessingStatus(unittest.TestCase):
|
||||
def test_processing_status_capability_only_enabled_for_supported_channels(self):
|
||||
supported = {
|
||||
MessageChannel.Telegram,
|
||||
MessageChannel.Feishu,
|
||||
MessageChannel.Slack,
|
||||
MessageChannel.Discord,
|
||||
NotificationChannel.Telegram,
|
||||
NotificationChannel.Feishu,
|
||||
NotificationChannel.Slack,
|
||||
NotificationChannel.Discord,
|
||||
}
|
||||
|
||||
for channel in MessageChannel:
|
||||
for channel in NotificationChannel:
|
||||
self.assertEqual(
|
||||
ChannelCapabilityManager.supports_capability(
|
||||
channel, ChannelCapability.PROCESSING_STATUS
|
||||
@@ -32,7 +32,7 @@ class TestMessageProcessingStatus(unittest.TestCase):
|
||||
|
||||
def test_slack_processing_status_uses_reaction(self):
|
||||
module = SlackModule()
|
||||
module._channel = MessageChannel.Slack
|
||||
module._channel = NotificationChannel.Slack
|
||||
client = MagicMock()
|
||||
client.add_reaction.return_value = True
|
||||
client.remove_reaction.return_value = True
|
||||
@@ -44,7 +44,7 @@ class TestMessageProcessingStatus(unittest.TestCase):
|
||||
patch.object(module, "get_instance", return_value=client),
|
||||
):
|
||||
status = module.mark_message_processing_started(
|
||||
channel=MessageChannel.Slack,
|
||||
channel=NotificationChannel.Slack,
|
||||
source="slack-main",
|
||||
userid="U01",
|
||||
message_id="1710000000.000100",
|
||||
@@ -52,7 +52,7 @@ class TestMessageProcessingStatus(unittest.TestCase):
|
||||
text="hello",
|
||||
)
|
||||
removed = module.mark_message_processing_finished(
|
||||
channel=MessageChannel.Slack,
|
||||
channel=NotificationChannel.Slack,
|
||||
source="slack-main",
|
||||
userid="U01",
|
||||
status=status,
|
||||
@@ -99,7 +99,7 @@ class TestMessageProcessingStatus(unittest.TestCase):
|
||||
|
||||
def test_discord_processing_status_starts_and_stops_typing(self):
|
||||
module = DiscordModule()
|
||||
module._channel = MessageChannel.Discord
|
||||
module._channel = NotificationChannel.Discord
|
||||
client = MagicMock()
|
||||
client.start_typing.return_value = True
|
||||
client.stop_typing.return_value = True
|
||||
@@ -111,7 +111,7 @@ class TestMessageProcessingStatus(unittest.TestCase):
|
||||
patch.object(module, "get_instance", return_value=client),
|
||||
):
|
||||
status = module.mark_message_processing_started(
|
||||
channel=MessageChannel.Discord,
|
||||
channel=NotificationChannel.Discord,
|
||||
source="discord-main",
|
||||
userid="10001",
|
||||
message_id="20002",
|
||||
@@ -119,7 +119,7 @@ class TestMessageProcessingStatus(unittest.TestCase):
|
||||
text="hello",
|
||||
)
|
||||
finished = module.mark_message_processing_finished(
|
||||
channel=MessageChannel.Discord,
|
||||
channel=NotificationChannel.Discord,
|
||||
source="discord-main",
|
||||
userid="10001",
|
||||
status=status,
|
||||
@@ -132,7 +132,7 @@ class TestMessageProcessingStatus(unittest.TestCase):
|
||||
|
||||
def test_agent_finish_processing_status_uses_module_interface(self):
|
||||
status = {
|
||||
"channel": MessageChannel.Telegram.value,
|
||||
"channel": NotificationChannel.Telegram.value,
|
||||
"source": "telegram-main",
|
||||
"userid": "10001",
|
||||
"message_id": None,
|
||||
|
||||
@@ -12,7 +12,7 @@ import pytest
|
||||
|
||||
from app.chain.notification import NotificationChain
|
||||
from app.modules.wechatclawbot import WechatClawBotModule
|
||||
from app.schemas.types import MessageChannel, NotificationAction
|
||||
from app.schemas.types import NotificationChannel, NotificationAction
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -23,7 +23,7 @@ def module():
|
||||
def test_channel_manage_routes_only_matching_channel(module):
|
||||
"""非本渠道的管理请求返回 None,run_module 分发将继续执行其它模块。"""
|
||||
result = module.channel_manage(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
action=NotificationAction.STATUS,
|
||||
)
|
||||
assert result is None
|
||||
@@ -32,7 +32,7 @@ def test_channel_manage_routes_only_matching_channel(module):
|
||||
def test_channel_manage_rejects_unknown_action(module):
|
||||
"""动作词汇表之外的请求返回统一错误结构。"""
|
||||
result = module.channel_manage(
|
||||
channel=MessageChannel.WechatClawBot,
|
||||
channel=NotificationChannel.WechatClawBot,
|
||||
action="not_an_action",
|
||||
)
|
||||
assert result["success"] is False
|
||||
@@ -43,7 +43,7 @@ def test_channel_manage_requires_saved_config_without_form_params(module, monkey
|
||||
"""无任何配置且未提供表单参数时,返回提示保存配置的错误。"""
|
||||
monkeypatch.setattr(module, "get_instance", lambda name=None: None)
|
||||
result = module.channel_manage(
|
||||
channel=MessageChannel.WechatClawBot,
|
||||
channel=NotificationChannel.WechatClawBot,
|
||||
action=NotificationAction.TEST_CONNECTION,
|
||||
)
|
||||
assert result["success"] is False
|
||||
@@ -67,7 +67,7 @@ def test_channel_manage_builds_temporary_client_from_form_params(module, monkeyp
|
||||
)
|
||||
|
||||
result = module.channel_manage(
|
||||
channel=MessageChannel.WechatClawBot,
|
||||
channel=NotificationChannel.WechatClawBot,
|
||||
action=NotificationAction.TEST_CONNECTION,
|
||||
source="预览渠道",
|
||||
WECHATCLAWBOT_BASE_URL="http://127.0.0.1:1",
|
||||
@@ -91,7 +91,7 @@ def test_channel_manage_prefers_saved_instance(module, monkeypatch):
|
||||
monkeypatch.setattr(module, "get_instance", lambda name=None: saved)
|
||||
|
||||
result = module.channel_manage(
|
||||
channel=MessageChannel.WechatClawBot,
|
||||
channel=NotificationChannel.WechatClawBot,
|
||||
action=NotificationAction.STATUS,
|
||||
source="已保存",
|
||||
)
|
||||
@@ -113,7 +113,7 @@ def test_channel_manage_migrate_cache_dispatches_without_client(module, monkeypa
|
||||
)
|
||||
|
||||
result = module.channel_manage(
|
||||
channel=MessageChannel.WechatClawBot,
|
||||
channel=NotificationChannel.WechatClawBot,
|
||||
action=NotificationAction.MIGRATE_CACHE,
|
||||
old_name="旧名",
|
||||
new_name="新名",
|
||||
|
||||
@@ -21,7 +21,7 @@ from app.domain.context import MUSIC_ENTITY_ALBUM, MusicInfo
|
||||
from app.domain.meta.metamusic import MetaMusic
|
||||
from app.db.oper.systemconfig import SystemConfigOper
|
||||
from app.application.messaging.message import MessageTemplateHelper, TemplateContextBuilder, TemplateHelper
|
||||
from app.schemas.message import Notification
|
||||
from app.schemas.message import Message
|
||||
from app.schemas.types import ContentType, SystemConfigKey
|
||||
|
||||
MUSIC_ORGANIZE_TEMPLATE = """
|
||||
@@ -184,7 +184,7 @@ def test_message_renders_from_db_config(notification_templates: SystemConfigOper
|
||||
SystemConfigKey.NotificationTemplates,
|
||||
{"organizeSuccess": MUSIC_ORGANIZE_TEMPLATE},
|
||||
)
|
||||
message = Notification(ctype=ContentType.OrganizeSuccess)
|
||||
message = Message(ctype=ContentType.OrganizeSuccess)
|
||||
|
||||
MessageTemplateHelper.render(message, **MUSIC_CONTEXT)
|
||||
|
||||
@@ -199,7 +199,7 @@ def test_message_without_template_config_stays_unchanged(
|
||||
数据库中没有模板配置时消息应保持原样,不应渲染也不应报错。
|
||||
"""
|
||||
notification_templates.set(SystemConfigKey.NotificationTemplates, None)
|
||||
message = Notification(ctype=ContentType.OrganizeSuccess)
|
||||
message = Message(ctype=ContentType.OrganizeSuccess)
|
||||
|
||||
MessageTemplateHelper.render(message, **MUSIC_CONTEXT)
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ from app.agent.skills.registry import (
|
||||
SkillMarketSource,
|
||||
settings as skill_settings,
|
||||
)
|
||||
from app.schemas.types import MessageChannel
|
||||
from app.schemas.types import NotificationChannel
|
||||
|
||||
|
||||
def _build_skill_zip(skill_dir: str, skill_name: str) -> bytes:
|
||||
@@ -62,7 +62,7 @@ class TestSkillsCommand(unittest.TestCase):
|
||||
chain = MessageChain()
|
||||
skill_interaction_manager.create_or_replace(
|
||||
user_id="10001",
|
||||
channel=MessageChannel.Wechat,
|
||||
channel=NotificationChannel.Wechat,
|
||||
source="wechat-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -72,7 +72,7 @@ class TestSkillsCommand(unittest.TestCase):
|
||||
return_value=True,
|
||||
) as handle_text, patch.object(chain, "_handle_ai_message") as handle_ai:
|
||||
chain.handle_message(
|
||||
channel=MessageChannel.Wechat,
|
||||
channel=NotificationChannel.Wechat,
|
||||
source="wechat-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -86,14 +86,14 @@ class TestSkillsCommand(unittest.TestCase):
|
||||
chain = SkillInteractionHandler(messenger=MessageChain())
|
||||
skill_interaction_manager.create_or_replace(
|
||||
user_id="10001",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
|
||||
with patch.object(chain._messenger, "post_message") as post_message:
|
||||
handled = chain.handle_text_interaction(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -110,7 +110,7 @@ class TestSkillsCommand(unittest.TestCase):
|
||||
chain = MessageChain()
|
||||
request = skill_interaction_manager.create_or_replace(
|
||||
user_id="10001",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -122,7 +122,7 @@ class TestSkillsCommand(unittest.TestCase):
|
||||
chain._handle_callback(
|
||||
callback_data=f"skills:{request.request_id}:market",
|
||||
context=InteractionContext(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
user_id="10001",
|
||||
username="tester",
|
||||
@@ -390,7 +390,7 @@ class TestSkillsCommand(unittest.TestCase):
|
||||
chain = SkillInteractionHandler(messenger=MessageChain())
|
||||
request = skill_interaction_manager.create_or_replace(
|
||||
user_id="10001",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -423,7 +423,7 @@ class TestSkillsCommand(unittest.TestCase):
|
||||
chain = SkillInteractionHandler(messenger=MessageChain())
|
||||
request = skill_interaction_manager.create_or_replace(
|
||||
user_id="10001",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -467,7 +467,7 @@ class TestSkillsCommand(unittest.TestCase):
|
||||
chain = SkillInteractionHandler(messenger=MessageChain())
|
||||
request = skill_interaction_manager.create_or_replace(
|
||||
user_id="10001",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -510,7 +510,7 @@ class TestSkillsCommand(unittest.TestCase):
|
||||
chain = SkillInteractionHandler(messenger=MessageChain())
|
||||
request = skill_interaction_manager.create_or_replace(
|
||||
user_id="10001",
|
||||
channel=MessageChannel.WebAgent,
|
||||
channel=NotificationChannel.WebAgent,
|
||||
source="web-agent",
|
||||
username="tester",
|
||||
)
|
||||
@@ -552,7 +552,7 @@ class TestSkillsCommand(unittest.TestCase):
|
||||
chain = SkillInteractionHandler(messenger=MessageChain())
|
||||
request = skill_interaction_manager.create_or_replace(
|
||||
user_id="10001",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -560,7 +560,7 @@ class TestSkillsCommand(unittest.TestCase):
|
||||
with patch.object(chain, "_render_interaction") as render:
|
||||
handled = chain.handle_callback_interaction(
|
||||
callback_data=f"skills:{request.request_id}:search",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -575,7 +575,7 @@ class TestSkillsCommand(unittest.TestCase):
|
||||
chain = SkillInteractionHandler(messenger=MessageChain())
|
||||
request = skill_interaction_manager.create_or_replace(
|
||||
user_id="10001",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -583,7 +583,7 @@ class TestSkillsCommand(unittest.TestCase):
|
||||
|
||||
with patch.object(chain, "_render_interaction") as render:
|
||||
handled = chain.handle_text_interaction(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -600,7 +600,7 @@ class TestSkillsCommand(unittest.TestCase):
|
||||
chain = SkillInteractionHandler(messenger=MessageChain())
|
||||
request = skill_interaction_manager.create_or_replace(
|
||||
user_id="10001",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -609,7 +609,7 @@ class TestSkillsCommand(unittest.TestCase):
|
||||
|
||||
with patch.object(chain, "_render_interaction") as render:
|
||||
handled = chain.handle_text_interaction(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -625,7 +625,7 @@ class TestSkillsCommand(unittest.TestCase):
|
||||
chain = SkillInteractionHandler(messenger=MessageChain())
|
||||
request = skill_interaction_manager.create_or_replace(
|
||||
user_id="10001",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -633,7 +633,7 @@ class TestSkillsCommand(unittest.TestCase):
|
||||
with patch.object(chain, "_render_interaction") as render:
|
||||
handled = chain.handle_callback_interaction(
|
||||
callback_data=f"skills:{request.request_id}:source-add",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -648,7 +648,7 @@ class TestSkillsCommand(unittest.TestCase):
|
||||
chain = SkillInteractionHandler(messenger=MessageChain())
|
||||
request = skill_interaction_manager.create_or_replace(
|
||||
user_id="10001",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -663,7 +663,7 @@ class TestSkillsCommand(unittest.TestCase):
|
||||
chain._messenger, "post_message"
|
||||
) as post_message:
|
||||
handled = chain.handle_text_interaction(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -680,7 +680,7 @@ class TestSkillsCommand(unittest.TestCase):
|
||||
chain = SkillInteractionHandler(messenger=MessageChain())
|
||||
request = skill_interaction_manager.create_or_replace(
|
||||
user_id="10001",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -693,7 +693,7 @@ class TestSkillsCommand(unittest.TestCase):
|
||||
chain._messenger, "post_message"
|
||||
) as post_message:
|
||||
handled = chain.handle_text_interaction(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -710,7 +710,7 @@ class TestSkillsCommand(unittest.TestCase):
|
||||
chain = SkillInteractionHandler(messenger=MessageChain())
|
||||
request = skill_interaction_manager.create_or_replace(
|
||||
user_id="10001",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -754,7 +754,7 @@ class TestSkillsCommand(unittest.TestCase):
|
||||
chain._messenger, "post_message"
|
||||
) as post_message:
|
||||
chain._update_or_post_message(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -766,7 +766,7 @@ class TestSkillsCommand(unittest.TestCase):
|
||||
)
|
||||
|
||||
edit_message.assert_called_once_with(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
message_id=123,
|
||||
chat_id="456",
|
||||
|
||||
@@ -15,7 +15,7 @@ from app.application.messaging.interaction import InteractionContext
|
||||
from app.chain.site import SiteChain, site_interaction_manager
|
||||
from app.application.messaging.skill import skill_interaction_manager
|
||||
from app.chain.subscribe import SubscribeChain, subscribe_interaction_manager
|
||||
from app.schemas.types import MessageChannel
|
||||
from app.schemas.types import NotificationChannel
|
||||
|
||||
|
||||
class TestSlashCommandInteractions(unittest.TestCase):
|
||||
@@ -28,14 +28,14 @@ class TestSlashCommandInteractions(unittest.TestCase):
|
||||
chain = MessageChain()
|
||||
skill_interaction_manager.create_or_replace(
|
||||
user_id="10001",
|
||||
channel=MessageChannel.Wechat,
|
||||
channel=NotificationChannel.Wechat,
|
||||
source="wechat-test",
|
||||
username="tester",
|
||||
)
|
||||
site_interaction_manager.create_or_replace(
|
||||
user_id="10001",
|
||||
command="/sites",
|
||||
channel=MessageChannel.Wechat,
|
||||
channel=NotificationChannel.Wechat,
|
||||
source="wechat-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -47,7 +47,7 @@ class TestSlashCommandInteractions(unittest.TestCase):
|
||||
"app.chain.message.SkillInteractionHandler.handle_text_interaction"
|
||||
) as handle_skills:
|
||||
chain.handle_message(
|
||||
channel=MessageChannel.Wechat,
|
||||
channel=NotificationChannel.Wechat,
|
||||
source="wechat-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -62,14 +62,14 @@ class TestSlashCommandInteractions(unittest.TestCase):
|
||||
site_interaction_manager.create_or_replace(
|
||||
user_id="10001",
|
||||
command="/sites",
|
||||
channel=MessageChannel.Wechat,
|
||||
channel=NotificationChannel.Wechat,
|
||||
source="wechat-test",
|
||||
username="tester",
|
||||
)
|
||||
subscribe_interaction_manager.create_or_replace(
|
||||
user_id="10001",
|
||||
command="/subscribes",
|
||||
channel=MessageChannel.Wechat,
|
||||
channel=NotificationChannel.Wechat,
|
||||
source="wechat-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -81,7 +81,7 @@ class TestSlashCommandInteractions(unittest.TestCase):
|
||||
"app.chain.message.SiteChain.handle_text_interaction"
|
||||
) as handle_sites:
|
||||
chain.handle_message(
|
||||
channel=MessageChannel.Wechat,
|
||||
channel=NotificationChannel.Wechat,
|
||||
source="wechat-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -96,7 +96,7 @@ class TestSlashCommandInteractions(unittest.TestCase):
|
||||
request = site_interaction_manager.create_or_replace(
|
||||
user_id="10001",
|
||||
command="/sites",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -108,7 +108,7 @@ class TestSlashCommandInteractions(unittest.TestCase):
|
||||
chain._handle_callback(
|
||||
callback_data=f"sites:{request.request_id}:refresh",
|
||||
context=InteractionContext(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
user_id="10001",
|
||||
username="tester",
|
||||
@@ -122,7 +122,7 @@ class TestSlashCommandInteractions(unittest.TestCase):
|
||||
request = subscribe_interaction_manager.create_or_replace(
|
||||
user_id="10001",
|
||||
command="/subscribes",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
@@ -134,7 +134,7 @@ class TestSlashCommandInteractions(unittest.TestCase):
|
||||
chain._handle_callback(
|
||||
callback_data=f"subscribes:{request.request_id}:refresh",
|
||||
context=InteractionContext(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
user_id="10001",
|
||||
username="tester",
|
||||
@@ -148,14 +148,14 @@ class TestSlashCommandInteractions(unittest.TestCase):
|
||||
site_interaction_manager.create_or_replace(
|
||||
user_id="10001",
|
||||
command="/sites",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
|
||||
with patch.object(chain, "post_message") as post_message:
|
||||
handled = chain.handle_text_interaction(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -173,14 +173,14 @@ class TestSlashCommandInteractions(unittest.TestCase):
|
||||
subscribe_interaction_manager.create_or_replace(
|
||||
user_id="10001",
|
||||
command="/subscribes",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
|
||||
with patch.object(chain, "post_message") as post_message:
|
||||
handled = chain.handle_text_interaction(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -210,7 +210,7 @@ class TestSlashCommandInteractions(unittest.TestCase):
|
||||
with patch("app.chain.site.SiteOper.list", return_value=fake_sites), patch.object(
|
||||
chain, "post_message"
|
||||
) as post_message:
|
||||
chain.remote_list(channel=MessageChannel.Web, userid="u1", source="web")
|
||||
chain.remote_list(channel=NotificationChannel.Web, userid="u1", source="web")
|
||||
|
||||
notification = post_message.call_args[0][0]
|
||||
self.assertIn("| ID | 站点 | 状态 | Cookie | 渲染 | 域名 |", notification.text)
|
||||
@@ -234,7 +234,7 @@ class TestSlashCommandInteractions(unittest.TestCase):
|
||||
with patch(
|
||||
"app.chain.subscribe.SubscribeOper.list", return_value=fake_subscribes
|
||||
), patch.object(chain, "post_message") as post_message:
|
||||
chain.remote_list(channel=MessageChannel.Web, userid="u1", source="web")
|
||||
chain.remote_list(channel=NotificationChannel.Web, userid="u1", source="web")
|
||||
|
||||
notification = post_message.call_args[0][0]
|
||||
self.assertIn("| ID | 名称 | 类型 | 年份 | 季/进度 | 状态 |", notification.text)
|
||||
@@ -256,7 +256,7 @@ class TestUpdateOrPostMessage(unittest.TestCase):
|
||||
|
||||
update_or_post_message(
|
||||
chain=chain,
|
||||
channel=MessageChannel.Feishu,
|
||||
channel=NotificationChannel.Feishu,
|
||||
source="feishu-main",
|
||||
userid="ou_user",
|
||||
username="tester",
|
||||
|
||||
@@ -242,7 +242,7 @@ def _load_subscribe_chain_class():
|
||||
def __init__(self, **kwargs):
|
||||
self.__dict__.update(kwargs)
|
||||
|
||||
schemas_module.Notification = _Notification
|
||||
schemas_module.Message = _Notification
|
||||
schemas_module.Subscribe = _SubscribeSchema
|
||||
schemas_module.NotExistMediaInfo = _NotExistMediaInfo
|
||||
schemas_module.SubscribeEpisodeInfo = _SubscribeEpisodeInfo
|
||||
|
||||
@@ -13,7 +13,7 @@ from app.db.oper.message import MessageOper
|
||||
from app.modules.indexer import IndexerModule
|
||||
from app.modules.indexer.parser.sunnypt import SunnyPTSiteUserInfo
|
||||
from app.modules.indexer.spider.sunnypt import SunnyPTSpider
|
||||
from app.schemas import MediaSource, MediaType, NotificationType
|
||||
from app.schemas import MediaSource, MediaType, MessageType
|
||||
|
||||
|
||||
class _FakeResponse:
|
||||
@@ -319,7 +319,7 @@ def test_site_messages_are_deduplicated_by_persisted_source(monkeypatch):
|
||||
duplicate_source = "sunnypt-message:9001-dedup-test"
|
||||
MessageOper().add(
|
||||
source=duplicate_source,
|
||||
mtype=NotificationType.SiteMessage,
|
||||
mtype=MessageType.SiteMessage,
|
||||
title="existing",
|
||||
text="existing",
|
||||
)
|
||||
|
||||
@@ -12,7 +12,7 @@ sys.modules.setdefault("psutil", ModuleType("psutil"))
|
||||
|
||||
from app.chain.message import MessageChain
|
||||
from app.application.messaging.message import MessageQueueManager
|
||||
from app.schemas import Notification
|
||||
from app.schemas import Message
|
||||
from app.foundation.identity import (
|
||||
SYSTEM_INTERNAL_USER_ID,
|
||||
is_internal_user_id,
|
||||
@@ -29,7 +29,7 @@ class TestSystemNotificationDispatch(unittest.TestCase):
|
||||
|
||||
def test_post_message_normalizes_internal_userid_before_queueing(self):
|
||||
chain = MessageChain()
|
||||
message = Notification(
|
||||
message = Message(
|
||||
userid=SYSTEM_INTERNAL_USER_ID,
|
||||
username="admin",
|
||||
title="后台报告",
|
||||
@@ -54,7 +54,7 @@ class TestSystemNotificationDispatch(unittest.TestCase):
|
||||
|
||||
def test_send_direct_message_normalizes_internal_userid(self):
|
||||
chain = MessageChain()
|
||||
message = Notification(
|
||||
message = Message(
|
||||
userid=SYSTEM_INTERNAL_USER_ID,
|
||||
username="admin",
|
||||
title="后台报告",
|
||||
|
||||
+16
-16
@@ -13,8 +13,8 @@ from app.domain.context import MediaInfo, Context, TorrentInfo
|
||||
from app.domain.metainfo import MetaInfo
|
||||
from app.modules.telegram import TelegramModule
|
||||
from app.modules.telegram.telegram import Telegram
|
||||
from app.schemas import Notification
|
||||
from app.schemas.types import MessageChannel
|
||||
from app.schemas import Message
|
||||
from app.schemas.types import NotificationChannel
|
||||
from app.schemas.types import MediaType
|
||||
|
||||
|
||||
@@ -346,8 +346,8 @@ def test_telegram_module_passes_parse_mode_to_client():
|
||||
module, "get_instance", return_value=client
|
||||
):
|
||||
module.post_message(
|
||||
Notification(
|
||||
channel=MessageChannel.Telegram,
|
||||
Message(
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
title="HTML",
|
||||
text="<b>正文</b>",
|
||||
@@ -374,8 +374,8 @@ def test_telegram_module_plain_post_message_keeps_chat_without_editing_source_me
|
||||
module, "get_instance", return_value=client
|
||||
):
|
||||
module.post_message(
|
||||
Notification(
|
||||
channel=MessageChannel.Telegram,
|
||||
Message(
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
title="Agent 回复",
|
||||
text="处理完成",
|
||||
@@ -406,8 +406,8 @@ def test_telegram_module_passes_force_reply_to_client():
|
||||
module, "get_instance", return_value=client
|
||||
):
|
||||
module.post_message(
|
||||
Notification(
|
||||
channel=MessageChannel.Telegram,
|
||||
Message(
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
title="请输入目录",
|
||||
text="回复目录路径",
|
||||
@@ -441,8 +441,8 @@ def test_telegram_module_force_reply_sends_new_prompt_message():
|
||||
module, "get_instance", return_value=client
|
||||
):
|
||||
module.post_message(
|
||||
Notification(
|
||||
channel=MessageChannel.Telegram,
|
||||
Message(
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
title="请输入目录",
|
||||
text="回复目录路径",
|
||||
@@ -480,8 +480,8 @@ def test_telegram_module_direct_force_reply_sends_new_prompt_message():
|
||||
module, "get_instance", return_value=client
|
||||
):
|
||||
response = module.send_direct_message(
|
||||
Notification(
|
||||
channel=MessageChannel.Telegram,
|
||||
Message(
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
title="请输入目录",
|
||||
text="回复目录路径",
|
||||
@@ -521,8 +521,8 @@ def test_telegram_module_direct_buttons_keep_new_message_behavior():
|
||||
module, "get_instance", return_value=client
|
||||
):
|
||||
response = module.send_direct_message(
|
||||
Notification(
|
||||
channel=MessageChannel.Telegram,
|
||||
Message(
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
title="请选择",
|
||||
text="请选择一个操作",
|
||||
@@ -560,8 +560,8 @@ def test_telegram_module_plain_direct_message_keeps_userid_target():
|
||||
module, "get_instance", return_value=client
|
||||
):
|
||||
response = module.send_direct_message(
|
||||
Notification(
|
||||
channel=MessageChannel.Telegram,
|
||||
Message(
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
title="普通通知",
|
||||
|
||||
@@ -11,7 +11,7 @@ from app.chain.message import MessageChain
|
||||
from app.command import Command, _finish_command_processing_status
|
||||
from app.modules.telegram import TelegramModule
|
||||
from app.modules.telegram.telegram import Telegram
|
||||
from app.schemas.types import MessageChannel
|
||||
from app.schemas.types import NotificationChannel
|
||||
|
||||
|
||||
def _wait_until(predicate, timeout: float = 1.0) -> bool:
|
||||
@@ -156,7 +156,7 @@ class TestTelegramTypingLifecycle(unittest.TestCase):
|
||||
Telegram 通过模块处理状态接口启动 typing 保活。
|
||||
"""
|
||||
module = TelegramModule()
|
||||
module._channel = MessageChannel.Telegram
|
||||
module._channel = NotificationChannel.Telegram
|
||||
client = Mock()
|
||||
client.start_typing.return_value = True
|
||||
|
||||
@@ -164,7 +164,7 @@ class TestTelegramTypingLifecycle(unittest.TestCase):
|
||||
module, "get_config", return_value=SimpleNamespace(name="telegram-test")
|
||||
), patch.object(module, "get_instance", return_value=client):
|
||||
status = module.mark_message_processing_started(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
chat_id="-100",
|
||||
@@ -178,7 +178,7 @@ class TestTelegramTypingLifecycle(unittest.TestCase):
|
||||
chain = MessageChain.__new__(MessageChain)
|
||||
chain.eventmanager = Mock()
|
||||
status = MessageChain._ProcessingStatus(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
chat_id="-100",
|
||||
@@ -191,7 +191,7 @@ class TestTelegramTypingLifecycle(unittest.TestCase):
|
||||
chain, "_mark_message_processing_finished"
|
||||
) as finish_status:
|
||||
chain.handle_message(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -217,10 +217,10 @@ class TestTelegramTypingLifecycle(unittest.TestCase):
|
||||
event_data={
|
||||
"cmd": "/sites",
|
||||
"user": "10001",
|
||||
"channel": MessageChannel.Telegram,
|
||||
"channel": NotificationChannel.Telegram,
|
||||
"source": "telegram-test",
|
||||
"processing_status": {
|
||||
"channel": MessageChannel.Telegram.value,
|
||||
"channel": NotificationChannel.Telegram.value,
|
||||
"source": "telegram-test",
|
||||
"userid": "10001",
|
||||
"chat_id": "-100",
|
||||
@@ -240,7 +240,7 @@ class TestTelegramTypingLifecycle(unittest.TestCase):
|
||||
|
||||
def test_finish_command_processing_status_uses_module_interface(self):
|
||||
status = {
|
||||
"channel": MessageChannel.Telegram.value,
|
||||
"channel": NotificationChannel.Telegram.value,
|
||||
"source": "telegram-test",
|
||||
"userid": "10001",
|
||||
"chat_id": "-100",
|
||||
@@ -272,7 +272,7 @@ class TestTelegramTypingLifecycle(unittest.TestCase):
|
||||
chain, "_mark_message_processing_finished"
|
||||
) as finish_status:
|
||||
chain.handle_message(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -286,7 +286,7 @@ class TestTelegramTypingLifecycle(unittest.TestCase):
|
||||
self.assertNotIn("processing_status", process_message.call_args.kwargs)
|
||||
self.assertEqual(
|
||||
process_message.call_args.kwargs["channel"],
|
||||
MessageChannel.Telegram.value,
|
||||
NotificationChannel.Telegram.value,
|
||||
)
|
||||
self.assertEqual(process_message.call_args.kwargs["source"], "telegram-test")
|
||||
self.assertEqual(process_message.call_args.kwargs["original_chat_id"], "-100")
|
||||
@@ -298,12 +298,12 @@ class TestTelegramTypingLifecycle(unittest.TestCase):
|
||||
session_id="session-1",
|
||||
user_id="10001",
|
||||
message="第一条",
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-test",
|
||||
original_chat_id="-100",
|
||||
)
|
||||
status = {
|
||||
"channel": MessageChannel.Telegram.value,
|
||||
"channel": NotificationChannel.Telegram.value,
|
||||
"source": "telegram-test",
|
||||
"userid": "10001",
|
||||
"chat_id": "-100",
|
||||
@@ -328,13 +328,13 @@ class TestTelegramTypingLifecycle(unittest.TestCase):
|
||||
session_id="session-1",
|
||||
user_id="10001",
|
||||
message="第一条",
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-test",
|
||||
original_message_id="10",
|
||||
original_chat_id="-100",
|
||||
)
|
||||
status = {
|
||||
"channel": MessageChannel.Telegram.value,
|
||||
"channel": NotificationChannel.Telegram.value,
|
||||
"source": "telegram-test",
|
||||
"userid": "10001",
|
||||
"message_id": "10",
|
||||
@@ -352,7 +352,7 @@ class TestTelegramTypingLifecycle(unittest.TestCase):
|
||||
result = await _async_start_processing_status(task)
|
||||
|
||||
self.assertEqual(calls, [{
|
||||
"channel": MessageChannel.Telegram,
|
||||
"channel": NotificationChannel.Telegram,
|
||||
"source": "telegram-test",
|
||||
"userid": "10001",
|
||||
"message_id": "10",
|
||||
@@ -366,7 +366,7 @@ class TestTelegramTypingLifecycle(unittest.TestCase):
|
||||
def test_callback_stops_typing_when_message_handler_returns(self):
|
||||
chain = MessageChain.__new__(MessageChain)
|
||||
status = MessageChain._ProcessingStatus(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
chat_id="-100",
|
||||
@@ -379,7 +379,7 @@ class TestTelegramTypingLifecycle(unittest.TestCase):
|
||||
chain, "_mark_message_processing_finished"
|
||||
) as finish_status:
|
||||
chain.handle_message(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -388,7 +388,7 @@ class TestTelegramTypingLifecycle(unittest.TestCase):
|
||||
)
|
||||
|
||||
finish_status.assert_called_once_with(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
status=status,
|
||||
@@ -399,7 +399,7 @@ class TestTelegramTypingLifecycle(unittest.TestCase):
|
||||
def test_chain_finishes_processing_through_module_interface(self):
|
||||
chain = MessageChain.__new__(MessageChain)
|
||||
status = MessageChain._ProcessingStatus(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
chat_id="-100",
|
||||
@@ -408,7 +408,7 @@ class TestTelegramTypingLifecycle(unittest.TestCase):
|
||||
|
||||
with patch.object(chain, "finish_message_processing_status") as finish_status:
|
||||
chain._mark_message_processing_finished(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
status=status,
|
||||
@@ -417,7 +417,7 @@ class TestTelegramTypingLifecycle(unittest.TestCase):
|
||||
|
||||
finish_status.assert_called_once_with(
|
||||
status=status.to_dict(),
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
message_id=None,
|
||||
@@ -428,7 +428,7 @@ class TestTelegramTypingLifecycle(unittest.TestCase):
|
||||
async def _run():
|
||||
manager = AgentManager()
|
||||
status = {
|
||||
"channel": MessageChannel.Telegram.value,
|
||||
"channel": NotificationChannel.Telegram.value,
|
||||
"source": "telegram-test",
|
||||
"userid": "10001",
|
||||
"chat_id": "-100",
|
||||
@@ -457,14 +457,14 @@ class TestTelegramTypingLifecycle(unittest.TestCase):
|
||||
manager = AgentManager()
|
||||
manager._session_queues["session-1"] = asyncio.Queue()
|
||||
first_status = {
|
||||
"channel": MessageChannel.Telegram.value,
|
||||
"channel": NotificationChannel.Telegram.value,
|
||||
"source": "telegram-test",
|
||||
"userid": "10001",
|
||||
"chat_id": "-100",
|
||||
"metadata": {"kind": "typing", "seq": 1},
|
||||
}
|
||||
second_status = {
|
||||
"channel": MessageChannel.Telegram.value,
|
||||
"channel": NotificationChannel.Telegram.value,
|
||||
"source": "telegram-test",
|
||||
"userid": "10001",
|
||||
"chat_id": "-100",
|
||||
@@ -474,7 +474,7 @@ class TestTelegramTypingLifecycle(unittest.TestCase):
|
||||
session_id="session-1",
|
||||
user_id="10001",
|
||||
message="第一条",
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-test",
|
||||
original_chat_id="-100",
|
||||
))
|
||||
@@ -482,7 +482,7 @@ class TestTelegramTypingLifecycle(unittest.TestCase):
|
||||
session_id="session-1",
|
||||
user_id="10001",
|
||||
message="第二条",
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-test",
|
||||
original_chat_id="-100",
|
||||
))
|
||||
|
||||
@@ -15,7 +15,7 @@ from app.chain.message import MessageChain
|
||||
from app.chain.transfer import TransferChain
|
||||
from app.application.messaging.interaction import InteractionContext
|
||||
from app.runtime.config import settings
|
||||
from app.schemas.types import MessageChannel
|
||||
from app.schemas.types import NotificationChannel
|
||||
|
||||
|
||||
class TestTransferFailedRetryButtons(unittest.TestCase):
|
||||
@@ -42,7 +42,7 @@ class TestTransferFailedRetryButtons(unittest.TestCase):
|
||||
with patch.object(chain, "post_message") as post_message:
|
||||
chain.remote_transfer(
|
||||
"12",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
userid="10001",
|
||||
source="telegram-test",
|
||||
)
|
||||
@@ -59,7 +59,7 @@ class TestTransferFailedRetryButtons(unittest.TestCase):
|
||||
chain._handle_callback(
|
||||
callback_data="transfer_retry_12",
|
||||
context=InteractionContext(
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
user_id="10001",
|
||||
username="tester",
|
||||
@@ -68,7 +68,7 @@ class TestTransferFailedRetryButtons(unittest.TestCase):
|
||||
|
||||
transfer_cls.return_value.handle_failed_transfer_callback.assert_called_once_with(
|
||||
callback_data="transfer_retry_12",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -81,7 +81,7 @@ class TestTransferFailedRetryButtons(unittest.TestCase):
|
||||
with patch.object(chain, "post_message") as post_message:
|
||||
handled = chain.handle_failed_transfer_callback(
|
||||
callback_data="transfer_retry_12",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -141,7 +141,7 @@ class TestTransferFailedRetryButtons(unittest.TestCase):
|
||||
with patch.object(chain, "post_message") as post_message:
|
||||
chain.handle_failed_transfer_callback(
|
||||
callback_data="transfer_ai_retry_34",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
@@ -216,7 +216,7 @@ class TestTransferFailedRetryButtons(unittest.TestCase):
|
||||
):
|
||||
chain.handle_failed_transfer_callback(
|
||||
callback_data="transfer_ai_retry_35",
|
||||
channel=MessageChannel.Telegram,
|
||||
channel=NotificationChannel.Telegram,
|
||||
source="telegram-test",
|
||||
userid="10001",
|
||||
username="tester",
|
||||
|
||||
@@ -5,7 +5,7 @@ from app.domain.context import MediaInfo
|
||||
from app.domain.meta.metabase import MetaBase
|
||||
from app.schemas import TransferInfo
|
||||
from app.schemas.tmdb import TmdbEpisode
|
||||
from app.schemas.types import ContentType, MediaType, NotificationType
|
||||
from app.schemas.types import ContentType, MediaType, MessageType
|
||||
|
||||
|
||||
def test_send_transfer_message_passes_episode_info_to_template_context() -> None:
|
||||
@@ -41,7 +41,7 @@ def test_send_transfer_message_passes_episode_info_to_template_context() -> None
|
||||
)
|
||||
|
||||
message = post_message.call_args.args[0]
|
||||
assert message.mtype == NotificationType.Organize
|
||||
assert message.mtype == MessageType.Organize
|
||||
assert message.ctype == ContentType.OrganizeSuccess
|
||||
assert post_message.call_args.kwargs["episodes_info"] is episodes_info
|
||||
assert post_message.call_args.kwargs["season_episode"] == "S01 E01"
|
||||
|
||||
@@ -12,17 +12,17 @@ from app.api.endpoints.agent import (
|
||||
_WebAgentMoviePilotAgent,
|
||||
_WebAgentEventPublisher,
|
||||
_WEB_AGENT_FILE_REGISTRY,
|
||||
_WEB_AGENT_NOTICE_QUEUES,
|
||||
_WEB_AGENT_MESSAGE_QUEUES,
|
||||
_apply_web_agent_display_event,
|
||||
_build_web_agent_input_attachments,
|
||||
_build_web_agent_notification_events,
|
||||
_build_web_agent_message_events,
|
||||
_build_web_agent_command_items,
|
||||
_build_web_agent_session_id,
|
||||
_build_web_agent_traditional_callback_payload,
|
||||
_build_web_agent_display_message_from_events,
|
||||
_collect_web_agent_traditional_events,
|
||||
_dispatch_web_agent_notice_event,
|
||||
_extract_web_agent_notification_from_event_data,
|
||||
_dispatch_web_agent_message_event,
|
||||
_extract_web_agent_message_from_event_data,
|
||||
_has_web_agent_traditional_interaction,
|
||||
_prepare_web_agent_audio_attachment_path,
|
||||
_transcribe_web_agent_audio_refs,
|
||||
@@ -37,8 +37,8 @@ from app.application.messaging.agent import build_web_agent_message_update_event
|
||||
from app.application.messaging.agent import AgentInteractionOption, agent_interaction_manager
|
||||
from app.application.messaging.skill import skill_interaction_manager
|
||||
from app.chain.message import MessageChain
|
||||
from app.schemas.message import ChannelCapability, ChannelCapabilityManager
|
||||
from app.schemas.types import EventType, MessageChannel, NotificationType
|
||||
from app.schemas.notification import ChannelCapability, ChannelCapabilityManager
|
||||
from app.schemas.types import EventType, NotificationChannel, MessageType
|
||||
|
||||
|
||||
def test_split_web_agent_output_extracts_verbose_tool_message():
|
||||
@@ -140,7 +140,7 @@ def test_build_web_agent_session_id_reuses_accessible_history():
|
||||
session_id="telegram-session",
|
||||
user_id="telegram-user",
|
||||
username="tester",
|
||||
channel=MessageChannel.Telegram.value,
|
||||
channel=NotificationChannel.Telegram.value,
|
||||
source="telegram-main",
|
||||
messages=[],
|
||||
title="Telegram 会话",
|
||||
@@ -345,7 +345,7 @@ def test_has_web_agent_traditional_interaction_detects_pending_skills():
|
||||
try:
|
||||
skill_interaction_manager.create_or_replace(
|
||||
user_id="1",
|
||||
channel=MessageChannel.WebAgent,
|
||||
channel=NotificationChannel.WebAgent,
|
||||
source="web-agent",
|
||||
username="admin",
|
||||
)
|
||||
@@ -361,7 +361,7 @@ def test_web_agent_admin_context_uses_current_user_id():
|
||||
agent = _WebAgentMoviePilotAgent(
|
||||
session_id="web-agent:session",
|
||||
user_id="7",
|
||||
channel=MessageChannel.WebAgent.value,
|
||||
channel=NotificationChannel.WebAgent.value,
|
||||
source="web-agent",
|
||||
username="normal-user",
|
||||
replay_mode=ReplyMode.CAPTURE_ONLY,
|
||||
@@ -397,7 +397,7 @@ def test_web_agent_output_callback_receives_only_new_text():
|
||||
agent = _WebAgentMoviePilotAgent(
|
||||
session_id="web-agent:incremental-output",
|
||||
user_id="7",
|
||||
channel=MessageChannel.WebAgent.value,
|
||||
channel=NotificationChannel.WebAgent.value,
|
||||
source="web-agent",
|
||||
username="admin",
|
||||
replay_mode=ReplyMode.CAPTURE_ONLY,
|
||||
@@ -417,7 +417,7 @@ def test_web_agent_tool_summary_is_emitted_before_following_text():
|
||||
agent = _WebAgentMoviePilotAgent(
|
||||
session_id="web-agent:tool-order",
|
||||
user_id="7",
|
||||
channel=MessageChannel.WebAgent.value,
|
||||
channel=NotificationChannel.WebAgent.value,
|
||||
source="web-agent",
|
||||
username="admin",
|
||||
replay_mode=ReplyMode.CAPTURE_ONLY,
|
||||
@@ -433,31 +433,31 @@ def test_web_agent_tool_summary_is_emitted_before_following_text():
|
||||
def test_web_agent_channel_supports_streaming_and_attachments():
|
||||
"""WebAgent 渠道应声明流式、多媒体和文件发送能力。"""
|
||||
assert ChannelCapabilityManager.supports_capability(
|
||||
MessageChannel.WebAgent, ChannelCapability.INLINE_BUTTONS
|
||||
NotificationChannel.WebAgent, ChannelCapability.INLINE_BUTTONS
|
||||
)
|
||||
assert ChannelCapabilityManager.supports_capability(
|
||||
MessageChannel.WebAgent, ChannelCapability.CALLBACK_QUERIES
|
||||
NotificationChannel.WebAgent, ChannelCapability.CALLBACK_QUERIES
|
||||
)
|
||||
assert ChannelCapabilityManager.supports_capability(
|
||||
MessageChannel.WebAgent, ChannelCapability.MESSAGE_EDITING
|
||||
NotificationChannel.WebAgent, ChannelCapability.MESSAGE_EDITING
|
||||
)
|
||||
assert ChannelCapabilityManager.supports_capability(
|
||||
MessageChannel.WebAgent, ChannelCapability.IMAGES
|
||||
NotificationChannel.WebAgent, ChannelCapability.IMAGES
|
||||
)
|
||||
assert ChannelCapabilityManager.supports_capability(
|
||||
MessageChannel.WebAgent, ChannelCapability.AUDIO_OUTPUT
|
||||
NotificationChannel.WebAgent, ChannelCapability.AUDIO_OUTPUT
|
||||
)
|
||||
assert ChannelCapabilityManager.supports_capability(
|
||||
MessageChannel.WebAgent, ChannelCapability.FILE_SENDING
|
||||
NotificationChannel.WebAgent, ChannelCapability.FILE_SENDING
|
||||
)
|
||||
|
||||
|
||||
def test_build_web_agent_notification_events_extracts_image():
|
||||
def test_build_web_agent_message_events_extracts_image():
|
||||
"""Agent 工具发送图片消息时应转换为图片附件事件。"""
|
||||
events = _build_web_agent_notification_events(
|
||||
schemas.Notification(
|
||||
channel=MessageChannel.WebAgent,
|
||||
mtype=NotificationType.Agent,
|
||||
events = _build_web_agent_message_events(
|
||||
schemas.Message(
|
||||
channel=NotificationChannel.WebAgent,
|
||||
mtype=MessageType.Agent,
|
||||
title="海报",
|
||||
text="已找到图片",
|
||||
image="https://example.com/poster.jpg",
|
||||
@@ -479,44 +479,44 @@ def test_build_web_agent_notification_events_extracts_image():
|
||||
]
|
||||
|
||||
|
||||
def test_extract_web_agent_notification_supports_wrapped_message_event():
|
||||
"""NoticeMessage 包装 Notification 时应仍能解析为 WebAgent 通知。"""
|
||||
notification = schemas.Notification(
|
||||
channel=MessageChannel.WebAgent,
|
||||
def test_extract_web_agent_message_supports_wrapped_message_event():
|
||||
"""NoticeMessage 包装 Message 时应仍能解析为 WebAgent 通知。"""
|
||||
message = schemas.Message(
|
||||
channel=NotificationChannel.WebAgent,
|
||||
source="web-agent",
|
||||
title="会话状态",
|
||||
userid="1",
|
||||
)
|
||||
|
||||
extracted = _extract_web_agent_notification_from_event_data(
|
||||
{"message": notification, "current_time": "2026-06-26 09:18:38"}
|
||||
extracted = _extract_web_agent_message_from_event_data(
|
||||
{"message": message, "current_time": "2026-06-26 09:18:38"}
|
||||
)
|
||||
|
||||
assert extracted == notification
|
||||
assert extracted == message
|
||||
|
||||
|
||||
def test_dispatch_web_agent_notice_event_accepts_wrapped_message_event():
|
||||
def test_dispatch_web_agent_message_event_accepts_wrapped_message_event():
|
||||
"""WebAgent 等待队列应接收 message 包装格式的 NoticeMessage 事件。"""
|
||||
notice_queue = Queue()
|
||||
_WEB_AGENT_NOTICE_QUEUES["1"] = [notice_queue]
|
||||
notification = schemas.Notification(
|
||||
channel=MessageChannel.WebAgent,
|
||||
_WEB_AGENT_MESSAGE_QUEUES["1"] = [notice_queue]
|
||||
message = schemas.Message(
|
||||
channel=NotificationChannel.WebAgent,
|
||||
source="web-agent",
|
||||
title="会话状态",
|
||||
userid="1",
|
||||
)
|
||||
|
||||
try:
|
||||
_dispatch_web_agent_notice_event(
|
||||
_dispatch_web_agent_message_event(
|
||||
Event(
|
||||
EventType.NoticeMessage,
|
||||
{"message": notification, "current_time": "2026-06-26 09:18:38"},
|
||||
{"message": message, "current_time": "2026-06-26 09:18:38"},
|
||||
)
|
||||
)
|
||||
finally:
|
||||
_WEB_AGENT_NOTICE_QUEUES.pop("1", None)
|
||||
_WEB_AGENT_MESSAGE_QUEUES.pop("1", None)
|
||||
|
||||
assert notice_queue.get_nowait() == notification
|
||||
assert notice_queue.get_nowait() == message
|
||||
|
||||
|
||||
def test_collect_web_agent_traditional_events_does_not_emit_submit_hint():
|
||||
@@ -542,15 +542,15 @@ def test_collect_web_agent_traditional_events_does_not_emit_submit_hint():
|
||||
assert events == []
|
||||
|
||||
|
||||
def test_build_web_agent_notification_events_registers_local_file(tmp_path):
|
||||
def test_build_web_agent_message_events_registers_local_file(tmp_path):
|
||||
"""Agent 工具发送本地文件时应生成可下载附件事件。"""
|
||||
file_path = tmp_path / "report.txt"
|
||||
file_path.write_text("hello", encoding="utf-8")
|
||||
|
||||
events = _build_web_agent_notification_events(
|
||||
schemas.Notification(
|
||||
channel=MessageChannel.WebAgent,
|
||||
mtype=NotificationType.Agent,
|
||||
events = _build_web_agent_message_events(
|
||||
schemas.Message(
|
||||
channel=NotificationChannel.WebAgent,
|
||||
mtype=MessageType.Agent,
|
||||
file_path=str(file_path),
|
||||
file_name="report.txt",
|
||||
)
|
||||
@@ -566,15 +566,15 @@ def test_build_web_agent_notification_events_registers_local_file(tmp_path):
|
||||
assert attachment["url"].startswith("message/agent/file/")
|
||||
|
||||
|
||||
def test_build_web_agent_notification_events_registers_voice_attachment(tmp_path):
|
||||
def test_build_web_agent_message_events_registers_voice_attachment(tmp_path):
|
||||
"""Agent 工具发送语音时应转换为可播放的音频附件事件。"""
|
||||
voice_path = tmp_path / "reply.wav"
|
||||
voice_path.write_bytes(b"wav-bytes")
|
||||
|
||||
events = _build_web_agent_notification_events(
|
||||
schemas.Notification(
|
||||
channel=MessageChannel.WebAgent,
|
||||
mtype=NotificationType.Agent,
|
||||
events = _build_web_agent_message_events(
|
||||
schemas.Message(
|
||||
channel=NotificationChannel.WebAgent,
|
||||
mtype=MessageType.Agent,
|
||||
text="你好",
|
||||
voice_path=str(voice_path),
|
||||
)
|
||||
@@ -688,9 +688,9 @@ def test_web_agent_stream_binds_session_to_agent_manager():
|
||||
"""更新当前 SSE 受保护输出回调。"""
|
||||
self.protected_output_callback = protected_output_callback
|
||||
|
||||
def set_notification_callback(self, notification_callback):
|
||||
def set_message_callback(self, message_callback):
|
||||
"""更新当前 SSE 通知回调。"""
|
||||
self.notification_callback = notification_callback
|
||||
self.message_callback = message_callback
|
||||
|
||||
async def process(self, message, **kwargs):
|
||||
"""模拟一次 WebAgent 推理输出。"""
|
||||
@@ -755,7 +755,7 @@ def test_web_agent_stream_emits_secret_result_only_as_protected_event():
|
||||
def __init__(self, **kwargs):
|
||||
self.__dict__.update(kwargs)
|
||||
self._pending_secret_confirmation = SimpleNamespace(
|
||||
channel=MessageChannel.WebAgent.value,
|
||||
channel=NotificationChannel.WebAgent.value,
|
||||
source="web-agent",
|
||||
original_chat_id="",
|
||||
)
|
||||
@@ -767,8 +767,8 @@ def test_web_agent_stream_emits_secret_result_only_as_protected_event():
|
||||
def set_output_callback(self, output_callback):
|
||||
self.output_callback = output_callback
|
||||
|
||||
def set_notification_callback(self, notification_callback):
|
||||
self.notification_callback = notification_callback
|
||||
def set_message_callback(self, message_callback):
|
||||
self.message_callback = message_callback
|
||||
|
||||
def set_protected_output_callback(self, protected_output_callback):
|
||||
self.protected_output_callback = protected_output_callback
|
||||
@@ -789,7 +789,7 @@ def test_web_agent_stream_emits_secret_result_only_as_protected_event():
|
||||
session_id=session_id,
|
||||
user_id="1",
|
||||
username="admin",
|
||||
channel=MessageChannel.WebAgent.value,
|
||||
channel=NotificationChannel.WebAgent.value,
|
||||
source="web-agent",
|
||||
messages=existing_messages,
|
||||
client_session_id=payload.session_id,
|
||||
@@ -797,7 +797,7 @@ def test_web_agent_stream_emits_secret_result_only_as_protected_event():
|
||||
agent_manager.active_agents[session_id] = FakeProtectedAgent(
|
||||
session_id=session_id,
|
||||
user_id="1",
|
||||
channel=MessageChannel.WebAgent.value,
|
||||
channel=NotificationChannel.WebAgent.value,
|
||||
source="web-agent",
|
||||
username="admin",
|
||||
)
|
||||
@@ -858,7 +858,7 @@ def test_web_agent_cancel_keeps_existing_display_history():
|
||||
session_id=session_id,
|
||||
user_id="1",
|
||||
username="admin",
|
||||
channel=MessageChannel.WebAgent.value,
|
||||
channel=NotificationChannel.WebAgent.value,
|
||||
source="web-agent",
|
||||
messages=existing_messages,
|
||||
client_session_id=payload.session_id,
|
||||
@@ -977,7 +977,7 @@ def test_web_agent_stream_drops_secret_result_after_disconnect():
|
||||
session_id=session_id,
|
||||
user_id="1",
|
||||
username="admin",
|
||||
channel=MessageChannel.WebAgent.value,
|
||||
channel=NotificationChannel.WebAgent.value,
|
||||
source="web-agent",
|
||||
messages=existing_messages,
|
||||
client_session_id=payload.session_id,
|
||||
@@ -1298,12 +1298,12 @@ async def _collect_streaming_response(response):
|
||||
return chunks
|
||||
|
||||
|
||||
def test_build_web_agent_notification_events_extracts_choice_card():
|
||||
def test_build_web_agent_message_events_extracts_choice_card():
|
||||
"""Agent 按钮通知应转换为 Web 选择卡片事件而非普通文本。"""
|
||||
events = _build_web_agent_notification_events(
|
||||
schemas.Notification(
|
||||
channel=MessageChannel.WebAgent,
|
||||
mtype=NotificationType.Agent,
|
||||
events = _build_web_agent_message_events(
|
||||
schemas.Message(
|
||||
channel=NotificationChannel.WebAgent,
|
||||
mtype=MessageType.Agent,
|
||||
title="需要你的选择",
|
||||
text="请选择要执行的操作",
|
||||
buttons=[
|
||||
@@ -1368,7 +1368,7 @@ def test_resolve_web_agent_choice_payload_returns_next_message():
|
||||
request = agent_interaction_manager.create_request(
|
||||
session_id="web-agent:session",
|
||||
user_id="1",
|
||||
channel=MessageChannel.WebAgent.value,
|
||||
channel=NotificationChannel.WebAgent.value,
|
||||
source="web-agent",
|
||||
username="admin",
|
||||
title="需要你的选择",
|
||||
|
||||
Reference in New Issue
Block a user