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 展示限制的
三种官方解法与代价对照。
This commit is contained in:
@@ -8,7 +8,6 @@ QQ音乐会员成长值任务 + 会员福利站 自动签到,青龙面板 Node
|
||||
> 不带前缀的文件不会被订阅拉取(脚本、测试、文档同一约定)。
|
||||
|
||||
## 环境变量
|
||||
|
||||
| 变量 | 必填 | 说明 |
|
||||
|---|---|---|
|
||||
| `SH_QQMUSIC_KEY` | ✅ | 多账号:青龙同名 env 加多条(注入自动 `&` 连接)。**单条格式 `<uin>#<qm_keyst>`**,如 `123456789#Q_H_L_xxxx`。也接受整串 cookie(含 `qm_keyst=` 与 `uin=o...`) |
|
||||
@@ -17,6 +16,18 @@ QQ音乐会员成长值任务 + 会员福利站 自动签到,青龙面板 Node
|
||||
| `QM_PLAY_REPORT` | 可选 | `on` 才启用任务20播放上报(本版未实现模板,挂了开关占位) |
|
||||
| `QM_SHARE_CARD` | 可选 | `on` 才让**定时任务**自动建卡。默认 off = 只读报数。手动取卡走 `-- card`,不受此开关限制 |
|
||||
| `QM_CARD` | 可选 | `1`/`on` 等价于 `-- card`(不方便传参时用) |
|
||||
| `WEBHOOK_URL` | 可选 | 自定义 webhook(如平台 `/api/hooks/ql`)。与 `PUSH_PLUS_TOKEN` 至少配一个 |
|
||||
| `WEBHOOK_METHOD` | 可选 | 默认 `POST` |
|
||||
| `WEBHOOK_CONTENT_TYPE` | 可选 | 默认 `application/json` |
|
||||
| `WEBHOOK_BODY` | 可选 | 支持 `$title` / `$content` 占位;不填则按 Content-Type 给默认形状 |
|
||||
| `WEBHOOK_HEADERS` | 可选 | 一行一个 `Key: Value`,**必须是真换行** |
|
||||
| `PUSH_PLUS_TOKEN` | 可选 | pushplus 直发 |
|
||||
| `PUSH_PLUS_TOPIC` | 可选 | pushplus 一对多群组编码(对应 `PUSH_PLUS_USER`) |
|
||||
| `PUSH_PLUS_TEMPLATE` | 可选 | 默认 `txt`(不转义 html、不折叠);`html` 会把换行变 `<br>` |
|
||||
|
||||
> 通知由本脚本**自己实现**(`notify()`),不依赖青龙的 `sendNotify.js` ——
|
||||
> 那份文件每轮都会被 `/ql/data/deps/*` 覆盖,改它不生效。详见「通知发不出去怎么查 · 坑 0」。
|
||||
|
||||
|
||||
## VIP 体验券(分享卡)
|
||||
|
||||
@@ -181,55 +192,59 @@ cron: 1 0,6 * * *
|
||||
|
||||
## 通知发不出去怎么查
|
||||
|
||||
### 坑 0(最狠的一个):仓库的 `sendNotify.js` 每轮都被覆盖
|
||||
|
||||
**症状**:改了 `sendNotify.js`,青龙跑起来行为不变;日志里永远是旧版那句话。
|
||||
|
||||
**原因**:青龙 `shell/update.sh` 的 `gen_list_repo` 里,文件复制是**三步**,后一步覆盖前一步:
|
||||
|
||||
```bash
|
||||
# 青龙里手动跑一次(cron 设成 1 月 1 日,不会自动跑)
|
||||
node scrip_diag_notify.js
|
||||
407: cp -f $file_notify_js "${dir_scripts}/${uniq_path}" # ① 面板自带的 sendNotify.js
|
||||
408: cp -f $file_notify_py "${dir_scripts}/${uniq_path}"
|
||||
|
||||
410: if [[ $dependence ]]; then
|
||||
411: results=$(find . -name "*.js" | egrep "$dependence")
|
||||
415: cp -f $_file "${dir_scripts}/${uniq_path}/${file_path}" # ② 仓库的 sendNotify.js(覆盖①)
|
||||
417: fi
|
||||
|
||||
420: if [[ -d $dir_dep ]]; then
|
||||
421: cp -rf $dir_dep/* "${dir_scripts}/${uniq_path}" &>/dev/null # ③ /ql/data/deps/*(再覆盖②)
|
||||
422: fi
|
||||
```
|
||||
|
||||
六段逐项定位,token 全程脱敏:
|
||||
`$dir_dep` 是 `/ql/data/deps`,青龙初始化时往里面拷了一份面板自带的 `notify.js`:
|
||||
|
||||
```
|
||||
1. sendNotify 加载 ← 容器缺 got 时 require 抛错,被静默吞掉
|
||||
2. 通知 env ← 青龙「环境变量」是否真的保存了
|
||||
3. webhook 配置分析 ← 三个前置条件 + body/headers 解析复刻
|
||||
4. 直打平台 /api/hooks/ql ← 按状态码定位卡在哪一道校验
|
||||
5. 直连 pushplus 模板对比 ← txt vs html,确认是不是展示折叠
|
||||
6. 完整 sendNotify ← 与第 4 节对照,区分通道问题与展示问题
|
||||
```bash
|
||||
# shell/share.sh:228
|
||||
if [[ ! -s $dep_notify_js ]]; then
|
||||
cp -f "$file_notify_js_sample" "$dep_notify_js" # sample/notify.js → /ql/data/deps/sendNotify.js
|
||||
fi
|
||||
```
|
||||
|
||||
### 坑 0:平台回 204,原版 sendNotify 判成失败(已修)
|
||||
**③ 排在最后,所以最终文件永远是面板自带的那份。** 仓库里的 `sendNotify.js` 白改。
|
||||
|
||||
`/api/hooks/ql` 按设计返回 **204**(表示已接收,且不写 event_logs),
|
||||
而原版 `sendNotify.js` 的判据是 `resp.statusCode !== 200`:
|
||||
**验证**:
|
||||
|
||||
```js
|
||||
if (resp.statusCode !== 200) {
|
||||
console.log(`自定义发送通知消息失败!!\n${resp.body}`); // 204 也走这里
|
||||
}
|
||||
```bash
|
||||
docker exec -it qinglong sh -c '
|
||||
md5sum /ql/data/deps/sendNotify.js /ql/data/scripts/zhuhjay_ql-scripts_master/sendNotify.js
|
||||
'
|
||||
```
|
||||
|
||||
**所以「自定义发送通知消息失败!!」是假警报 —— 平台其实收到了。**
|
||||
已改成 2xx 一律算成功,并把状态码打进日志:
|
||||
两个 md5 相同 = 被覆盖了。
|
||||
|
||||
```js
|
||||
if (resp.statusCode < 200 || resp.statusCode >= 300) {
|
||||
console.log(`自定义发送通知消息失败!!HTTP ${resp.statusCode}\n${resp.body}`);
|
||||
} else {
|
||||
console.log(`自定义发送通知消息成功🎉。HTTP ${resp.statusCode}\n${resp.body || ''}`);
|
||||
}
|
||||
```
|
||||
**结论:本脚本不再 `require('../sendNotify')`。** 通知逻辑内联在
|
||||
`scrip_qqmusic_checkin.js` 的 `notify()` 里(约 90 行,零依赖),行为完全由仓库决定。
|
||||
|
||||
### 坑 1:`require('../sendNotify')` 静默失败
|
||||
### 坑 1:`sendNotify` 的三个静默 `resolve()`
|
||||
|
||||
原本 `catch { /* 仅控制台 */ }`,缺 `got` 时**一条通知都发不出去且日志无痕**。现在会打一行:
|
||||
如果哪天又接回 `sendNotify`,注意 `webhookNotify` 开头逐行 return,**不发请求也不报错**:
|
||||
|
||||
```
|
||||
[通知] sendNotify 不可用(Cannot find module 'got'),本轮不发推送
|
||||
```
|
||||
1. `WEBHOOK_URL` 或 `WEBHOOK_BODY` **至少一个**含 `$title`
|
||||
2. `WEBHOOK_METHOD` **必须显式设**(空则走 else 分支 resolve)
|
||||
3. body 能被 `parseString` 正确解析(见坑 2)
|
||||
|
||||
修:`docker exec -it qinglong pnpm install got`
|
||||
|
||||
### 坑 2:WEBHOOK_BODY 写成单行 JSON 会被解析成空
|
||||
### 坑 2:`WEBHOOK_BODY` 写成单行 JSON 会被解析成空
|
||||
|
||||
`sendNotify` 的 `parseString` 用 `/(\w+):\s*((?:(?!\n\w+:).)*)/g`,**靠换行 + `\w+:` 断值**。
|
||||
单行 JSON 没有换行,整个 body 被吞掉,实测解析结果 `{}`(0 个 key):
|
||||
@@ -241,21 +256,32 @@ if (resp.statusCode < 200 || resp.statusCode >= 300) {
|
||||
|
||||
body 变成 `{}` → 平台 `JSON.parse` 得到空对象 → 400「title 与 content 不能同时为空」。
|
||||
|
||||
### 坑 3:webhook 三个前置条件,缺一个就静默跳过
|
||||
**本脚本的 `buildHookBody` 不受这个坑影响** —— 它对 `application/json` 会先试
|
||||
`JSON.parse`,不合法就直接 `JSON.stringify({title, content})` 重建,单行多行都行。
|
||||
|
||||
`webhookNotify` 开头逐行 return,**不发请求也不报错**:
|
||||
### 坑 3:pushplus 通知栏只显示标题 + 首行
|
||||
|
||||
1. `WEBHOOK_URL` 或 `WEBHOOK_BODY` **至少一个**含 `$title`
|
||||
2. `WEBHOOK_METHOD` **必须显式设**(空则走 else 分支 resolve)
|
||||
3. body 能被 `parseString` 正确解析(见坑 2)
|
||||
微信服务号模板消息**默认不展示具体内容**,点「查看详情」才展开全文。
|
||||
pushplus 官方给了三种解法([文档](https://pushplus.plus/doc/help/showmessage.html)):
|
||||
|
||||
### 坑 4:pushplus 只显示一行 ≠ 内容丢了
|
||||
| 方案 | 代价 |
|
||||
|---|---|
|
||||
| 在服务号发「激活消息」 | 24 小时内 5 条走客服消息,能显示全文 |
|
||||
| 开通 pushplus 会员 | 付费,模板能多显示一部分 |
|
||||
| 绑定自己的服务号 | 企业/个体工商户认证,每年 300 元 |
|
||||
|
||||
微信通知卡片默认只展示开头,点开才展开全文。要确认是折叠还是真丢:
|
||||
第 5 节会同时发 `txt` 和 `html` 两条,对比哪个正文完整。
|
||||
**不是 bug,是通道限制。** 要通知栏直接看到明细,换 Bark / Telegram / 企业微信机器人。
|
||||
|
||||
- txt 完整 / html 一行 → 把 pushplus 目标改成 `template=txt`
|
||||
- 两条都一行 → 微信卡片折叠,点开即全文,不是 bug
|
||||
### 自检
|
||||
|
||||
```bash
|
||||
# 纯函数 + 六种状态码(200/204/201/401/403/400/429)+ 无配置 + 双通道
|
||||
node qqmusic/test_notify.js
|
||||
```
|
||||
|
||||
```
|
||||
RESULT: PASS (38 passed, 0 failed)
|
||||
```
|
||||
|
||||
### 指向平台的完整配置
|
||||
|
||||
@@ -294,4 +320,16 @@ x-sh-hook-token: <平台 QL_HOOK_TOKEN>
|
||||
node --check scrip_qqmusic_checkin.js # 语法
|
||||
node scrip_qqmusic_checkin.js # 无 env 时优雅跳过
|
||||
SH_QQMUSIC_KEY='123#Q_H_L_x' node -e "console.log(require('./scrip_qqmusic_checkin').parseAccounts())"
|
||||
|
||||
node test_notify.js # 通知实现:38 条断言(含六种状态码)
|
||||
node test_sign_run.js # 签名对拍(需先 python test_sign_vector.py 生成期望)
|
||||
node ../_probe_meta.js # 青龙元数据注解
|
||||
```
|
||||
|
||||
`test_notify.js` 覆盖:
|
||||
|
||||
- `parseHookHeaders`:多行 / 空行 / 无冒号 / 值含冒号 / 字面量 `\n` 不切开
|
||||
- `buildHookBody`:内容含双引号、反斜杠、制表符时仍产出合法 JSON
|
||||
- `notify`:mock 服务测 **200 / 204 / 201 / 401 / 403 / 400 / 429** 七种状态码
|
||||
- 无配置、双通道部分失败
|
||||
|
||||
|
||||
Reference in New Issue
Block a user