From 3500876b5c85421222a0b99692cf8b03ff83211b Mon Sep 17 00:00:00 2001 From: Jan' s Date: Sun, 10 May 2026 11:41:43 +0800 Subject: [PATCH] fix: clarify 115 login qrcode flow --- AgentResourceOfficer/__init__.py | 7 ++- plugins.v2/agentresourceofficer/__init__.py | 7 ++- plugins/agentresourceofficer/__init__.py | 7 ++- .../agent-resource-officer/EXTERNAL_AGENTS.md | 2 + skills/agent-resource-officer/SKILL.md | 1 + .../scripts/aro_request.py | 48 +++++++++++++++++++ 6 files changed, 66 insertions(+), 6 deletions(-) diff --git a/AgentResourceOfficer/__init__.py b/AgentResourceOfficer/__init__.py index 53ffc7d..199a6cf 100644 --- a/AgentResourceOfficer/__init__.py +++ b/AgentResourceOfficer/__init__.py @@ -2157,7 +2157,7 @@ class AgentResourceOfficer(_PluginBase): elif cookie_state.get("configured"): p115_ready = "已配置但不是扫码会话" else: - p115_ready = "复用 115 助手客户端" + p115_ready = "未配置扫码会话,尝试复用 P115StrmHelper" hdhive_summary = self._build_hdhive_page_summary() feishu_health = self._ensure_feishu_channel().health() feishu_state = "已启用" if feishu_health.get("enabled") else "未启用" @@ -2252,6 +2252,9 @@ class AgentResourceOfficer(_PluginBase): f"默认目录:{self._p115_default_path}", f"登录方式:{p115_ready}", f"扫码客户端:{self._p115_client_type_title(self._p115_client_type)}", + "智能体命令:115登录 → 检查115登录", + "复用顺序:插件扫码会话 > P115StrmHelper", + "不复用:MoviePilot 主程序 115 登录态", ], "success" if p115_health_ok else "error", ) @@ -2968,7 +2971,7 @@ class AgentResourceOfficer(_PluginBase): "props": { "type": "info", "variant": "tonal", - "text": "115 建议走扫码会话,不建议填网页版 Cookie。插件支持 /p115/qrcode 和 /p115/qrcode/check 两步扫码登录;手填 Cookie 仅作为高级兜底。", + "text": "115 登录用法:下面的扫码会话 Cookie 可以不填;不填写时,插件会先尝试复用 P115StrmHelper 的已登录客户端。如果没有安装 P115StrmHelper,或复用不可用,就在外部智能体或飞书里发送「115登录」,智能体会返回二维码图片;用 115 App 扫码确认后,再发送「检查115登录」。插件暂不复用 MoviePilot 主程序 115 登录态。高级用户才需要手填 UID/CID/SEID 客户端 Cookie,普通网页版 Cookie 不建议填写。", }, } ], diff --git a/plugins.v2/agentresourceofficer/__init__.py b/plugins.v2/agentresourceofficer/__init__.py index 53ffc7d..199a6cf 100644 --- a/plugins.v2/agentresourceofficer/__init__.py +++ b/plugins.v2/agentresourceofficer/__init__.py @@ -2157,7 +2157,7 @@ class AgentResourceOfficer(_PluginBase): elif cookie_state.get("configured"): p115_ready = "已配置但不是扫码会话" else: - p115_ready = "复用 115 助手客户端" + p115_ready = "未配置扫码会话,尝试复用 P115StrmHelper" hdhive_summary = self._build_hdhive_page_summary() feishu_health = self._ensure_feishu_channel().health() feishu_state = "已启用" if feishu_health.get("enabled") else "未启用" @@ -2252,6 +2252,9 @@ class AgentResourceOfficer(_PluginBase): f"默认目录:{self._p115_default_path}", f"登录方式:{p115_ready}", f"扫码客户端:{self._p115_client_type_title(self._p115_client_type)}", + "智能体命令:115登录 → 检查115登录", + "复用顺序:插件扫码会话 > P115StrmHelper", + "不复用:MoviePilot 主程序 115 登录态", ], "success" if p115_health_ok else "error", ) @@ -2968,7 +2971,7 @@ class AgentResourceOfficer(_PluginBase): "props": { "type": "info", "variant": "tonal", - "text": "115 建议走扫码会话,不建议填网页版 Cookie。插件支持 /p115/qrcode 和 /p115/qrcode/check 两步扫码登录;手填 Cookie 仅作为高级兜底。", + "text": "115 登录用法:下面的扫码会话 Cookie 可以不填;不填写时,插件会先尝试复用 P115StrmHelper 的已登录客户端。如果没有安装 P115StrmHelper,或复用不可用,就在外部智能体或飞书里发送「115登录」,智能体会返回二维码图片;用 115 App 扫码确认后,再发送「检查115登录」。插件暂不复用 MoviePilot 主程序 115 登录态。高级用户才需要手填 UID/CID/SEID 客户端 Cookie,普通网页版 Cookie 不建议填写。", }, } ], diff --git a/plugins/agentresourceofficer/__init__.py b/plugins/agentresourceofficer/__init__.py index 53ffc7d..199a6cf 100644 --- a/plugins/agentresourceofficer/__init__.py +++ b/plugins/agentresourceofficer/__init__.py @@ -2157,7 +2157,7 @@ class AgentResourceOfficer(_PluginBase): elif cookie_state.get("configured"): p115_ready = "已配置但不是扫码会话" else: - p115_ready = "复用 115 助手客户端" + p115_ready = "未配置扫码会话,尝试复用 P115StrmHelper" hdhive_summary = self._build_hdhive_page_summary() feishu_health = self._ensure_feishu_channel().health() feishu_state = "已启用" if feishu_health.get("enabled") else "未启用" @@ -2252,6 +2252,9 @@ class AgentResourceOfficer(_PluginBase): f"默认目录:{self._p115_default_path}", f"登录方式:{p115_ready}", f"扫码客户端:{self._p115_client_type_title(self._p115_client_type)}", + "智能体命令:115登录 → 检查115登录", + "复用顺序:插件扫码会话 > P115StrmHelper", + "不复用:MoviePilot 主程序 115 登录态", ], "success" if p115_health_ok else "error", ) @@ -2968,7 +2971,7 @@ class AgentResourceOfficer(_PluginBase): "props": { "type": "info", "variant": "tonal", - "text": "115 建议走扫码会话,不建议填网页版 Cookie。插件支持 /p115/qrcode 和 /p115/qrcode/check 两步扫码登录;手填 Cookie 仅作为高级兜底。", + "text": "115 登录用法:下面的扫码会话 Cookie 可以不填;不填写时,插件会先尝试复用 P115StrmHelper 的已登录客户端。如果没有安装 P115StrmHelper,或复用不可用,就在外部智能体或飞书里发送「115登录」,智能体会返回二维码图片;用 115 App 扫码确认后,再发送「检查115登录」。插件暂不复用 MoviePilot 主程序 115 登录态。高级用户才需要手填 UID/CID/SEID 客户端 Cookie,普通网页版 Cookie 不建议填写。", }, } ], diff --git a/skills/agent-resource-officer/EXTERNAL_AGENTS.md b/skills/agent-resource-officer/EXTERNAL_AGENTS.md index 0d161fd..08c1a69 100644 --- a/skills/agent-resource-officer/EXTERNAL_AGENTS.md +++ b/skills/agent-resource-officer/EXTERNAL_AGENTS.md @@ -128,6 +128,8 @@ MCP 地址:http://你的NAS地址:3000/api/v1/mcp 完整命令列表见 [ALL_COMMANDS.md](../../docs/ALL_COMMANDS.md)。 +`115登录` 是一个容易被长线程摘要掉的命令:如果 helper 返回二维码图片路径或 Markdown 图片,外部智能体必须原样展示给用户,不能只说“二维码已生成”。用户扫码后,再回复 `检查115登录` 完成验证。 + --- ## MCP 要不要接 diff --git a/skills/agent-resource-officer/SKILL.md b/skills/agent-resource-officer/SKILL.md index 0058381..0f60a4c 100644 --- a/skills/agent-resource-officer/SKILL.md +++ b/skills/agent-resource-officer/SKILL.md @@ -80,6 +80,7 @@ Core rules: - `1详情` and similar variants must preserve detail intent. Never collapse them into `1` or `选择 1`. - Before confirming PT download execution, make sure the connected MoviePilot is the real download instance, not a cloud-drive/STRM-only instance. - If the user says `校准影视技能`, run `python3 scripts/aro_request.py calibrate` or `python3 scripts/aro_request.py route "校准影视技能"` first, apply the returned hard rules to the current session, then reply only `影视技能已校准。`. +- If the user says `115登录`, route it through the helper and preserve the helper output exactly. When the helper prints a QR image path or Markdown image such as `![115扫码二维码](...)`, show it to the user; do not summarize it into plain text or drop the image/path. After the user scans, tell them to reply `检查115登录`. - For explicit title searches such as `MP 搜索 罪无可逃`, the first and only initial action is helper `route "<原话>" --session `. Do not pre-call TMDB, MCP search, raw MoviePilot API, or torrent search before that helper route. Environment overrides: diff --git a/skills/agent-resource-officer/scripts/aro_request.py b/skills/agent-resource-officer/scripts/aro_request.py index 349c2c4..ebf6d31 100755 --- a/skills/agent-resource-officer/scripts/aro_request.py +++ b/skills/agent-resource-officer/scripts/aro_request.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 import argparse +import base64 import json import os import subprocess @@ -880,6 +881,50 @@ def print_json(data): print(json.dumps(data, ensure_ascii=False, indent=2)) +def qrcode_payload(data): + if not isinstance(data, dict): + return {} + payload = data.get("data") if isinstance(data.get("data"), dict) else data + qrcode = str((payload or {}).get("qrcode") or "") + if qrcode.startswith("data:image/") and ";base64," in qrcode: + return payload + return {} + + +def write_qrcode_image(payload): + qrcode = str((payload or {}).get("qrcode") or "") + if not qrcode.startswith("data:image/") or ";base64," not in qrcode: + return "" + header, encoded = qrcode.split(",", 1) + suffix = ".png" + if "image/jpeg" in header or "image/jpg" in header: + suffix = ".jpg" + safe_uid = "".join(ch for ch in str(payload.get("uid") or "qrcode") if ch.isalnum() or ch in {"-", "_"})[:48] + output_dir = "/tmp/agent-resource-officer" + os.makedirs(output_dir, exist_ok=True) + path = os.path.join(output_dir, f"p115-login-{safe_uid or 'qrcode'}{suffix}") + with open(path, "wb") as f: + f.write(base64.b64decode(encoded)) + return path + + +def print_qrcode_message(result): + payload = qrcode_payload(result) + if not payload: + return False + image_path = write_qrcode_image(payload) + message = str((result or {}).get("message") or "").strip() + if message: + print(message) + if image_path: + print("") + print(f"二维码图片:{image_path}") + print(f"![115扫码二维码]({image_path})") + print("") + print("扫码确认后回复:检查115登录") + return True + + def summary_command(summary, confirmed=False): summary = summary or {} explicit_behavior = str(summary.get("recommended_agent_behavior") or "").strip() @@ -1222,6 +1267,7 @@ def calibration_payload(): "MP搜索/PT搜索 后面带 给我最新集、最新集、最新一集 时,仍然原样 route;插件会只展示最高集数候选,不要把上一批旧集数混回摘要。", "MP搜索/PT搜索/下载 后面带 第4集、第四集、E04、S01E04 时,仍然原样 route;插件会只展示包含该目标集的候选并安全重编号。", "MP搜索/PT搜索 最新集 如果先返回影片候选,后续编号必须继续使用同一个 helper session;不要新开会话裸跑 route \"1\",否则会丢失最新集过滤上下文。", + "115登录 必须原样展示 helper 输出中的二维码图片路径或 Markdown 图片;不能摘要成“二维码已生成”后吞掉图片。扫码后让用户回复 检查115登录。", "下载/MP搜索/PT搜索 返回 PT 资源列表时,必须原样展示插件 message 里的编号资源,不要压缩成“PT资源已列出”。", "MP/PT 结果列表不能重新编号;插件返回 2、4、21、29 就原样显示并用这些编号继续选择/下载,不能改成 1、2、3、4,也不要追加自己的选择提示尾巴。", "编号详情、15详情、十六详情、选择 15 详情 = 只读详情,不能执行转存或下载。", @@ -2534,6 +2580,8 @@ def main(): return 0 output = result if args.full else compact(result) if args.command in {"route", "pick"} and not args.full and not args.json_output: + if print_qrcode_message(result): + return 0 if (result or {}).get("success", True) else 2 message = str((output or {}).get("message") or "").strip() if isinstance(output, dict) else "" if message: print(message)