fix(notify): 平台回 204 被误判为失败 + 诊断工具改名 scrip_
三个改动: 1) sendNotify.js 的 webhook 判据从 statusCode !== 200 改成 2xx 一律成功。 平台 /api/hooks/ql 按设计回 204(表示已接收且不写 event_logs), 原判据把 204 当失败 → 每轮日志都打「自定义发送通知消息失败!!」, 而投递其实成功了。这是纯假警报,也是这次误判的根因。 同时把状态码打进日志,真失败时能直接看出是 401/403/400/429。 2) _diag_notify.js → scrip_diag_notify.js。 订阅白名单是 scrip_,不带前缀的文件拉不下来,工具等于不存在。 加 // cron: 0 0 1 1 * 与 // name:,避免青龙自动建任务时落随机 cron。 正文补到六段:sendNotify 加载 / env / webhook 三前置条件与 body+headers 解析复刻 / 直打平台按状态码定位 / pushplus txt-vs-html 模板对比 / 完整 sendNotify 对照。 3) 文档补齐平台侧的两道硬校验:来源 IP 必须 127.0.0.1 或 172.16/12 (走公网域名会变成反代地址 → 403),回环带 XFF 一律 403。 给出指向平台的完整 env 写法(WEBHOOK_BODY 必须多行,单行 JSON 会被 parseString 整段吞掉 —— 已实测解析结果为 0 个 key)。 验证:node --check(sendNotify + 诊断)/ 5 个 mock 状态码用例 (204/401/403/400/429)全部被正确识别 / _probe_meta 四文件 PASS
This commit is contained in:
@@ -181,30 +181,57 @@ cron: 1 0,6 * * *
|
||||
|
||||
## 通知发不出去怎么查
|
||||
|
||||
`node _diag_notify.js`(放在仓库根,跟 `sendNotify.js` 同级)一次跑完查五件事:
|
||||
```bash
|
||||
# 青龙里手动跑一次(cron 设成 1 月 1 日,不会自动跑)
|
||||
node scrip_diag_notify.js
|
||||
```
|
||||
|
||||
六段逐项定位,token 全程脱敏:
|
||||
|
||||
```
|
||||
1. sendNotify 能不能加载 ← 最常见:容器里缺 got,require 抛错被静默吞掉
|
||||
2. 通知 env 有没有读到 ← 青龙「环境变量」是否真的保存了
|
||||
3. webhook 前置条件 + body 解析 ← 见下方两个坑
|
||||
4. 直连 pushplus 发 txt/html 两条 ← 对比哪个模板正文完整
|
||||
5. 走一遍 sendNotify ← 与 4 对比,定位是通道问题还是展示问题
|
||||
1. sendNotify 加载 ← 容器缺 got 时 require 抛错,被静默吞掉
|
||||
2. 通知 env ← 青龙「环境变量」是否真的保存了
|
||||
3. webhook 配置分析 ← 三个前置条件 + body/headers 解析复刻
|
||||
4. 直打平台 /api/hooks/ql ← 按状态码定位卡在哪一道校验
|
||||
5. 直连 pushplus 模板对比 ← txt vs html,确认是不是展示折叠
|
||||
6. 完整 sendNotify ← 与第 4 节对照,区分通道问题与展示问题
|
||||
```
|
||||
|
||||
### 坑 0:平台回 204,原版 sendNotify 判成失败(已修)
|
||||
|
||||
`/api/hooks/ql` 按设计返回 **204**(表示已接收,且不写 event_logs),
|
||||
而原版 `sendNotify.js` 的判据是 `resp.statusCode !== 200`:
|
||||
|
||||
```js
|
||||
if (resp.statusCode !== 200) {
|
||||
console.log(`自定义发送通知消息失败!!\n${resp.body}`); // 204 也走这里
|
||||
}
|
||||
```
|
||||
|
||||
**所以「自定义发送通知消息失败!!」是假警报 —— 平台其实收到了。**
|
||||
已改成 2xx 一律算成功,并把状态码打进日志:
|
||||
|
||||
```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 || ''}`);
|
||||
}
|
||||
```
|
||||
|
||||
### 坑 1:`require('../sendNotify')` 静默失败
|
||||
|
||||
脚本里原本是 `catch { /* 仅控制台 */ }`,缺 `got` 时**一条通知都发不出去且日志无痕**。
|
||||
现在会打一行:
|
||||
原本 `catch { /* 仅控制台 */ }`,缺 `got` 时**一条通知都发不出去且日志无痕**。现在会打一行:
|
||||
|
||||
```
|
||||
[通知] sendNotify 不可用(Cannot find module 'got'),本轮不发推送
|
||||
```
|
||||
|
||||
修:`docker exec -it qinglong pnpm install got`(sendNotify 只依赖 got + 内置模块)。
|
||||
修:`docker exec -it qinglong pnpm install got`
|
||||
|
||||
### 坑 2:WEBHOOK_BODY 写成单行 JSON 会被解析成空
|
||||
|
||||
`sendNotify` 的 `parseString` 用 `/(\w+):\s*((?:(?!\n\w+:).)*)/g` 解析,**靠换行 + `\w+:` 断值**。
|
||||
`sendNotify` 的 `parseString` 用 `/(\w+):\s*((?:(?!\n\w+:).)*)/g`,**靠换行 + `\w+:` 断值**。
|
||||
单行 JSON 没有换行,整个 body 被吞掉,实测解析结果 `{}`(0 个 key):
|
||||
|
||||
```js
|
||||
@@ -212,19 +239,41 @@ cron: 1 0,6 * * *
|
||||
'title:$title\ncontent:$content' → 两个 key ✓
|
||||
```
|
||||
|
||||
body 畸形 → 对方返回非 200 → 日志出现 `自定义发送通知消息失败!!`。
|
||||
body 变成 `{}` → 平台 `JSON.parse` 得到空对象 → 400「title 与 content 不能同时为空」。
|
||||
|
||||
**三个前置条件缺一不可**(`webhookNotify` 逐行 return):
|
||||
### 坑 3:webhook 三个前置条件,缺一个就静默跳过
|
||||
|
||||
`webhookNotify` 开头逐行 return,**不发请求也不报错**:
|
||||
|
||||
1. `WEBHOOK_URL` 或 `WEBHOOK_BODY` **至少一个**含 `$title`
|
||||
2. `WEBHOOK_METHOD` **必须显式设**(空则走 else 分支 resolve,不发请求也不报错)
|
||||
3. body 能被 `parseString` 正确解析(多行,或干脆用 URL 传参)
|
||||
2. `WEBHOOK_METHOD` **必须显式设**(空则走 else 分支 resolve)
|
||||
3. body 能被 `parseString` 正确解析(见坑 2)
|
||||
|
||||
### 坑 3:pushplus 的 html 模板可能只显示标题
|
||||
### 坑 4:pushplus 只显示一行 ≠ 内容丢了
|
||||
|
||||
`sendNotify` 的 pushplus 通道固定发 `template` 缺省值(html),并把 `\n` 换成 `<br>`。
|
||||
微信侧只显示一行是**展示折叠**,点开消息卡片才看全文。
|
||||
要确认是折叠还是真丢内容:`_diag_notify.js` 第 4 节会同时发 txt 和 html 两条,对比即可。
|
||||
微信通知卡片默认只展示开头,点开才展开全文。要确认是折叠还是真丢:
|
||||
第 5 节会同时发 `txt` 和 `html` 两条,对比哪个正文完整。
|
||||
|
||||
- txt 完整 / html 一行 → 把 pushplus 目标改成 `template=txt`
|
||||
- 两条都一行 → 微信卡片折叠,点开即全文,不是 bug
|
||||
|
||||
### 指向平台的完整配置
|
||||
|
||||
```bash
|
||||
WEBHOOK_URL=https://<平台域名>/api/hooks/ql
|
||||
WEBHOOK_METHOD=POST
|
||||
WEBHOOK_CONTENT_TYPE=application/json
|
||||
WEBHOOK_BODY=title:$title
|
||||
content:$content
|
||||
WEBHOOK_HEADERS=Content-Type: application/json
|
||||
x-sh-hook-token: <平台 QL_HOOK_TOKEN>
|
||||
```
|
||||
|
||||
平台侧还有两道硬校验(`hookGuard`):
|
||||
|
||||
- **来源 IP 必须是 127.0.0.1 或 172.16/12**(docker 内网)。青龙与平台不同容器时,
|
||||
走公网域名会让源 IP 变成反代地址 → 403。要用**容器内网地址**(如 `http://script-helper:3000`)。
|
||||
- 回环地址带 `X-Forwarded-For` 一律 403(说明穿过了反代)。
|
||||
|
||||
## 任务覆盖
|
||||
|
||||
|
||||
Reference in New Issue
Block a user