|
|
23d35cd942
|
refactor(common): 通知实现搬进 common.js,所有脚本一行 require 即可用
上一版把 notify 内联在 qqmusic 脚本里,只解决了单个脚本。这次搬到仓库根的
common.js,后续任何脚本 require('../common') 就能发通知。
common.js 新增导出:
notify(title, content, opts) webhook + pushplus 双通道,任一成功即 ok,永不抛错
notifyConfig(env) 读 env 并给默认值(可注入替身,方便测试)
httpPost(url, headers, body) 通用 POST,http/https 自适应
parseHookHeaders(raw) 一行一个 Key: Value
buildHookBody(t, c, tpl, ct) 拼 body,JSON 不合法就重建
Env 保持原样未动(jd 与 xiequ 在用)。
顺带删掉第 1 行的 require('node-fetch') —— 它是死代码:Env 内部所有 HTTP 走的是
require('got') + require('tough-cookie'),fetch 只以 $task.fetch(...) 的形式出现在
QuantumultX 分支(宿主 API,不是 node-fetch)。留着它反而会在容器缺 node-fetch 时
让整个文件 require 抛错,连带 jd / xiequ 一起挂。
qqmusic 脚本从 125 行内联实现缩成一行 require('../common'),删掉重复的
httpPost / parseHookHeaders / buildHookBody / notify 四份副本。
测试从 qqmusic/test_notify.js 挪到仓库根 test_common_notify.js,断言从 38 加到 69:
- parseHookHeaders 九种输入(新增重复 key 覆盖)
- buildHookBody 五种(新增自定义模板 + urlencoded + text/plain)
- notifyConfig 十项默认值与 trim
- notify 八种状态(200/204/201/401/403/400/429/连不上)
- 无配置、双通道互不牵连
- Env 兼容性(确认 node-fetch 已移除、老接口都在)
端到端已验证:common.js 的 notify 发出的 content 与原始 desp 逐字相同(10 行完整),
title 正确,header 带 token,HTTP 204 判为成功。
README(根 + qqmusic)新增 common.js 公共库章节,列出 notify 支持的八个环境变量
与「为什么不能放 sendNotify.js」的 deps 覆盖原因。
|
2026-11-07 02:10:23 +08:00 |
|
|
|
6a64a7de6f
|
fix(qqmusic): 通知改为脚本自实现,断开对青龙 sendNotify 的依赖
真机定位(2026-11-07):仓库里的 sendNotify.js 每轮都被覆盖,改它不生效。
青龙 shell/update.sh 的 gen_list_repo 复制顺序是三步,后一步盖前一步:
407: cp -f $file_notify_js "${dir_scripts}/${uniq_path}" 面板自带的 sendNotify.js
415: cp -f $_file "${dir_scripts}/${uniq_path}/${file_path}" 仓库的(覆盖①)
421: cp -rf $dir_dep/* "${dir_scripts}/${uniq_path}" /ql/data/deps/*(再覆盖②)
而 share.sh:228 在初始化时把面板自带的 sample/notify.js 拷成了 /ql/data/deps/sendNotify.js。
③ 排在最后 → 最终文件永远是面板自带那份,204 修复永远不生效。
改为脚本内联实现 notify(),零依赖(只用内置 http/https):
- httpPost / parseHookHeaders / buildHookBody / notify 四个函数,约 90 行
- 2xx 一律算成功(204 不再误判)
- buildHookBody 对 application/json 先试 JSON.parse,不合法就 JSON.stringify 重建,
内容里带引号/换行/反斜杠都安全(青龙 parseString 那个坑绕开了)
- 双通道:webhook + pushplus 直发,任一路成功即 ok
- 两路都没配时打一行明确提示,不再静默
main() 里删掉 require('../sendNotify'),改成 await notify('QQ音乐签到', desp)。
第四参 author 的追加逻辑也一并没了,正文不再带「本通知 By:github.com/whyour/qinglong」。
新增 test_notify.js:38 条断言
- parseHookHeaders 八种输入(含字面量 \n 不切开)
- buildHookBody 三种特殊字符(双引号/反斜杠/制表符)
- notify 六种状态码(200/204/201 通过,401/403/400/429 失败)
- 无配置、双通道部分失败
端到端已验证:mock 服务收到的 content 与原始 desp 逐字相同(10 行完整),
title 正确,header 带 token,HTTP 204 判为成功。
README 重写「通知发不出去怎么查」:坑 0 改成 deps 覆盖(附验证命令),
坑 1-3 保留但标注为「若又接回 sendNotify 才需注意」,新增 pushplus 展示限制的
三种官方解法与代价对照。
|
2026-11-07 01:37:59 +08:00 |
|
|
|
81976fb18b
|
Revert "feat(qqmusic): 标题带结论 + 首行放摘要,折叠状态也能看关键信息"
This reverts commit f5c9ca6fa1.
|
2026-11-05 20:49:25 +08:00 |
|
|
|
f5c9ca6fa1
|
feat(qqmusic): 标题带结论 + 首行放摘要,折叠状态也能看关键信息
用户截图确认:pushplus 的微信卡片只显示「标题 QQ音乐签到 + 正文第一行」,
后面的明细全折叠。查 pushplus 官方文档,这是平台默认行为 ——
服务号模板消息默认不展示具体内容,必须点「查看详情」。
三条官方解法都要额外成本(发「激活消息」24h 内限 5 条 / 开会员 /
绑定自己认证的服务号每年 300 元),所以不改通道,改信息密度:
- 标题从固定的「QQ音乐签到」改成三态:QQ音乐签到 ✅ / 🟡 / ⚠️
- 正文第一行从「✅ QQ音乐签到 · 全部完成」改成带数字的摘要:
「✅ 全部完成 · 成长值 +35」「✅ 已完成 · 成长值 +15 · 1 项待处理」
- 引入三态结论 state:done / warn(有 △ 但无 ✗,仍算完成)/ fail
- 调用 sendNotify 时第四参传空串,去掉尾部「本通知 By:github.com/...」
标题不放数字的理由:平台抑制指纹是 sha256(kind|credId|title去数字),
数字本来就被 titleKeyOf 剥掉,带 +35 / +15 算同一指纹,抑制不会失效。
顺带修 main 里的累加:blocks 从数组改成 {body,total,warn},
成长值合计与待处理项数在拼装前算好,摘要行才能直接用。
验证:node --check / 签名对拍 6 组 PASS / 5 个场景
(全好、有遗留、有失败、登录失效、双账号)的 state+标题+首行逐字比对 PASS /
_probe_meta 四文件 PASS
|
2026-11-05 20:43:22 +08:00 |
|
|
|
483bc29424
|
fix(qqmusic): 通知发送失败不再静默 + 新增通知诊断工具
现象:青龙日志里「自定义发送通知消息失败!!」,pushplus 只收到一行标题。
根因排查(三处,都已实测复现):
1) 脚本里 require('../sendNotify') 是 catch{} 静默吞掉 —— 容器缺 got 时
一条通知都发不出去且日志无痕,只能靠猜。改成打印一行原因。
2) sendNotify 的 parseString 用 /(\w+):\s*((?:(?!\n\w+:).)*)/g 解析
WEBHOOK_BODY,靠「换行 + \w+:」断值。单行 JSON 没有换行,整个 body
被吞掉 —— 实测 '{"title":"$title",...}' 解析结果为 {}(0 个 key)。
畸形 body → 对方返回非 200 → 正是「自定义发送通知消息失败」的来源。
3) webhookNotify 有三个前置条件,缺一个就静默 resolve(不发也不报错):
URL/BODY 至少一个含 $title、WEBHOOK_METHOD 必须显式设、body 能被解析。
新增 _diag_notify.js(仓库根,与 sendNotify.js 同级):
- 查 sendNotify 能否加载、通知 env 是否读到(token 脱敏)
- 复刻 parseString,直接告出 WEBHOOK_BODY 会解析成什么
- 直连 pushplus 发 txt 与 html 两条,对比哪个模板正文完整
- 最后走一遍完整 sendNotify,与直连结果对照
用来区分「通道没发」和「微信侧展示折叠」两件事。
验证:node --check / 签名对拍 6 组 PASS / 加载失败时日志文案实测输出
/ _probe_meta 三元数据 PASS
|
2026-11-05 08:54:26 +08:00 |
|
|
|
2d29abb440
|
feat(qqmusic): 通知改成逐项明细 + 结论标题
之前通知只有一行「本次新增成长值 N」,看不出这轮到底做了什么、
哪些没做成、要不要人工介入。
改动:
- QMClient 新增 steps 数组与 step(name,status,detail) 记录点,
status 四态:done=做到 / warn=部分完成留待下次 / fail=失败 /
skip=无需处理(今日已完成或未开放)
- verdict() 汇总:有 fail→fail,有 warn→warn,其余 done
- 每个动作补上 step():任务中心签到、挂件、装扮、听书行为、
节目开始标记、四项领奖、福利站签到/礼券/旅程激励/月里程碑/
月度好礼/臻享抽奖、分享卡
- 新增 renderAccount():done 逐行列,warn/fail 逐行列,skip 折叠成
一行(避免十几个「今日已完成」淹没有效信息),分享卡链接单独一行
- 通知标题按结论给:✅ 全部完成 / ⚠️ 有未完成项
- 登录态失效保持 🔴 单列并直接给处理指引,不进明细列表
设计取舍:skip 折叠而不是省略 —— 折叠能看出「今天该做的都做了」,
省略会让人怀疑是不是漏跑了。done/warn/fail 永不折叠。
验证:node --check / 签名对拍 6 组 PASS / verdict 三态用例 /
渲染两例(含卡片链接与不含)输出符合预期 / _probe_meta 三元数据 PASS
|
2026-11-05 00:00:44 +08:00 |
|
|
|
c7c341e10a
|
feat(qqmusic): 取卡结果回传平台(QM_CARD_CALLBACK)
手动取卡目前要「登青龙 → 点运行 → 翻日志复制链接」。加一条可选回传:
脚本跑完把结果 POST 回平台,由平台转发到微信,手机上直接收到链接。
实现(脚本侧,零依赖 Node 内置 https):
- QM_CARD_CALLBACK 回传地址(如 https://<平台域名>/api/hooks/card)
- QM_CARD_TOKEN 鉴权 token,缺省回落 QL_HOOK_TOKEN
- 回传体只有 {cards:[{uin, ok, url|reason}], at},**不含 cookie/key** —— 走公网不泄凭证
- 回传失败只记日志,绝不让取卡本身算失败(卡已建出、链接已在日志里)
- 未配 QM_CARD_CALLBACK 时完全跳过,行为与之前一致
- 顺带把 pushCardResult 加进 module.exports,供单测直接调用
验证:node --check / 签名对拍 6 组 PASS / 4 个回传用例(未配跳过、正常 POST
含 token 头与 body 结构、URL 非法跳过、服务端 500 不抛)全 PASS /
_probe_meta 三元数据 PASS
|
2026-11-04 23:31:43 +08:00 |
|
|
|
656442e4d6
|
feat(qqmusic): 手动取卡模式 + 复用本月未领完的卡
两件事:
1) 手动取卡入口(给好友送卡用,点一次就出链接):
task qqmusic/scrip_qqmusic_checkin.js -- card
或 QM_CARD=1。青龙 task 的 -- 之后是脚本参数,经 shell/task.sh 的
format_params 收进 script_params、otask.sh 的 run_task_command 原样透传给
node(已读青龙 shell 源码确认,不是猜的)。手动模式只跑取卡,不跑签到。
2) 复用优先(照抄落地页行为):
体验券落地页的按钮逻辑是 createCard(d ? cardId : ) —— 已有卡就复用那张,
没有才建新的。原实现只会 CreatCard,会不断堆废卡。现在先扫本月卡列表,
对每张调 CheckCard 拿 cardInfo.left,>0 就直接吐那张的链接,绝不重复建卡;
全部领完且配额有剩才 CreatCard。
同时纠正三处口径:
- 输出文案「已送出」→「已被领」:reciveUinList 是好友领走记录,不是主动送出
- 区分三个数:sum=本月配额 / reciveUinList.length=已被领 / cardInfo.left=单张卡
剩余可领次数(一张卡可被多人领)。「还能送几张」= sum - 已被领
- CheckCard 顺带校验 ownerUin 是自己(自己领不了自己的卡,能确认卡还有效)
验证:node --check / 签名对拍 6 组 PASS / 空 env 与 -- card 与 QM_CARD=1 三种
入口均优雅跳过 / 7 个 mock 用例(复用/多卡择一/新建/配额用完/无卡直建/只读/
单卡过期跳过)全 PASS / _probe_meta 三元数据 PASS
|
2026-11-04 22:57:49 +08:00 |
|
|
|
2bfddc7e04
|
feat(qqmusic): VIP 体验券(分享卡)配额查询与建卡
从体验券落地页 y.qq.com/m/vip/experience_card/dist/index.html 的
index.036b2e716.js 逆出 music.vip.VipFreeCardSvr 全部 7 个 method,
并在 u6 网关实测路由(GetCanNumConf 用假 key 也返回真实配额表
{1:1,2:1,3:2,4:3,5:3,6:5,7:5,8:7,9:10,10:15},说明该 method 不校验登录;
CreatCard / CheckCurrentCardList 返回 1000=未登录,确认需要登录态)。
语义(官方特权页):有效期内的豪华绿钻每 31 天可按等级赠送不同非会员
好友 3 天体验券,LV1-2 各 1 张、LV3 2 张、LV4-5 各 3 张、LV6-7 各 5 张、
LV8 7 张。卡只能送人,自己领不了自己的(CheckCard 的 ownerUin 判定);
好友非会员自然月限领 1 张。
实现:
- 新增 QMClient.shareCard(),接在 vipStation 之后,失败不拖垮主流程
(QmDead 照常上抛,其他异常只记日志)
- 默认只读:报「本月配额 N 张 / 已送 M 张 / 剩余 K 张」,不 CreatCard
- QM_SHARE_CARD=on 才在 remain>0 时 CreatCard,输出 cardid 分享链接
- 配额来源:本月卡 sum 优先;本月无卡时取 confInfo 最大档位作上限参考
(不拿 confInfo[0] 兜底 —— 表里没有 0 号档,会取到 undefined 让 remain 恒 0)
- run() 返回值新增 cardUrl,汇总与通知里带分享链接
验证:node --check 通过 / 签名对拍 6 组 PASS / 空 env 优雅跳过 /
5 个 mock 用例(只读/用完/建卡/无卡兜底)输出符合预期 /
_probe_meta 三元数据 PASS
|
2026-11-04 22:43:06 +08:00 |
|
|
|
87debf37a9
|
chore(meta): 三个脚本统一青龙元数据注解,新增 _probe_meta.js 自检
继 3531437 修好 qqmusic 之后,把同一问题在另两个脚本上一并清掉 ——
它们原本都没写 name 注解(任务名会退回文件名),且 jd 脚本的 cron 写在块注释里
用了 */ 转义,青龙会把反斜杠一起提取出去。
改动(三个脚本统一成同一形态:文件最前面两行行注释):
// cron: <表达式>
// name: <中文任务名>
- jd/scrip_moving_env_enhance.js: 补 name 注解;cron 从块注释移到行注释
(原来写的 \*/ 转义会产出非法表达式)
- xiequ/scrip_switch.js: 补 name 注解;正文两处日志里的「name: xxx」改成「账号 xxx」
(青龙的任务名正则不锚定行首、取全文第一个命中,正文命中会盖掉注解)
- qqmusic/scrip_qqmusic_checkin.js: 注解从块注释改成行注释,与另两个统一
- 新增 _probe_meta.js: 扫描所有 scrip_*.js,复刻旧版(/name:\s*(.+)/ 不锚定)
与新版(cli/src/internal/subscription 的 cronMetadata)两条提取逻辑,
断言任务名不是代码片段、cron 是合法表达式。不带 scrip_ 前缀,订阅不会拉取
验证:_probe_meta.js RESULT: PASS(3 个文件,两个版本青龙提取都正确)/
三个脚本 node --check 全过 / qqmusic 签名对拍 6 组 PASS / 空 env 优雅跳过
|
2026-11-04 21:22:38 +08:00 |
|
|
|
3531437898
|
fix(qqmusic): 修正青龙自动建任务的元数据,任务名与 cron 不再被误提取
真机故障(2026-11-04):青龙订阅勾「自动添加定时任务」后,任务被建成
名称 = u.hostname, (应为 QQ音乐自动签到)
cron = 18 21 * * * (应为 1 0,6 * * *)
根因是青龙从脚本正文正则提取元数据,两条正则都不锚定行首:
1) 任务名 /name:\s*(.+)/ —— 全文第一个命中即采用。脚本里 https.request 的
选项对象写了 hostname: u.hostname,其中的 name 段被当成任务名。
2) cron /\bcron:\s*([^\r\n]+)/ —— 必须带冒号。原写法是注释里的
「定时: cron 1 0,6 * * *」,冒号在「定时」后面,cron 后面没有冒号 → 匹配不到,
落到随机 fallback(randomInt 生成 18 21 * * *)。
修法:
- 脚本第 3 行显式写 name 注解(独占一行,内容为「QQ音乐自动签到」)
- cron 注解改成「cron: 1 0,6 * * *」独占一行,注释挪到下一行
- https.request 选项 hostname -> host(Node 的别名,语义等价,实测 u6.y.qq.com
返回 404 而非连接失败),彻底消除全文唯一的 name+冒号连写
- README 补「青龙自动建任务的元数据约定」小节,记录两条正则与兜底行为
验证:node --check 通过 / 签名对拍 6 组 PASS / 空 env 优雅跳过 /
复刻青龙两条正则做提取断言,name=QQ音乐自动签到、cron=1 0,6 * * *;
新版青龙(cli/src/internal/subscription/subscriptionRunner.ts 的 cronMetadata)
同样提取正确
|
2026-11-04 21:13:34 +08:00 |
|
|
|
68721d1abb
|
refactor(qqmusic): 脚本改名 scrip_qqmusic_checkin.js 以匹配订阅白名单
订阅规则 ql repo 的 whitelist 参数是 "scrip_",只有文件名带该前缀的才会被拉取。
原文件名 qqmusic_checkin.js 不在白名单内,青龙订阅拉不到。
- qqmusic/qqmusic_checkin.js -> qqmusic/scrip_qqmusic_checkin.js(git mv 保留历史)
- test_sign_run.js 的 require 路径同步
- qqmusic/README.md 与根 README.md 更新文件名与白名单约定说明
- 根 README 补 QQ音乐条目(环境变量 SH_QQMUSIC_KEY / cron 1 0,6 * * *)
依赖拉取不受影响:订阅命令的 dependence 参数 sendNotify|common 会额外拉这两个文件,
脚本的 require('../sendNotify') 仍指向青龙内置/仓库根的 sendNotify.js。
验证:node --check 通过 / 签名对拍 6 组向量全 PASS / 空 env 优雅跳过 /
单账号与 & 多账号解析正确 / 整串 cookie 形态解析正确
|
2026-11-04 20:49:54 +08:00 |
|