From d2a7e49ec5d99e397d20d24aa029aaaa940de5fc Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Fri, 22 May 2026 00:24:49 +0800 Subject: [PATCH] fix(qdreader): align adv completion with upstream --- js/qdreader_sign_autman.js | 31 +++++++++-- plugins/qdreader/qdreader_sign_autman.js | 31 +++++++++-- tests/qdreader_plugin.test.mjs | 68 ++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 10 deletions(-) diff --git a/js/qdreader_sign_autman.js b/js/qdreader_sign_autman.js index 86d5289..409c77f 100644 --- a/js/qdreader_sign_autman.js +++ b/js/qdreader_sign_autman.js @@ -4,7 +4,7 @@ //[author: Hermes] //[service: BOSS] //[class: 工具类] -//[version: 2.10.5] +//[version: 2.10.6] //[platform: web,qq,wx,tg,tb,fs,we] //[public: false] //[price: 0] @@ -276,7 +276,7 @@ function contentText() { } function usage() { return [ - "启点读书签到插件 v2.10.5", + "启点读书签到插件 v2.10.6", "【一级菜单】", "账号管理:启点账号", "执行查询:启点查询", @@ -603,8 +603,18 @@ function taskTitle(task) { return String(field(task, ["Title", "TaskName", "Name function taskIdOf(task) { return field(task, ["TaskId", "TaskID", "taskId", "TaskIDStr", "Id", "id"], "") } function taskFinished(task) { return Number(field(task, ["IsFinished", "Finished", "Status", "status"], 0)) === 1 } function taskIdKey(id) { return String(id === undefined || id === null ? "" : id) } +function taskRemainingCount(task) { + var cur = Number(field(task, ["Count", "count", "FinishedCount", "finishedCount", "FinishCount", "finishCount", "CurrentCount", "currentCount", "CompleteCount", "completeCount", "DoneCount", "doneCount", "Num", "num"], NaN)) + var total = Number(field(task, ["NeedCount", "needCount", "TargetCount", "targetCount", "TotalCount", "totalCount", "MaxCount", "maxCount", "Times", "times", "Limit", "limit"], NaN)) + if (!isNaN(total) && total > 0) { + if (!isNaN(cur) && cur >= 0 && cur < total) return Math.min(10, Math.max(1, Math.ceil(total - cur))) + return Math.min(10, Math.max(1, Math.ceil(total))) + } + if (!isNaN(cur) && cur > 1) return Math.min(10, Math.ceil(cur)) + return 1 +} function taskRunCount(task, def) { - var n = Number(field(task, ["NeedCount", "needCount", "Count", "count", "TotalCount", "totalCount", "Times", "times", "Limit", "limit"], def || 1)) + var n = taskRemainingCount(task) if (!n || isNaN(n) || n < 1) n = def || 1 if (n > 10) n = 10 return n @@ -645,12 +655,22 @@ function doReceiveGifts(cookie) { var ok = taskResultOk(res) return { name: "领取奖励", ok: !!ok, msg: resultMsg(res) || (ok ? "领取成功" : "领取失败"), path: "/argus/api/v3/checkin/receivegifts" } } +function advCompletionLooksUpstreamComplete(tasks, submittedIds) { + if (!tasks || !tasks.length) return false + if (tasks[tasks.length - 1] && taskFinished(tasks[tasks.length - 1])) return true + for (var i = 0; i < tasks.length; i++) { + var t = tasks[i] + var id = taskIdKey(taskIdOf(t)) + if (id && submittedIds && submittedIds[id] && taskFinished(t)) return true + } + return false +} function doAdvTask(cookie) { var main = qdGet("/argus/api/v2/video/adv/mainPage", {}, cookie) if (!taskResultOk(main)) return { name: "激励任务", ok: false, msg: resultMsg(main) || "获取任务失败", path: "/argus/api/v2/video/adv/mainPage" } var tasks = firstArray(nested(main, ["Data", "VideoRewardTab", "TaskList"]), nested(main, ["data", "VideoRewardTab", "TaskList"]), nested(main, ["Data", "TaskList"]), nested(main, ["data", "TaskList"])) if (!tasks.length) return { name: "激励任务", ok: true, msg: "无待完成任务", path: "/argus/api/v2/video/adv/mainPage" } - if (taskFinished(tasks[tasks.length - 1])) return { name: "激励任务", ok: true, msg: "已完成", path: "/argus/api/v2/video/adv/mainPage" } + if (advCompletionLooksUpstreamComplete(tasks, null)) return { name: "激励任务", ok: true, msg: "已完成", path: "/argus/api/v2/video/adv/mainPage" } var submittedIds = {} var submittedCount = 0 var skippedCount = 0 @@ -668,6 +688,7 @@ function doAdvTask(cookie) { var after = firstArray(nested(verify, ["Data", "VideoRewardTab", "TaskList"]), nested(verify, ["data", "VideoRewardTab", "TaskList"]), nested(verify, ["Data", "TaskList"]), nested(verify, ["data", "TaskList"])) var unfinishedSubmitted = 0 var actionableLeft = 0 + var upstreamDone = advCompletionLooksUpstreamComplete(after, submittedIds) for (var v = 0; v < after.length; v++) { var at = after[v] var aid = taskIdKey(taskIdOf(at)) @@ -679,7 +700,7 @@ function doAdvTask(cookie) { if (!taskFinished(at) && advTaskActionable(at)) actionableLeft++ } var submittedKeys = Object.keys(submittedIds) - var finished = submittedKeys.length ? unfinishedSubmitted === 0 : actionableLeft === 0 + var finished = upstreamDone || (submittedKeys.length ? unfinishedSubmitted === 0 : actionableLeft === 0) var suffix = skippedCount ? (",跳过 " + skippedCount + " 个需其它入口任务") : "" var left = submittedKeys.length ? unfinishedSubmitted : actionableLeft return { name: "激励任务", ok: finished, msg: finished ? (submittedKeys.length ? ("已完成" + suffix) : ("无待完成任务" + suffix)) : (submittedCount ? ("已提交 " + submittedCount + " 个视频任务,仍有 " + left + " 个未完成" + suffix) : ("仍有 " + left + " 个未完成" + suffix)), path: "/argus/api/v2/video/adv/mainPage" } diff --git a/plugins/qdreader/qdreader_sign_autman.js b/plugins/qdreader/qdreader_sign_autman.js index 86d5289..409c77f 100644 --- a/plugins/qdreader/qdreader_sign_autman.js +++ b/plugins/qdreader/qdreader_sign_autman.js @@ -4,7 +4,7 @@ //[author: Hermes] //[service: BOSS] //[class: 工具类] -//[version: 2.10.5] +//[version: 2.10.6] //[platform: web,qq,wx,tg,tb,fs,we] //[public: false] //[price: 0] @@ -276,7 +276,7 @@ function contentText() { } function usage() { return [ - "启点读书签到插件 v2.10.5", + "启点读书签到插件 v2.10.6", "【一级菜单】", "账号管理:启点账号", "执行查询:启点查询", @@ -603,8 +603,18 @@ function taskTitle(task) { return String(field(task, ["Title", "TaskName", "Name function taskIdOf(task) { return field(task, ["TaskId", "TaskID", "taskId", "TaskIDStr", "Id", "id"], "") } function taskFinished(task) { return Number(field(task, ["IsFinished", "Finished", "Status", "status"], 0)) === 1 } function taskIdKey(id) { return String(id === undefined || id === null ? "" : id) } +function taskRemainingCount(task) { + var cur = Number(field(task, ["Count", "count", "FinishedCount", "finishedCount", "FinishCount", "finishCount", "CurrentCount", "currentCount", "CompleteCount", "completeCount", "DoneCount", "doneCount", "Num", "num"], NaN)) + var total = Number(field(task, ["NeedCount", "needCount", "TargetCount", "targetCount", "TotalCount", "totalCount", "MaxCount", "maxCount", "Times", "times", "Limit", "limit"], NaN)) + if (!isNaN(total) && total > 0) { + if (!isNaN(cur) && cur >= 0 && cur < total) return Math.min(10, Math.max(1, Math.ceil(total - cur))) + return Math.min(10, Math.max(1, Math.ceil(total))) + } + if (!isNaN(cur) && cur > 1) return Math.min(10, Math.ceil(cur)) + return 1 +} function taskRunCount(task, def) { - var n = Number(field(task, ["NeedCount", "needCount", "Count", "count", "TotalCount", "totalCount", "Times", "times", "Limit", "limit"], def || 1)) + var n = taskRemainingCount(task) if (!n || isNaN(n) || n < 1) n = def || 1 if (n > 10) n = 10 return n @@ -645,12 +655,22 @@ function doReceiveGifts(cookie) { var ok = taskResultOk(res) return { name: "领取奖励", ok: !!ok, msg: resultMsg(res) || (ok ? "领取成功" : "领取失败"), path: "/argus/api/v3/checkin/receivegifts" } } +function advCompletionLooksUpstreamComplete(tasks, submittedIds) { + if (!tasks || !tasks.length) return false + if (tasks[tasks.length - 1] && taskFinished(tasks[tasks.length - 1])) return true + for (var i = 0; i < tasks.length; i++) { + var t = tasks[i] + var id = taskIdKey(taskIdOf(t)) + if (id && submittedIds && submittedIds[id] && taskFinished(t)) return true + } + return false +} function doAdvTask(cookie) { var main = qdGet("/argus/api/v2/video/adv/mainPage", {}, cookie) if (!taskResultOk(main)) return { name: "激励任务", ok: false, msg: resultMsg(main) || "获取任务失败", path: "/argus/api/v2/video/adv/mainPage" } var tasks = firstArray(nested(main, ["Data", "VideoRewardTab", "TaskList"]), nested(main, ["data", "VideoRewardTab", "TaskList"]), nested(main, ["Data", "TaskList"]), nested(main, ["data", "TaskList"])) if (!tasks.length) return { name: "激励任务", ok: true, msg: "无待完成任务", path: "/argus/api/v2/video/adv/mainPage" } - if (taskFinished(tasks[tasks.length - 1])) return { name: "激励任务", ok: true, msg: "已完成", path: "/argus/api/v2/video/adv/mainPage" } + if (advCompletionLooksUpstreamComplete(tasks, null)) return { name: "激励任务", ok: true, msg: "已完成", path: "/argus/api/v2/video/adv/mainPage" } var submittedIds = {} var submittedCount = 0 var skippedCount = 0 @@ -668,6 +688,7 @@ function doAdvTask(cookie) { var after = firstArray(nested(verify, ["Data", "VideoRewardTab", "TaskList"]), nested(verify, ["data", "VideoRewardTab", "TaskList"]), nested(verify, ["Data", "TaskList"]), nested(verify, ["data", "TaskList"])) var unfinishedSubmitted = 0 var actionableLeft = 0 + var upstreamDone = advCompletionLooksUpstreamComplete(after, submittedIds) for (var v = 0; v < after.length; v++) { var at = after[v] var aid = taskIdKey(taskIdOf(at)) @@ -679,7 +700,7 @@ function doAdvTask(cookie) { if (!taskFinished(at) && advTaskActionable(at)) actionableLeft++ } var submittedKeys = Object.keys(submittedIds) - var finished = submittedKeys.length ? unfinishedSubmitted === 0 : actionableLeft === 0 + var finished = upstreamDone || (submittedKeys.length ? unfinishedSubmitted === 0 : actionableLeft === 0) var suffix = skippedCount ? (",跳过 " + skippedCount + " 个需其它入口任务") : "" var left = submittedKeys.length ? unfinishedSubmitted : actionableLeft return { name: "激励任务", ok: finished, msg: finished ? (submittedKeys.length ? ("已完成" + suffix) : ("无待完成任务" + suffix)) : (submittedCount ? ("已提交 " + submittedCount + " 个视频任务,仍有 " + left + " 个未完成" + suffix) : ("仍有 " + left + " 个未完成" + suffix)), path: "/argus/api/v2/video/adv/mainPage" } diff --git a/tests/qdreader_plugin.test.mjs b/tests/qdreader_plugin.test.mjs index 73673da..036b77b 100644 --- a/tests/qdreader_plugin.test.mjs +++ b/tests/qdreader_plugin.test.mjs @@ -998,6 +998,74 @@ test('adv task repeats same upstream fragment task until requested count before assert.ok(finishCalls.every(c => /1187767793786748928/.test(c.body))); }); +test('adv task follows upstream completion when final list item is finished after callbacks', () => { + const store = { + qdreader_cookie_json: JSON.stringify({ '12345': cookie }), + qdreader_user_bind_json: JSON.stringify({ '12345': 'user-a' }), + qdreader_task_pref_json: JSON.stringify({ '12345': { adv: true } }), + }; + const r = runPlugin({ + content: '启点签到', + store, + userId: 'user-a', + requests: [ + { body: { code: 0, data: { headers: { 'QD-Sign': 'sig' } } } }, + { statusCode: 200, body: { code: 1, msg: '今日已签到' } }, + { body: { code: 0, data: { headers: { 'QD-Sign': 'sig' } } } }, + { statusCode: 200, body: { code: 1, msg: '无可领取礼包' } }, + { body: { code: 0, data: { headers: { 'QD-Sign': 'sig' } } } }, + { statusCode: 200, body: { code: 0, Data: { VideoRewardTab: { TaskList: [ + { TaskId: 'target-a', Title: '看视频领福利A', IsFinished: 0 }, + { TaskId: 'target-b', Title: '看视频领福利B', IsFinished: 0 }, + { TaskId: '1165585369246138370', Title: '每日签到', IsFinished: 0 }, + ] } } } }, + { body: { code: 0, data: { headers: { 'QD-Sign': 'sig' } } } }, + { statusCode: 200, body: { result: 9, message: '' } }, + { body: { code: 0, data: { headers: { 'QD-Sign': 'sig' } } } }, + { statusCode: 200, body: { result: 9, message: '' } }, + { body: { code: 0, data: { headers: { 'QD-Sign': 'sig' } } } }, + { statusCode: 200, body: { code: 0, Data: { VideoRewardTab: { TaskList: [ + { TaskId: 'target-a', Title: '看视频领福利A', IsFinished: 0 }, + { TaskId: 'target-b', Title: '看视频领福利B', IsFinished: 0 }, + { TaskId: '1165585369246138370', Title: '每日签到', IsFinished: 1 }, + ] } } } }, + ], + }); + assert.match(r.replies[0], /激励任务✅\(已完成,跳过 1 个需其它入口任务\)/); + assert.match(r.replies[0], /汇总:成功 1 \/ 失败 0 \/ 总计 1/); +}); + +test('adv task derives remaining callbacks from Count and NeedCount metadata', () => { + const store = { + qdreader_cookie_json: JSON.stringify({ '12345': cookie }), + qdreader_user_bind_json: JSON.stringify({ '12345': 'user-a' }), + qdreader_task_pref_json: JSON.stringify({ '12345': { adv: true } }), + }; + const r = runPlugin({ + content: '启点签到', + store, + userId: 'user-a', + requests: [ + { body: { code: 0, data: { headers: { 'QD-Sign': 'sig' } } } }, + { statusCode: 200, body: { code: 1, msg: '今日已签到' } }, + { body: { code: 0, data: { headers: { 'QD-Sign': 'sig' } } } }, + { statusCode: 200, body: { code: 1, msg: '无可领取礼包' } }, + { body: { code: 0, data: { headers: { 'QD-Sign': 'sig' } } } }, + { statusCode: 200, body: { code: 0, Data: { VideoRewardTab: { TaskList: [{ TaskId: 'partial-task', Title: '看视频领福利', IsFinished: 0, Count: 1, NeedCount: 3 }] } } } }, + { body: { code: 0, data: { headers: { 'QD-Sign': 'sig' } } } }, + { statusCode: 200, body: { result: 9, message: '' } }, + { body: { code: 0, data: { headers: { 'QD-Sign': 'sig' } } } }, + { statusCode: 200, body: { result: 9, message: '' } }, + { body: { code: 0, data: { headers: { 'QD-Sign': 'sig' } } } }, + { statusCode: 200, body: { code: 0, Data: { VideoRewardTab: { TaskList: [{ TaskId: 'partial-task', Title: '看视频领福利', IsFinished: 1, Count: 3, NeedCount: 3 }] } } } }, + ], + }); + assert.match(r.replies[0], /激励任务✅\(已完成\)/); + const finishCalls = r.calls.filter(c => /video\/adv\/finishWatch$/.test(c.url)); + assert.equal(finishCalls.length, 2); + assert.ok(finishCalls.every(c => /partial-task/.test(c.body))); +}); + test('daily task follows upstream sequence: surprise task, one-ad task, then three daily ad callbacks', () => { const store = { qdreader_cookie_json: JSON.stringify({ '12345': cookie }),