#!/usr/bin/env node /** * QQ音乐签到通知诊断(青龙里跑:cd /ql/data/scripts/ql-scripts && node _diag_notify.js) * * 一次跑完查五件事: * 1. sendNotify 能不能加载 * 2. 通知 env 有没有读到 * 3. webhook 前置条件过不过 + body 会被解析成什么(sendNotify 的 parseString 有坑) * 4. 直连 pushplus 发两次(txt / html 模板对比),确定是不是模板导致正文被折叠 * 5. 用 sendNotify 走一遍完整流程,对比它和直连的差别 * * token 一律脱敏。只读配置 + 发测试消息,不改任何东西。 */ 'use strict' const https = require('https') const mask = (v, n = 6) => (!v ? '(未设)' : `${v.slice(0, n)}…(len=${v.length})`) const TITLE = 'QQ音乐签到' const DESP = [ '✅ QQ音乐签到 · 全部完成', '', '🟡 1006764347:成长值 +0', ' ✓ 节目开始标记 已挂', ' △ 领奖·主题装扮 code 30005', ' △ 领奖·收听节目 未计次,待 App 内收听', ' · 无需处理 12 项:任务中心签到、挂件、装扮、听书行为…', '', '———', '轮次: 00:01 / 06:02 双轮', '时间: ' + new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }), ].join('\n') function post(url, body) { return new Promise((resolve) => { const u = new URL(url) const payload = JSON.stringify(body) const req = https.request({ host: u.hostname, port: u.port || 443, path: u.pathname + u.search, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) }, timeout: 15000, }, (res) => { const cs = [] res.on('data', c => cs.push(c)) res.on('end', () => resolve({ status: res.statusCode, body: Buffer.concat(cs).toString('utf8') })) }) req.on('timeout', () => { req.destroy(); resolve({ status: 0, body: 'TIMEOUT' }) }) req.on('error', e => resolve({ status: 0, body: e.message })) req.write(payload) req.end() }) } console.log('══════════ 1. sendNotify 加载 ══════════') let sn = null try { sn = require('./sendNotify') console.log(' ✓ 加载成功') } catch (e) { console.log(' ✗ 加载失败:', e.message) console.log(' → 脚本里 try{require}catch{} 静默吞掉,一条通知都发不出') } console.log('\n══════════ 2. 通知 env ══════════') for (const k of ['PUSH_PLUS_TOKEN', 'PUSH_PLUS_USER', 'WEBHOOK_URL', 'WEBHOOK_BODY', 'WEBHOOK_METHOD', 'WEBHOOK_CONTENT_TYPE', 'WEBHOOK_HEADERS', 'SKIP_PUSH_TITLE']) { const v = process.env[k] const sensitive = /TOKEN|URL|BODY|HEADERS/i.test(k) console.log(` ${v ? '✓' : '·'} ${k.padEnd(20)} ${v ? (sensitive ? mask(v) : v) : '(未设)'}`) } console.log('\n══════════ 3. webhook 前置条件 ══════════') const WU = process.env.WEBHOOK_URL || '' const WB = process.env.WEBHOOK_BODY || '' const WM = process.env.WEBHOOK_METHOD || '' const WCT = process.env.WEBHOOK_CONTENT_TYPE || '' console.log(' URL 含 $title :', WU.includes('$title')) console.log(' BODY 含 $title:', WB.includes('$title')) console.log(' METHOD :', WM || '(空 → 不发请求)') console.log(' CONTENT_TYPE :', WCT || '(空)') if (WU.includes('$title') || WB.includes('$title')) { console.log('\n --- body 解析复刻(sendNotify 的 parseString)---') const regex = /(\w+):\s*((?:(?!\n\w+:).)*)/g const matches = {} let m while ((m = regex.exec(WB)) !== null) { if (!matches[m[1]]) matches[m[1]] = m[2].trim() } const keys = Object.keys(matches) console.log(' 解析出的 key:', keys.length ? keys.join(', ') : '(一个都没有)') if (keys.length === 0 && WB.trim()) { console.log(' ✗✗ BODY 非空但解析出 0 个 key —— 单行 JSON 会被整段吞掉(已实测)') console.log(' 这会让发出去的 body 畸形 → 对方返回 4xx → 「自定义发送通知消息失败」') } } console.log('\n══════════ 4. 直连 pushplus(模板对比)══════════') const PT = process.env.PUSH_PLUS_TOKEN || '' if (!PT) { console.log(' · PUSH_PLUS_TOKEN 未设,跳过') } else { ;(async () => { for (const tpl of ['txt', 'html']) { const content = tpl === 'html' ? DESP.replace(/\n/g, '
') : DESP const r = await post('https://www.pushplus.plus/send', { token: PT, title: `${TITLE}(${tpl} 测试)`, content, template: tpl, }) let j = {} try { j = JSON.parse(r.body) } catch { /* 非 JSON */ } console.log(` [${tpl}] HTTP ${r.status} code=${j.code} msg=${j.msg || ''}`) console.log(` content 长度=${content.length} 行数=${DESP.split('\n').length}`) } console.log('\n → 两条都发到你微信了,对比哪个正文显示完整') console.log(' → 若 txt 完整、html 只有一行 → pushplus 的 html 模板问题') console.log(' → 若两条都只有一行 → 微信渠道本身折叠,点「查看详情」才展开') console.log('\n══════════ 5. 走一遍 sendNotify ══════════') if (!sn) { console.log(' 跳过(未加载)'); return } console.log(' 调用 sendNotify("QQ音乐签到", desp)…\n') await sn.sendNotify(TITLE, DESP) console.log('\n══════════ 完成 ══════════') console.log('对照上面 4 的直连结果:') console.log(' · sendNotify 的 pushplus 也正常 → 两条都收到,问题在展示层') console.log(' · sendNotify 报错 → 看报错文本,多半是 webhook 那一路') })().catch(e => console.log(' 异常:', e.message)) }