From ef17909032d4adf92552cb2d706f2ad8b1c63ad0 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Sun, 17 May 2026 16:24:00 +0800 Subject: [PATCH] fix: recognize qdreader uid from response body --- docs/qx-rewrite-lessons.md | 3 +- js/qdreader_cookie.js | 25 ++++++++++++++-- tests/qdreader_cookie.test.mjs | 52 ++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 tests/qdreader_cookie.test.mjs diff --git a/docs/qx-rewrite-lessons.md b/docs/qx-rewrite-lessons.md index 6ea6813..57c4ca0 100644 --- a/docs/qx-rewrite-lessons.md +++ b/docs/qx-rewrite-lessons.md @@ -55,7 +55,8 @@ Autman 插件保留快捷命令: ## 验证清单 - `node --check js/qdreader_cookie.js` -- raw 地址返回 HTTP 200: +- `node tests/qdreader_cookie.test.mjs` +- raw 地址返回 HTTP 200: - `conf/qdreader.conf` - `js/qdreader_cookie.js` - 订阅文本内容第一行是 `hostname = ...`,第二行是 rewrite rule。 diff --git a/js/qdreader_cookie.js b/js/qdreader_cookie.js index 437efe7..eca3ba1 100644 --- a/js/qdreader_cookie.js +++ b/js/qdreader_cookie.js @@ -25,21 +25,40 @@ function parseCookie(cookie) { return jar } function b64decode(s) { - try { return decodeURIComponent(escape(atob(String(s || '')))) } catch (e) { return '' } + let raw = String(s || '').trim() + try { raw = decodeURIComponent(raw) } catch (e) {} + raw = raw.replace(/-/g, '+').replace(/_/g, '/') + while (raw.length % 4) raw += '=' + try { return decodeURIComponent(escape(atob(raw))) } catch (e) { return '' } } function uidFromQDHeader(qdheader) { const decoded = b64decode(qdheader) return decoded ? String(decoded.split('|')[0] || '').trim() : '' } function parseJson(s, def) { try { return JSON.parse(s) } catch (e) { return def } } +function findUid(obj) { + if (!obj || typeof obj !== 'object') return '' + const keys = ['UserId', 'userId', 'UserID', 'userid', 'uid', 'UID'] + for (const key of keys) { + const val = obj[key] + if (val !== undefined && val !== null && String(val).trim()) return String(val).trim() + } + for (const key in obj) { + const val = obj[key] + if (val && typeof val === 'object') { + const found = findUid(val) + if (found) return found + } + } + return '' +} function getUid(cookie, body) { const jar = parseCookie(cookie) let uid = uidFromQDHeader(jar.QDHeader || jar.qdheader || jar.QDHEADER || '') if (uid) return uid if (jar.uid) return String(jar.uid) const obj = parseJson(body || '', null) - uid = obj && obj.Data && obj.Data.UserInfo && (obj.Data.UserInfo.UserId || obj.Data.UserInfo.userId) - return uid ? String(uid) : '' + return findUid(obj) } function notify(title, sub, msg, opts) { try { $notify(title, sub || '', msg || '', opts || {}) } catch (e) { $notify(title, sub || '', msg || '') } diff --git a/tests/qdreader_cookie.test.mjs b/tests/qdreader_cookie.test.mjs new file mode 100644 index 0000000..61ca5e4 --- /dev/null +++ b/tests/qdreader_cookie.test.mjs @@ -0,0 +1,52 @@ +import assert from 'node:assert/strict'; +import fs from 'node:fs'; +import vm from 'node:vm'; + +const code = fs.readFileSync(new URL('../js/qdreader_cookie.js', import.meta.url), 'utf8'); + +function run({ cookie = '', body = '' }) { + const notices = []; + const context = { + $request: { headers: { Cookie: cookie } }, + $response: { body }, + $notify: (...args) => notices.push(args), + $done: () => {}, + atob: (s) => Buffer.from(s, 'base64').toString('binary'), + encodeURIComponent, + decodeURIComponent, + escape, + }; + vm.runInNewContext(code, context); + return notices; +} + +{ + const qdheader = Buffer.from('123456|device|more').toString('base64').replace(/=+$/, ''); + const notices = run({ cookie: `QDHeader=${encodeURIComponent(qdheader)}; QDH=abc` }); + assert.equal(notices[0][1], '抓取成功 uid:123456'); + assert.match(notices[0][2], /^启点ck /); +} + +{ + const notices = run({ + cookie: 'cmfuToken=abc; QDH=def', + body: JSON.stringify({ Data: { UserId: 987654 } }), + }); + assert.equal(notices[0][1], '抓取成功 uid:987654'); +} + +{ + const notices = run({ + cookie: 'cmfuToken=abc; QDH=def', + body: JSON.stringify({ Data: { UserInfo: { UserId: 2468 } } }), + }); + assert.equal(notices[0][1], '抓取成功 uid:2468'); +} + +{ + const notices = run({ cookie: 'cmfuToken=abc; QDH=def', body: '{}' }); + assert.equal(notices[0][1], 'Cookie 获取失败'); + assert.match(notices[0][2], /无法识别 uid/); +} + +console.log('ok - qdreader qx uid parsing');