Install
openclaw skills install @laneovcc/qq-group-homework-summarizerQQ群作业整理 —— 从 QQ 群「群作业」抓取指定日期的作业内容(含图片附件),生成排版规范的 A4 Word 文档(默认单页、可多页,支持多群合并、科目筛选、仅文字版),并可发送到指定邮箱或微信;内置 doctor 环境自检(CLI/daemon/连通性/登录态/产物),适合挂无人值守定时任务。This skill should be used when the user asks to organize, export, or send QQ group homework (群作业) as a document — for example "把X月X日的群作业整理成Word"、"把今天的作业导出成文档"、"把作业发到我邮箱/微信"。触发词:群作业、作业整理、作业文档、QQ作业、发作业给XX。
openclaw skills install @laneovcc/qq-group-homework-summarizer将 QQ 群「群作业」中某一天的内容(文字 + 图片)抓取出来,自动排成一页 A4 的 Word 文档, 并按需发送到邮箱或微信。
| 事实 | 说明 |
|---|---|
| 登录态在浏览器 | 需要 QQ 浏览器内已登录的 QQ 账号;QQ 客户端的登录不共享给浏览器 |
| 列表接口剥图 | get_hw_list.fcg 只返回文字,图片作业仅显示「【图片】」 |
| 图片须走详情接口 | get_hw_detail.fcg 才返回 type:"img" 与 url |
| 会话层不可用 | CLI 1.5.4 与 QQ 浏览器 21.7 扩展的会话协议不兼容,禁止调用 browser_start_session,直接调业务命令 |
| CLI 会被强制下线 | 旧版(≤1.5.4)所有子命令返回 blocked until you upgrade;先升级 + 重启 daemon,别误判成未登录 |
| 必须在沙箱外运行 | 浏览器命令需 dangerouslyDisableSandbox: true,否则安全删除机制 fail-closed |
接口细节、参数、返回结构见 references/api.md。
登录态是本 skill 最高频的故障源(2026-09-01~09-03 连续三天、每天至少一次)。 且存在双向误判:既可能「bkn 有值但其实没登录」,也可能「浏览器已登录但接口仍报错」。
bkn 由 cookie 里的 skey 算出。只要 cookie 没过期就一定能算出来,
而 cookie 过期时间远长于 ptlogin 会话有效期。所以:
bkn 仍能正常输出,且数值往往与失效前完全相同(实测 689439289 前后不变)get_hw_detail.fcg)会暴露问题;列表接口校验较松,照常返回数据| # | bkn | day --refresh | 登录页 innerText | 判定 | 处理 |
|---|---|---|---|---|---|
| 1 | ✅ 有值 | ✅ 成功 | (已跳转群管理页) | 正常 | 直接生成文档 |
| 2 | ✅ 有值 | ❌ verify fail | QQ群 / QQ登录 | 真的没登录 | 引导用户登录 → 重探 bkn → 再拉数据 |
| 3 | ✅ 有值 | ❌ verify fail | 已跳转(有昵称 +「退出」) | 已登录,但 bkn 缓存过期 | 只需重探 bkn,不用再登录 |
第 3 态是最隐蔽的:用户明明点了登录,命令却照样报 verify fail, 极易误判成「登录没生效」而让用户反复登录。
# ① 接口侧:详情接口是否报错(先重试一次再下结论,避免偶发抖动)
python scripts/qq_hw.py day <日期> --refresh
# ② 浏览器侧:直接看登录页(判定第 2/3 态的唯一可靠依据)
qqbrowser-skill browser_go_to_url --url "https://qun.qq.com/#/login"
qqbrowser-skill browser_eval_content_js --script "document.body.innerText.slice(0,300)"
innerText 判读:
QQ群 / QQ登录 → 未登录(第 2 态)https://qun.qq.com/#/member-manage/base-manage,含昵称与「退出」按钮 → 已登录(第 3 态)用户点完登录后,qq_hw.json 里缓存的仍是旧会话的 bkn,而 skey 已经变了。
不重探就去拉数据,必然再次 verify fail。
# 用户点登录 → 先刷新 bkn(数值会变化,如 689439289 → 196315760)→ 再拉数据
python scripts/qq_hw.py bkn --gid <群号>
python scripts/qq_hw.py day <日期> --refresh
先跑自检,别凭感觉排查——大部分「失败」其实卡在环境,而不是作业数据:
python scripts/qq_hw.py doctor --gid <群号>
一次输出:Python 依赖 / CLI 路径 / daemon / 扩展连通性实测 / 登录态 / bkn 缓存 / 现存产物。
全绿才继续;有 [FAIL] 就按它的提示修。定时任务每轮开头都跑一次,
可避免整轮耗在错误方向(CLI 被封锁却去让用户登录、产物已生成却重做一遍)。
⚠️ 解释器别选错(2026-09-04 新增):依赖通常装在隔离 venv 里,而 base 解释器 (如
.../python/versions/3.13.x/python.exe)没有任何依赖,用它跑docx会直接报ModuleNotFoundError。始终用与qqbrowser-skill同目录的 python(即QQB_CLI所在Scripts/下的python.exe)。doctor 检测到依赖缺失时会自动提示正确路径。🛟 脚本会自己兜底:
docx子命令发现当前解释器缺python-docx/pillow时, 会自动切到QQB_CLI同目录的 venv 解释器重跑(打印一行提示,输出与退出码原样透传)。 所以定时任务 prompt 里即使写死了 base 解释器路径也不会翻车, 不用为此批量修改已配好的 automation。
自检不通过时的常见修法:
# ① CLI 被服务端强制下线(<=1.5.4)→ 所有浏览器子命令都返回
# "❌ Browser skill commands are blocked until you upgrade"
# 这不是扩展问题、也不是未登录 —— 看到 blocked 就先升级,别去查扩展:
pip install --index-url https://pypi.org/simple --upgrade qqbrowser-skill # 必须官方源,镜像常滞后
qqbrowser-skill stop && qqbrowser-skill serve --daemon # 不重启 daemon 不生效
# 🆕 1.5.6 的 `status` 里 `Connected clients` 可能恒为 0,但业务命令照常可用。
# → 不要死等 clients≥1,直接跑一次业务命令(或 doctor)验证连通性。
# ② CLI 不在 PATH(隔离 venv 安装的常见情况)→ FileNotFoundError: [WinError 2]
QQB_CLI="<venv>/Scripts/qqbrowser-skill.exe" python scripts/qq_hw.py doctor
# ③ 依赖缺失
pip install python-docx pillow pypdf
首次安装时才需要比对 pip 源版本(镜像滞后会装到旧版,旧版协议不兼容):
pip index versions qqbrowser-skill --index-url https://pypi.org/simple # 官方源最新版
pip index versions qqbrowser-skill # 本地镜像源版本
# 两源一致 → 用本地镜像源;不一致 → 用官方源;安装失败 → 阿里云源
# https://mirrors.aliyun.com/pypi/simple/
python scripts/qq_hw.py bkn --gid <群号> # 单群
python scripts/qq_hw.py bkn --gid <群号1>,<群号2> # 多群(逗号分隔)
bkn = xxxxx,自动写入 qq_hw.json(--gid 支持逗号分隔多群)⚠️ 本节最容易被误判,详见下方「登录态:三态判定与恢复」。 核心结论:
bkn探测成功 ≠ 已登录;反过来,浏览器已登录也仍可能 verify fail(bkn 缓存没刷新)。
python scripts/qq_hw.py list --size 100 # → hw_list.json
python scripts/qq_hw.py day 2026-05-11 # → hw_day_2026-05-11.json
列表过期时加 --refresh。
⚠️ 若接口返回
ptlogin-ex verify fail(retcode 2001),先按上方 「登录态:三态判定与恢复」排查,不要直接认定未登录。
📌 老师用「消息」而非「群作业」布置科目时,接口拉不到(2026-09-01 数学作业即如此)。 处理方式:让用户把内容发来,手动补一条进
hw_day_<日期>.json,再直接跑docx:json { "id": "<自拟唯一 id>", "title": "X月X日XX作业", "ts": <Unix 秒>, "course": "<科目>", "pub": "<发布人>", "fbname": "<昵称>", "group_id": "<群号>", "manual": true, "c": [{"t": "str", "s": "1. xxx\n2. yyy"}] }⚠️
ts要插在已有条目之间以保持时间顺序。 ⚠️ 补完后千万不要再跑day --refresh,否则整份 JSON 被覆盖、手动补的内容丢失。
python scripts/qq_hw.py docx 2026-05-11 # 默认:单页、全部科目、含图片
python scripts/qq_hw.py docx 2026-05-11 --courses 语文,数学 # 仅指定科目
python scripts/qq_hw.py docx 2026-05-11 --text-only # 仅文字版(不插图)→ 作业_2026-05-11_文字版.docx
python scripts/qq_hw.py docx 2026-05-11 --allow-multi # 允许分页(内容过多时)
| 参数 | 说明 |
|---|---|
--courses 语文,数学 | 科目筛选(逗号分隔);默认当天全部科目 |
--text-only | 仅文字版,跳过图片下载与插入 |
--allow-multi | 允许多页:内容过多放不下一页时使用,必须先征得用户同意 |
--scale N | 单页模式下压缩图片高度的系数(>1 更保守) |
多页逻辑:默认仍按单页排版。若
pages校验发现 >1 页,先询问用户是否允许多页; 用户同意后再加--allow-multi重新生成(图片按自然高度排,允许分页)。
PDF 是 Epson 等打印邮箱最稳的格式。默认一律把作业以 PDF 附件发送,docx 仅作本地留存 (打印前记得提醒用户在打印服务里设为「只打印附件」,见下方「打印省纸提醒」)。
两级生成策略(按优先级):
pdf、pdfkit-py 等)做 docx → PDF;
它是纯 Python 方案、不依赖本机 Word、跨机器更稳。python scripts/qq_hw.py pdf 2026-05-11 # → 作业_2026-05-11.pdf(Word COM 回退)
Word COM 方案的中文路径坑(直接 SaveAs 中文名被静默吞、8.3 短路径等)见 troubleshooting.md §10。docx 单页则 PDF 也单页,页数以步骤 5 校验 docx 为准。
python scripts/qq_hw.py pages 作业_2026-05-11.docx
PAGES=1 → 完成PAGES=2 → 加大 --scale(默认 1.15,试 1.3~1.5)后重新生成:python scripts/qq_hw.py docx 2026-05-11 --scale 1.4
| 元素 | 规范 |
|---|---|
| 文首 | 日期标题,居中,微软雅黑 14pt |
| 每条作业 | Heading 2,微软雅黑 13pt;仅保留科目名——去掉 ^\d{1,2}月\d{1,2}日 前缀,不加编号、不显示发布人昵称(多群时保留「(群xxx)」标注) |
| 具体内容 | 编号列表 + 正文样式 + 宋体 10.5pt;剥掉原文自带的 1. 2. 3. 前缀,每条作业独立编号(从 1 开始)——手工编号 + 悬挂缩进(避免 Word「List Number」跨科目连续计数) |
| 图片 | 竖图(宽<高)两两横排,横图单独成行 |
单页约束的关键:必须在样式层把 Normal / Heading 2 / List Number 的
space_before/after 归零、line_spacing 设为 1.0(样式自带 1.15 倍行距会撑到第二页);
页边距 0.8cm;表格单元格边距清零。
🚨 第一步必须用精确名加载工具(2026-09-02 踩坑修正):MCP 工具是延迟注册的,
用 ToolSearch(queries=[...]) 模糊搜索搜不到 mcp__agent-mail__*(只能搜到内置的
agent_mail_upload_attachment / agent_mail_download_attachment),会误判成「没有发信工具」。
正确做法:
ToolSearch(tool_names=["mcp__agent-mail__SendMessage", "mcp__agent-mail__GetMe"])
拿到 schema 后再 DeferExecuteTool("mcp__agent-mail__SendMessage", {...})。
不要再去 app.asar 里翻 HTTP 端点——本机没有对应的本地 HTTP 服务,纯属浪费时间。
🆕 发信工具可能整轮缺席(2026-09-04 17:00 / 17:30 连续两轮实测):
agent_mail_upload_attachment是内置工具,几乎总在;但发信的 MCP 工具mcp__agent-mail__SendMessage不保证注册。缺席时精确名 + 模糊 ToolSearch 都只回两个附件工具,DeferExecuteTool 报not found in the deferred tools index, 且~/.workbuddy/mcp.json里查不到该服务器。 → 上传附件成功 ≠ 能发信;每轮先确认发信工具可用,别等附件传完才发现发不出去。🔑 但缺席是「按轮次」的,不是服务没启用(2026-09-04 17:41 证实): 同一台机器、同一任务,前两轮完全搜不到,用户新开一轮对话后立刻可用。 所以正确应对是:产物落盘 + 发告警 + 等下一轮调度或用户介入,并且——
- ❌ 不要据此判定「Agent Mail 没启用」
- ❌ 不要去改
~/.workbuddy/mcp.json- ❌ 不要用 curl / Python 自建 HTTP 绕过(本机没有对应本地服务,纯属浪费时间)
- ❌ 不要在一轮里反复重试耗尽轮次:一轮只探一次,探不到就收尾
调用序列:
1. agent_mail_upload_attachment(file_path) → file_id
2. mcp__agent-mail__SendMessage(
to=[{"email": "<打印邮箱>"}], # 注意是对象数组,不是字符串数组
subject="作业_<日期>",
body=" ", # 空字符串会校验失败,传一个空格
file_refs=[{"file_id": "<上一步的 file_id>"}],
skip_confirmation=true # 仅当用户已预先授权
) → {"queued": true}
3. 可选校验:mcp__agent-mail__ListMessages(dir="sent", limit=1)
⚠️ 未经用户明确授权时不要传 skip_confirmation:SendMessage 会返回
CONFIRMATION_REQUIRED,此时必须先向用户展示收件人 / 主题 / 附件并征得明确同意,
再带 confirmation_token 重试。定时任务等无人值守场景由用户预先授权,可直接 skip。
账号上下文(mcp__agent-mail__GetMe)可查别名、scopes(需含 mail:send)、
日发送配额(50/天)、附件上限(单文件 20MB)。
备用通道:企业微信邮件 wecom-cli mail send(先加载 wecomcli-email skill)
⚠️ 别把它当兜底:该通道 2026-09-02 起持续返回 850003(机器人「邮件」权限过期), 与「消息」权限是两套,消息能发不代表邮件能发。发信失败时不要为它反复重试多轮, 最多试一次确认状态,然后转「告警 + 等下一轮」。
wecom-cli mail send --json '{
"to": {"emails": ["<打印邮箱>"]},
"subject": "作业_<日期>",
"file_path": "<正文 .md 绝对路径>",
"content_type": "markdown",
"attachments": [{"file_path": "<PDF 绝对路径>"}]
}'
.md 写成一个空格(空字符串会被接口判校验失败)。📌 中文路径分场景,别一刀切(都实测过):
| 场景 | 中文路径 |
|---|---|
agent_mail_upload_attachment 上传附件 | ✅ 直接用中文绝对路径,没问题 |
SendMessage / 企微邮件传路径 | ✅ 同上 |
PowerShell 传参(Word COM 转 PDF、count_pages.ps1) | ❌ 会被按系统编码解码成乱码 → 必须先 cp 成 ASCII 名(见 troubleshooting §10) |
两条通道都不通时:不要写已发送标记,只发企微告警 + 保留现场文件(PDF/docx), 等下一轮调度重试。
🖨️ 打印省纸提醒(重要):Epson 等打印邮箱默认会连邮件正文一起打印, 常常多出一张只写了两行说明的纸。发送前应提醒用户在打印服务(如 Epson Connect)的 打印设置里勾「只打印附件 / 不打印正文」,或把邮件正文留空、尽量简短。 这样每份作业只出 1 页,避免浪费纸张。
加载 wecomcli-message skill 发送消息;需先上传文件时用 wecomcli-media 换取 media_id。
⚠️
sessions list经常返回sessions_count: 0(本机连续多日为 0)。 此时按 skill 规则兜底:用wecom-cli identity whoami返回的授权真人用户 ID 作为chat_id发给用户本人,不要因为「找不到「本地助理」会话」就放弃推送。
本节是 2026-09-01~09-04 连续多天定时跑通「群作业 → PDF → 打印邮箱」沉淀出的经验。
这是配定时任务时最容易翻车的一条(2026-09-04 实测确认):
BYHOUR/BYMINUTE只接受单个整数,传逗号列表直接报BYHOUR must be an integer between 0 and 23/BYMINUTE must be an integer between 0 and 59。 只有BYDAY支持逗号列表(BYDAY=MO,TU,WE,TH,FR)。
❌ 下面这条看着合理但平台不接受,照抄必报错:
FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;BYHOUR=15,16,17,18;BYMINUTE=0,30
❌ 另一个反例:FREQ=HOURLY;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR;BYMINUTE=0,30
—— 它是工作日全天 24 小时每半小时触发(48 次/天)。若实际只在 15:00-18:30 干活,
约 40 次是空转(实测累计 48+ 条无意义记录)。
✅ 正解:一个时间点 = 一条 automation,拆成多条。例如「工作日 15:00-18:30 每半小时」拆 8 条:
FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;BYHOUR=15;BYMINUTE=0
FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;BYHOUR=15;BYMINUTE=30
FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;BYHOUR=16;BYMINUTE=0
... 以此类推,直到 BYHOUR=18;BYMINUTE=30
拆多条时保留最关键的那一轮沿用原 id(历史经验绑在 id 上),其余新建。 例:把 18:00 兜底那条沿用原 id,这样它继承下来的运维上下文不丢。
即使 rrule 已精确限定,仍建议在任务 prompt 里保留一道时间窗闸门(第 0 步), 双保险避免边界情况空跑。
每条 automation 的 prompt 都要自包含(平台不会把其它条目的上下文带过来), 环境要点(Python/CLI 绝对路径、打印邮箱、登录态与 bkn 的处理)要写全。
发送成功后立即在工作区写 hw_auto_sent_<日期>.flag,下一轮开头检查到就跳过。
这让截止时间之后的重复调度天然安全,也避免同一天重复打印。
检查 hw_auto_sent_<TODAY>.flag → 存在即直接结束,不拉作业、不发邮件、不推送
两条邮件通道(Agent Mail / 企微邮件)都不通时,绝不写已发送标记;
只发企微告警 + 保留现场文件(作业_<日期>.pdf / .docx),等下一轮重试。
写了标记会导致当天再也发不出去。
上一轮若已生成 作业_<日期>.docx / .pdf,下一轮重试时直接复用,
不要重新 day --refresh + docx + pdf(2026-09-04 17:41 即靠复用 17:30 的 PDF 秒发成功):
| 情况 | 做法 |
|---|---|
有 .pdf 且 JSON 未变 | 直接发 |
只有 .docx | 转 PDF 后发 |
| 都没有 | 才走完整流程 |
重跑 day --refresh 还有个副作用:会覆盖 hw_day_<日期>.json,
把手动补录的条目冲掉(见步骤 3)。
「产物齐备但发信通道不通」是最常见的僵局(2026-09-04 连挂两轮)。收尾姿势:
三科(语文/数学/英语)未集齐时的推荐策略:
| 条件 | 行为 |
|---|---|
| 缺科 且 未到截止时间 | 不发送,推送「已收到 XX,仍缺 XX,XX 点前每半小时复查」 |
| 缺科 且 已到截止时间 | 按已收到科目强制生成发送(需用户预先授权),主题与推送里标注缺科 |
| 当天一条作业都没有 | 不发送,推送说明 |
强制发送时 --courses 只传已收到的科目,主题形如 作业_2026-09-03(缺英语)。
登录态会在窗口中途掉线,不能凭上一轮成功就跳过本轮校验(见上方三态判定)。
Epson 打印邮箱会连正文一起打印 → 多打一张纸。 邮件正文留空(传一个空格),并提醒用户在打印服务里勾「只打印附件」。
scripts/qq_hw.py 单入口,子命令:
| 子命令 | 作用 |
|---|---|
doctor | 环境自检:依赖 / CLI / daemon / 扩展连通性 / 登录态 / bkn 缓存 / 现存产物,一次说清。定时任务每轮开头先跑它 |
bkn | 探测 bkn 并写入配置 |
list | 拉取作业列表 |
day <日期> | 拉取当天详情(含图片 URL) |
docx <日期> | 生成 Word |
pdf <日期> | docx → PDF(Word COM 回退方案) |
pages <file.docx> | 统计页数(需本机装 Word) |
bkn/list/day 的 --gid 支持逗号分隔多群;docx 支持 --courses(科目筛选)、
--text-only(仅文字)、--allow-multi(多页)。
多群数据自动按(群号, hw_id)去重,避免同群号重复拉取产生重复条目。
scripts/count_pages.ps1 供 pages 子命令调用(需本机装有 Word)。
见 references/troubleshooting.md,覆盖: 会话协议不兼容、pip 镜像滞后、CLI 输出双重转义、Word COM 页数统计、单页约束失效、 登录态三态判定、MCP 工具延迟注册与按轮次缺席、定时任务运维、 CLI 被服务端强制下线、rrule 平台限制、解释器选错等。
💡 拿不准是哪一类问题时,先跑
python scripts/qq_hw.py doctor --gid <群号>, 它会直接告诉你卡在哪一步。