From f320eb4f6cf258ab5a1697941102d70b2f857306 Mon Sep 17 00:00:00 2001 From: maogeigei Date: Mon, 31 Aug 2026 17:23:58 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=8A=E5=B8=8C=E5=85=A8=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E7=94=A8=E4=BE=8B=E8=A1=A5=E5=85=85=EF=BC=9A=E5=89=8D=E7=BD=AE?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=A3=80=E6=9F=A5=E5=88=A4=E5=AE=9A+?= =?UTF-8?q?=E7=95=8C=E9=9D=A2=E6=A8=A1=E6=8B=9F=E7=82=B9=E5=87=BB=E8=84=9A?= =?UTF-8?q?=E6=9C=AC(cdp-test-junxi.mjs=20=E5=AE=9E=E6=B5=8B=E9=80=9A?= =?UTF-8?q?=E8=BF=87)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .workbuddy/memory/2026-08-31.md | 8 ++ .../test-cases/2026-08-31_俊希全流程.md | 65 ++++++++-- .../V1.0/mcn-work-shop/cdp-test-junxi.mjs | 115 ++++++++++++++++++ 3 files changed, 179 insertions(+), 9 deletions(-) create mode 100644 project/短视频脚本创作/V1.0/mcn-work-shop/cdp-test-junxi.mjs diff --git a/.workbuddy/memory/2026-08-31.md b/.workbuddy/memory/2026-08-31.md index 7ed5a3d..5b4d779 100644 --- a/.workbuddy/memory/2026-08-31.md +++ b/.workbuddy/memory/2026-08-31.md @@ -422,3 +422,11 @@ - **前置调研结论**:达人账号表仅 2 行(小咪橘座 70010/旧梦留声机 70013),**俊希不在表** → 首次登记 70014;`MCNSkill项目/俊希/` 仅含 `提示词01/`(分镜产出),无账号分析产出 → 全新执行场景 - **用例落盘**:`.workbuddy/test-cases/2026-08-31_俊希全流程.md`(TC-20260831-01),覆盖 TC-01 账号表登记 → TC-02 获取账号信息(含「有就刷新」分支:更新时间近1个月提示用户判断)→ TC-03 获取视频(≥60条/13列Excel)→ TC-04 筛选TOP(select-top6,n≤6按实际)→ TC-05 MCP解析(upload→3-5min→detail,补充规则)→ TC-06 json_tool 保存 → TC-07 人设卡12段 → TC-08 设定卡自动生成(grep一致性)→ TC-09 工作台AI选题(/api/ai/topics→3选题)→ TC-10 AI写脚本(S1-S11,automations触发)→ TC-11 收尾验证(git提交);含数据校验标准表 + 8 项异常分支预案 + 验收汇总表 - 用例引用的权威参数:视频筛选标准(TOP6上限/不足按实际,SKILL.md L43)、视频获取数量(≥60条停止,L44)、browser-harness 红线(L49)、路径配置(references/路径配置.md:开发机→桌面 MCNSkill项目/俊希/ 直挂) + +## 俊希用例补充:数据检查 + 界面模拟点击(17:30) +- 用户补充要求:①测试用例需要**模拟点击界面**进行测试;②执行前要**判断项目文件夹和数据库是否已有数据、是否需要清除** +- **数据检查实测结论**(写入用例「二·补」章节):桌面 `MCNSkill项目/俊希/` 仅提示词01(分镜产出)→ 无需清除;达人账号表无俊希 → 首次登记 70014;**工作台 mcn-plugin.db 俊希有历史数据**——hot_accounts id=3(完整账号信息+content 设定文本,source_dir=`D:\dshworkspace\账号解析\俊希` **旧路径已不存在**)、account_videos 108 条(`抖音账号分析\俊希` 旧路径)、rewrite_log 3 条(2026-08-19 海鲜大餐主题);**关键判定:account_persona 表无俊希**(仅 1627-1630 测试占位)→ confirmAiScript 的 hasPersona=false → 弹窗走「先进行账号设定分析」分支,**首次场景成立,无需清除数据库**;dsh 原库(~/.dsh/mcn-plugin.db)同数据但只读红线不写 +- **界面模拟点击脚本**:`mcn-work-shop/cdp-test-junxi.mjs`(新写,CDP 模式同 cdp-verify-*:连 9223 → Target.createTarget 新开标签页 → Runtime.evaluate 模拟点击 → 自动关标签页;`--confirm` 参数才真实触发创作任务);覆盖 #/accounts 定位俊希 `[data-create='俊希']` → 点击 → 轮询弹窗 → 点 #aiTopicGen → 轮询选题(100s 上限)→ 读 3 选题 +- **实测通过**(安全模式):Step1 定位 PASS / Step2 弹窗结构 PASS(文案=「先进行账号设定分析,再生成完整脚本」首次分支)/ Step3-4 AI 选题 PASS(3 选题:突击妈妈公司送便当/替妈妈筹备周末闺蜜聚会/打工人妈妈病号特权日,贴合亲子Vlog人设) +- ⚠️ **坑**:`.modal` 选择器会被 index.html 隐藏的 `#settingsModal`(hidden 但 .modal 子元素在 DOM)干扰 → 判据须 `[...document.querySelectorAll('.modal-mask')].find(x => !x.hidden && x.querySelector('#aiTopicGen'))` +- **两条 AI写脚本入口差异**(写入用例 TC-09):#/accounts 每行「AI写脚本」(data-create→confirmAiScript)=自定义选题/AI结合人设生成选题(**主链路,对应「AI参考最新视频选题」**);#/rewrites 每行(data-rewrite→confirmRun)=基于视频《Y》改写全新 S9 脚本不复刻(次链路) diff --git a/.workbuddy/test-cases/2026-08-31_俊希全流程.md b/.workbuddy/test-cases/2026-08-31_俊希全流程.md index 4bcf821..cd912fa 100644 --- a/.workbuddy/test-cases/2026-08-31_俊希全流程.md +++ b/.workbuddy/test-cases/2026-08-31_俊希全流程.md @@ -36,11 +36,44 @@ | # | 检查项 | 判定标准 | 本用例实际状态 | |---|--------|---------|---------------| | P1 | 达人账号表是否含「俊希」 | 有 → 走「有就刷新」分支;无 → Step 1.0 登记 | **无**(仅小咪橘座 70010 / 旧梦留声机 70013)→ 首次登记 70014 | -| P2 | 产出目录 `MCNSkill项目/俊希/` 现有内容 | 有账号设定/视频表格 → 刷新覆盖;无 → 全新创建 | **仅 `提示词01/` 子目录**,无账号设定/视频表格 → 全新 | +| P2 | 产出目录 `MCNSkill项目/俊希/` 现有内容 | 有账号设定/视频表格 → 刷新覆盖;无 → 全新创建 | **仅 `提示词01/` 子目录**(分镜技能产出,与本次链路不冲突),无账号分析产出 → 全新 | | P3 | browser-harness 是否可用(功能一/二红线) | 可执行文件存在(Win `~/.local/bin/browser-harness.exe` / mac `~/.local/bin/browser-harness`)或 `where`/`command -v` 有输出 | 执行前检测,未装则按红线提醒/安装 | | P4 | MCP 服务(myai-mcp-production)可用 | 当前会话 MCP 工具列表含 `upload_douyin_video`/`short_video_detail` 或 `~/.workbuddy/mcp.json` 含该服务配置 | 执行前检测,未装按 MCP 规范提醒 | -| P5 | MCN 工作台服务(端口 8899) | `http://localhost:8899` 可访问 | 未启动则先启动(`node server.js`,自动避让端口) | -| P6 | 输出路径权威配置 | 读 `references/路径配置.md` | 开发机 → 桌面 `MCNSkill项目/`,`俊希/` 账号层直挂 | +| P5 | MCN 工作台服务(端口 8899) | `http://localhost:8899` 可访问 | **运行中(200)**;未启动则先启动(`node server.js`,自动避让端口) | +| P6 | 浏览器 9223 调试实例 | `http://localhost:9223/json` 可访问(界面模拟点击测试用) | **运行中**;未启动则按浏览器操作规范启动调试 Chrome | +| P7 | 输出路径权威配置 | 读 `references/路径配置.md` | 开发机 → 桌面 `MCNSkill项目/`,`俊希/` 账号层直挂 | + +## 二·补、前置数据检查与清理判定(本次实测 2026-08-31) + +> 测试前先判断**项目文件夹**与**数据库**是否已有俊希数据,决定是否需要清除,避免测试污染/误判。 + +### 检查结果(本次实测) + +| 数据源 | 检查项 | 实测结果 | 是否需清除 | +|-------|-------|---------|-----------| +| 项目文件夹(桌面 `MCNSkill项目/俊希/`) | 账号设定/视频表格/脚本 | **仅 `提示词01/`**(分镜产出,属短视频分镜提示词技能) | **否**(本链路产出全新创建;提示词01 保留) | +| 达人账号表.xlsx | 俊希行 | **无** | 否(首次登记 70014) | +| 工作台 mcn-plugin.db(`…\mcn-work-shop\`,自有副本) | hot_accounts 俊希 | **有**(id=3,含完整账号信息 + content 账号设定文本、douyin_id=JJX0827、sec_uid、278.7万粉、source_dir=`D:\dshworkspace\账号解析\俊希` 为**旧路径残留**,目录已不存在) | **否**(见下判定) | +| 同上 | account_videos 俊希 | **有 108 条**(source_dir=`D:\dshworkspace\抖音账号分析\俊希` 旧路径) | 否 | +| 同上 | account_persona 俊希 | **无**(5 行均为 1627-1630 测试占位,无 account_id=3) | 否 | +| 同上 | rewrite_log 俊希 | **有 3 条**(2026-08-19 AI 写脚本记录,id=12/15/16,topic 含完整选题) | 否 | +| dsh 原库(`~/.dsh/mcn-plugin.db`) | 俊希数据 | 与工作台副本一致(hot_accounts/videos/rewrite_log + account_video_source 2 条) | **否(.dsh 单向只读红线,绝不写)** | +| 旧目录 `D:\dshworkspace\账号解析\俊希`、`抖音账号分析\俊希` | 文件 | **已不存在**(数据库 source_dir 为历史残留) | 无需处理 | + +### 清理判定逻辑(结论) + +1. **文件层面**:桌面 `MCNSkill项目/俊希/` 无本链路产出 → **无需清除**,全新创建;已有产出时(有就刷新)→ 覆盖更新 `短视频表格.xlsx`、新增/更新设定与脚本,**不删除旧产出**(保留可追溯) +2. **数据库层面(工作台 mcn-plugin.db 自有副本)**:俊希有历史数据但**不影响本测试**——关键判定 `account_persona` 无俊希 → 工作台 `confirmAiScript` 的 hasPersona=false → 弹窗走「先进行账号设定分析」分支,**首次场景成立**(hot_accounts.content 的旧设定文本不会被界面读取,界面只读 account_persona)。因此**无需清除**;若追求「完全干净」需用户明确授权后选择性清除(hot_accounts id=3 及其 videos/rewrite_log 行),且只动工作台自有副本 +3. **dsh 原库**:**红线只读,绝不写**(清理不适用) +4. **工作台副本与 dsh 原库的数据同步**:工作台读自有副本(`mcn-work-shop/mcn-plugin.db`),与 `~/.dsh/mcn-plugin.db` 隔离;测试新增数据(rewrite_log 第 4 条等)只落工作台副本,不影响 dsh 原库 + +### 清除操作(如用户要求执行,需先备份) + +```bash +# 仅工作台自有副本;先 cp 备份 mcn-plugin.db 再操作 +# 清除俊希:DELETE FROM rewrite_log WHERE account_id=3; DELETE FROM account_videos WHERE account_id=3; DELETE FROM hot_accounts WHERE id=3; +# 注意:达人账号表/桌面产出/dsh 原库一律不动 +``` --- @@ -118,20 +151,31 @@ | 验证点 | ① 卡片 TOPn 与设定 md 内容基因板块数一致(**n 不写死 6**);② 双向联动一致性:交付前 **grep 校验**两文件关键术语一致(模块名「叙事结构」「内容标签」「内容基因·TOPn」等),零残留后再交付;③ 卡片内容与设定 md 内容级一致 | | 通过标准 | `俊希账号设定卡.svg` 生成;grep 校验通过(模块名/TOPn 两文件一致) | -### TC-09 工作台 AI 参考最新视频选题 +### TC-09 工作台 AI 参考最新视频选题(**界面模拟点击**) + +> **本环节用可执行脚本模拟真实点击测试**,脚本:`mcn-work-shop/cdp-test-junxi.mjs` +> 用法:`node cdp-test-junxi.mjs`(安全模式:验证到选题生成,不触发任务);`node cdp-test-junxi.mjs --confirm`(继续确认执行,真实触发创作任务) +> 机制:连接浏览器 9223 调试实例 → 新开标签页打开 `#/accounts` → `Runtime.evaluate` 模拟点击 → 轮询弹窗/AI 选题 → 自动关闭标签页(不干扰用户页面)。脚本复用 cdp-verify-*.mjs 的 CDP 模式。 + +**两条「AI写脚本」入口差异(重要)**: + +| 入口 | 位置 | 行为 | 本用例用途 | +|------|------|------|-----------| +| 账号列表「AI写脚本」 | `#/accounts` 每行(`[data-create]`) | `confirmAiScript` → 弹窗:手输选题 **或 点 AI 按钮结合人设生成选题**(`POST /api/ai/topics`,基于账号设定+最新视频提炼 3 个方向) | **主链路**(AI参考最新视频选题) | +| 视频列表「AI写脚本」 | `#/rewrites` 每行(`[data-rewrite]`) | `confirmRun` 改写脚本:「基于视频《Y》改写全新 S9 故事脚本(不复刻原视频)」 | 次链路(基于单条视频改写,可选回归) | | 项 | 内容 | |---|------| -| 操作 | ① 启动工作台(`node server.js`,8899 自动避让);② 打开「AI写脚本」页(#/ai-script),在「俊希」行点「创作」→ `confirmAiScript('俊希', btn)`;③ 因俊希**已有设定** → 弹窗文案为「基于当前账号设定生成脚本」;④ 点击右下角 `AI` 按钮(#aiTopicGen)→ `POST /api/ai/topics`(body `{accountName:'俊希'}`)→ 等待约 50s 返回 | -| 预期 | 返回 **3 个选题方向**(基于俊希账号设定 + 最新视频内容提炼);自动填入选题输入框并 toast「✅ 已生成 3 个选题方向」 | -| 验证点 | ① 弹窗文案正确识别「已有设定」分支;② 选题方向与俊希亲子 Vlog 人设相关(非泛泛);③ 选题可编辑(手输或修改) | -| 通过标准 | 3 个选题方向生成且贴合账号设定,填入输入框 | +| 操作(脚本自动执行) | ① 脚本新开标签页打开 `#/accounts` → 定位俊希行「AI写脚本」按钮(`[data-create='俊希']`)→ **模拟点击**;② 轮询等待弹窗出现(`.modal-mask:not([hidden])` 含 `#aiTopicGen`,排除 index.html 隐藏的 `#settingsModal` 干扰);③ 读取弹窗标题/文案 → 验证分支;④ **模拟点击 `AI` 按钮**(#aiTopicGen)→ `POST /api/ai/topics` → 轮询选题输入框(最长 100s,Dify 约 50s)→ 读取 3 个选题方向;⑤ 安全模式点「取消」关闭弹窗;`--confirm` 模式点「确认执行」触发任务 | +| 预期 | 弹窗标题「AI写脚本」;文案分支 = **「先进行账号设定分析,再生成完整脚本」**(account_persona 无俊希 → hasPersona=false → 首次场景);AI 选题返回 **3 个贴合俊希亲子 Vlog 人设的方向**(参考最新视频内容提炼,如职场探班/闺蜜聚会/病号特权等暖萌治愈向);toast「✅ 已生成 3 个选题方向」 | +| 验证点 | ① 弹窗结构齐全(#aiTopicInput/#aiTopicGen/[data-ok]);② 文案分支正确;③ 选题数量 ≥3 且与俊希人设相关(非泛泛);④ 选题可编辑;⑤ 脚本 Step1-4 全 PASS | +| 通过标准 | 脚本输出 `Step1 定位俊希按钮: PASS / Step2 弹窗结构: PASS / Step3-4 AI 选题: PASS`(**已实测通过 2026-08-31**:选题=突击妈妈公司送便当/替妈妈筹备周末闺蜜聚会/打工人妈妈病号特权日) | ### TC-10 AI 写脚本(S1-S11 完整流程) | 项 | 内容 | |---|------| -| 操作 | ① 选定一个选题方向(保留在输入框)→ 点「确认」→ `executeTask()` `POST /api/run` 写入 `~/.workbuddy/workbuddy.db` automations(once+now,cwds=`D:\AgentSkill\mcn-workshop`)→ WorkBuddy 自动执行创建会话(左侧会话栏 mcn-workshop 空间可见);② 轮询 `GET /api/run/status?id=`(5s 间隔,30min 上限)观察任务状态 running → done | +| 操作 | ① 选定一个选题方向(保留在输入框)→ 点「确认执行」(TC-09 脚本 `--confirm` 模式自动完成)→ `executeTask()` `POST /api/run` 写入 `~/.workbuddy/workbuddy.db` automations(once+now,cwds=`D:\AgentSkill\mcn-workshop`)→ WorkBuddy 自动执行创建会话(左侧会话栏 mcn-workshop 空间可见);② 轮询 `GET /api/run/status?id=`(5s 间隔,30min 上限)观察任务状态 running → done | | 预期 | 创作会话按脚本创作技能执行 **S1-S11 完整流程**(三态创作默认自动模式=静默执行、只落最终交付物):S1 理解需求 → S2 检索对标 → S3 确定选题 → S4 素材库 → S5 极致事件 → S6 情绪规划 → S7 结构搭建 → S8 大纲 → S9 脚本(分镜)→ S10 可选细粒度分镜 → S11 诊断复盘;prompt 带「选题方向:{topic}」 | | 验证点 | ① 任务状态流转正常(IN_PROGRESS → DONE/COMPLETED);② 脚本落盘 `MCNSkill项目/俊希/脚本1/`(产物链:05_故事选题 → 06_短视频框架 → 07_短视频大纲 → 09_短视频脚本,禁跳步);③ S9 脚本质量门禁:开场钩子绝对时长 3-7 秒(硬上限 10 秒,不看占比);④ 数字账取证(禁心算);⑤ 画面标注 5 字段【景别|环境|光线|场景|场景元素】;⑥ 人物标注格式统一;⑦ 交付物只含输出模板字段(验收口径=执行约束,不在正文自证) | | 通过标准 | 脚本全流程完成落盘;S9 脚本完整可拍;工作台任务状态 done;按钮恢复 | @@ -202,6 +246,9 @@ - 达人账号表当前仅 2 行:小咪橘座(70010,已解析/已生成)、旧梦留声机(70013,已生成)→ 俊希首次登记为 70014 - `MCNSkill项目/俊希/` 现有仅 `提示词01/` 子目录(分镜产出,属短视频分镜提示词技能),无账号分析产出 +- **工作台 mcn-plugin.db 俊希历史数据**:hot_accounts id=3(完整账号信息 + content 设定文本,source_dir=`D:\dshworkspace\账号解析\俊希` **旧路径已不存在**)、account_videos 108 条(source_dir=`D:\dshworkspace\抖音账号分析\俊希` 旧路径)、rewrite_log 3 条(2026-08-19,海鲜大餐主题);**account_persona 无俊希** → hasPersona=false → 弹窗走「先进行账号设定分析」分支,首次场景成立,无需清除 +- dsh 原库(~/.dsh/mcn-plugin.db)俊希数据与副本一致(.dsh 只读红线不写) +- **界面模拟点击测试脚本**:`mcn-work-shop/cdp-test-junxi.mjs`(2026-08-31 实测通过:账号列表定位俊希 PASS / AI写脚本弹窗结构 PASS / AI 选题 3 个方向 PASS);注意 `.modal` 选择器会被 index.html 隐藏的 `#settingsModal` 干扰,须限定 `:not([hidden])` 且含 `#aiTopicGen` - MCP 解析单条视频需等待 3-5 分钟;已解析过的视频可直接查询 - 工作台 #/reviews「AI脚本诊断」复盘按钮明确按 **mcn-script-review(脚本复盘)技能**执行(本用例主链路不含复盘,如需回归可追加) - 产出根目录:开发机 → 桌面 `MCNSkill项目/{达人昵称}/`(`references/路径配置.md` 权威) diff --git a/project/短视频脚本创作/V1.0/mcn-work-shop/cdp-test-junxi.mjs b/project/短视频脚本创作/V1.0/mcn-work-shop/cdp-test-junxi.mjs new file mode 100644 index 0000000..7a5d13a --- /dev/null +++ b/project/短视频脚本创作/V1.0/mcn-work-shop/cdp-test-junxi.mjs @@ -0,0 +1,115 @@ +// 模拟点击测试:俊希「AI写脚本」界面链路(#/accounts → AI写脚本弹窗 → AI 选题生成) +// 覆盖测试用例 TC-09 的界面交互环节(真实模拟用户点击,非仅文字步骤) +// +// 前置: +// 1. 工作台服务运行中(node server.js,端口 8899) +// 2. 浏览器 9223 调试实例已启动(browser-harness 调试实例) +// 3. 账号列表(hot_accounts)含「俊希」 +// 用法: +// node cdp-test-junxi.mjs # 只验证到「选题生成」,不触发创作任务(安全模式) +// node cdp-test-junxi.mjs --confirm # 选题生成后继续点「确认执行」,真实触发创作任务 +// 说明:脚本在 9223 实例新开标签页操作,不干扰用户已打开的页面;结束后自动关闭新开标签页。 + +const CONFIRM = process.argv.includes('--confirm'); +const BASE = 'http://localhost:8899'; + +// 1) 连接 9223 调试实例 +const list = await fetch('http://localhost:9223/json').then((r) => r.json()); +const page = list.find((t) => t.type === 'page'); +if (!page) { console.log('ERR: 9223 无可用页面,请先启动 browser-harness 调试实例'); process.exit(1); } + +const ws = new WebSocket(page.webSocketDebuggerUrl); +let id = 0; const pending = new Map(); +const send = (method, params = {}) => new Promise((res, rej) => { const i = ++id; pending.set(i, { res, rej }); ws.send(JSON.stringify({ id: i, method, params })); }); +ws.onmessage = (e) => { const m = JSON.parse(e.data); if (m.id && pending.has(m.id)) { const p = pending.get(m.id); pending.delete(m.id); m.error ? p.rej(new Error(m.error.message)) : p.res(m.result); } }; +await new Promise((r) => (ws.onopen = r)); +const ev = async (expr) => { const r = await send('Runtime.evaluate', { expression: expr, returnByValue: true, awaitPromise: true }); if (r.exceptionDetails) return { __err: (r.exceptionDetails.exception?.description || r.exceptionDetails.text).slice(0, 300) }; return r.result.value; }; +const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); +await send('Page.enable'); await send('Runtime.enable'); + +// 2) 新开标签页打开账号列表(不干扰现有页面) +const { targetId } = await send('Target.createTarget', { url: BASE + '/#/accounts' }); +await sleep(2500); +let targetPage = list2 => list2.find((t) => t.type === 'page' && t.url.includes('localhost:8899') && t.id === targetId); +let pages = await fetch('http://localhost:9223/json').then((r) => r.json()); +let tp = targetPage(pages); +if (!tp) { console.log('ERR: 新开标签页未找到 8899 页面'); process.exit(1); } +// 切换 WS 到新页面 +const ws2 = new WebSocket(tp.webSocketDebuggerUrl); +let id2 = 0; const pending2 = new Map(); +const send2 = (method, params = {}) => new Promise((res, rej) => { const i = ++id2; pending2.set(i, { res, rej }); ws2.send(JSON.stringify({ id: i, method, params })); }); +ws2.onmessage = (e) => { const m = JSON.parse(e.data); if (m.id && pending2.has(m.id)) { const p = pending2.get(m.id); pending2.delete(m.id); m.error ? p.rej(new Error(m.error.message)) : p.res(m.result); } }; +await new Promise((r) => (ws2.onopen = r)); +const ev2 = async (expr) => { const r = await send2('Runtime.evaluate', { expression: expr, returnByValue: true, awaitPromise: true }); if (r.exceptionDetails) return { __err: (r.exceptionDetails.exception?.description || r.exceptionDetails.text).slice(0, 300) }; return r.result.value; }; +const sleep2 = (ms) => new Promise((r) => setTimeout(r, ms)); +await send2('Page.enable'); await send2('Runtime.enable'); +await send2('Page.reload', { ignoreCache: true }); await sleep2(3500); + +// 3) 账号列表页:定位俊希行「AI写脚本」按钮 +const findBtn = await ev2(`(() => { + const btns = [...document.querySelectorAll('[data-create]')]; + const b = btns.find((x) => x.dataset.create === '俊希'); + if (!b) return { found: false, total: btns.length, names: btns.slice(0, 20).map((x) => x.dataset.create) }; + return { found: true, accId: b.dataset.accId, txt: b.textContent.trim() }; +})()`); +console.log('【Step1 账号列表定位俊希】', JSON.stringify(findBtn)); +if (!findBtn.found) { + console.log('FAIL: 账号列表未找到俊希「AI写脚本」按钮(若账号未关注,请先到周榜/账号列表关注俊希)'); + await send('Target.closeTarget', { targetId }); ws.close(); ws2.close(); process.exit(1); +} + +// 4) 模拟点击「AI写脚本」→ 轮询等待弹窗出现(confirmAiScript 先 await persona 接口再建弹窗,需等待) +await ev2(`(() => { const b = [...document.querySelectorAll('[data-create]')].find((x) => x.dataset.create === '俊希'); b.click(); return true; })()`); +const modal = await ev2(`(async () => { + for (let i = 0; i < 24; i++) { + // 只认非 hidden 且含 #aiTopicGen 的弹窗(index.html 有 hidden 的 #settingsModal 干扰) + const m = [...document.querySelectorAll('.modal-mask')].find((x) => !x.hidden && x.querySelector('#aiTopicGen')); + if (m) { + return { open: true, title: m.querySelector('h3')?.textContent.trim() || '', desc: m.querySelector('.dim')?.textContent.trim() || '', hasTopicInput: !!m.querySelector('#aiTopicInput'), hasGenBtn: !!m.querySelector('#aiTopicGen'), hasOk: !!m.querySelector('[data-ok]') }; + } + await new Promise((r) => setTimeout(r, 500)); + } + return { open: false }; +})()`); +console.log('【Step2 AI写脚本弹窗】', JSON.stringify(modal, null, 1)); +const descOk = modal.open && modal.title.includes('AI写脚本') && modal.hasTopicInput && modal.hasGenBtn; +const branch = modal.open && (modal.desc || '').includes('账号设定'); +console.log(branch ? ' → 文案分支:先进行账号设定分析(首次场景,符合预期)' : ' → 文案分支:基于当前账号设定(有设定场景)'); + +// 5) 模拟点击「AI」选题按钮 → 轮询选题输入框(Dify 约 50s) +await ev2(`(() => { const g = document.querySelector('#aiTopicGen'); g.click(); return true; })()`); +console.log('【Step3 AI 选题生成中…(最长等待 100s)】'); +let topics = ''; +const t0 = Date.now(); +while (Date.now() - t0 < 100000) { + await sleep2(3000); + topics = (await ev2(`(() => document.querySelector('#aiTopicInput')?.value || '')()`)) || ''; + if (topics && !topics.includes('(未生成内容)')) break; +} +console.log('【Step4 选题结果】'); +console.log(topics || '(超时未返回)'); +const topicOk = topics.split('\n').filter((l) => l.trim()).length >= 3 || /选题|方向/.test(topics); + +// 6) --confirm 模式:确认执行触发创作任务 +if (CONFIRM) { + console.log('【Step5 --confirm】点击确认执行,触发创作任务…'); + await ev2(`(() => { document.querySelector('[data-ok]').click(); return true; })()`); + await sleep2(1500); + const toast = await ev2(`(() => document.querySelector('.toast')?.textContent || '')()`); + console.log(' toast:', toast || '(未捕获)'); +} else { + console.log('【Step5 安全模式】未触发创作任务(加 --confirm 参数可真实触发)'); + await ev2(`(() => { document.querySelector('[data-cancel]')?.click(); return true; })()`); +} + +// 7) 汇总 +console.log('\n===== 测试结果 ====='); +console.log('Step1 定位俊希按钮:', findBtn.found ? 'PASS' : 'FAIL'); +console.log('Step2 弹窗结构:', descOk ? 'PASS' : 'FAIL'); +console.log('Step3-4 AI 选题:', topicOk ? 'PASS' : 'CHECK'); +console.log(CONFIRM ? 'Step5 任务触发: 已提交(到左侧会话栏查看)' : 'Step5 任务触发: 未触发(安全模式)'); + +// 关闭新开标签页 +await send('Target.closeTarget', { targetId }); +ws.close(); ws2.close(); +process.exit(descOk && findBtn.found ? 0 : 1);