diff --git a/js/qdreader_sign_autman.js b/js/qdreader_sign_autman.js index 912980d..b15b8e8 100644 --- a/js/qdreader_sign_autman.js +++ b/js/qdreader_sign_autman.js @@ -618,6 +618,61 @@ function nested(obj, path) { } return cur } +function payloadData(obj) { return (obj && (obj.Data || obj.data)) || {} } +function looksLikeVideoTask(task) { return isObject(task) && !!taskIdOf(task) && (task["IsFinished"] !== undefined || task["IsReceived"] !== undefined || task["Title"] !== undefined || task["TaskName"] !== undefined) } +function taskArrayScore(list, kind) { + if (!Array.isArray(list) || !list.length) return 0 + var score = 0 + for (var i = 0; i < list.length; i++) { + var t = list[i] || {} + if (!looksLikeVideoTask(t)) continue + var title = taskTitle(t) + score += 2 + if (kind === "daily" && (title === "完成1个广告任务得奖励" || title === "完成3个广告任务得奖励" || t["IsReceived"] !== undefined)) score += 5 + if (kind === "adv" && (t["IsFinished"] !== undefined || /激励|碎片|福利|广告/.test(title))) score += 3 + } + return score +} +function findTaskListByShape(root, kind) { + var best = [] + var bestScore = 0 + var seen = [] + function walk(node, depth) { + if (!node || depth > 8) return + if (Array.isArray(node)) { + var score = taskArrayScore(node, kind) + if (score > bestScore) { bestScore = score; best = node } + for (var i = 0; i < node.length; i++) walk(node[i], depth + 1) + return + } + if (!isObject(node) || seen.indexOf(node) >= 0) return + seen.push(node) + for (var k in node) walk(node[k], depth + 1) + } + walk(root, 0) + return best +} +function advTaskList(main) { + return firstArray( + nested(main, ["Data", "DailyBenefitModule", "TaskList"]), + nested(main, ["data", "DailyBenefitModule", "TaskList"]), + nested(main, ["Data", "AdvTaskList"]), + nested(main, ["data", "AdvTaskList"]), + findTaskListByShape(payloadData(main), "adv") + ) +} +function dailyTaskList(main) { + return firstArray( + nested(main, ["Data", "VideoRewardTab", "TaskList"]), + nested(main, ["data", "VideoRewardTab", "TaskList"]), + nested(main, ["Data", "TaskList"]), + nested(main, ["data", "TaskList"]), + findTaskListByShape(payloadData(main), "daily") + ) +} +function surpriseBenefit(data) { + return data.SurpriseBenefit || data.SurpriseTask || data.SurpriseBenefitTask || null +} function makeVideoPayload(taskId) { return { taskId: String(taskId || ""), BanId: "0", BanMessage: "", CaptchaAId: "", CaptchaURL: "", CaptchaType: "0", Challenge: "", Gt: "", NewCaptcha: "0", Offline: "0", PhoneNumber: "", SessionKey: "" } } @@ -667,7 +722,7 @@ function doReceiveGifts(cookie) { 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", "DailyBenefitModule", "TaskList"]), nested(main, ["data", "DailyBenefitModule", "TaskList"])) + var tasks = advTaskList(main) if (!tasks.length) return { name: "激励任务", ok: false, 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" } var submittedCount = 0 @@ -681,7 +736,7 @@ function doAdvTask(cookie) { if (fr.ok < fr.total) return { name: "激励任务", ok: false, msg: resultMsg(fr.last) || ("第 " + (i + 1) + " 个任务失败"), path: "/argus/api/v1/video/adv/finishWatch" } } var verify = qdGet("/argus/api/v2/video/adv/mainPage", {}, cookie) - var after = firstArray(nested(verify, ["Data", "DailyBenefitModule", "TaskList"]), nested(verify, ["data", "DailyBenefitModule", "TaskList"])) + var after = advTaskList(verify) var finished = !!(after && after.length && after.every(function (t) { return taskFinished(t) })) return { name: "激励任务", ok: finished, msg: finished ? "任务已完成" : (submittedCount ? "部分任务未完成" : "未找到可提交任务"), path: "/argus/api/v2/video/adv/mainPage" } } @@ -691,15 +746,15 @@ function taskReceived(task) { function doDailyTask(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 data = main.Data || main.data || {} + var data = payloadData(main) var done = 0 - var pre = data.SurpriseBenefit + var pre = surpriseBenefit(data) if (pre && !taskFinished(pre) && taskIdOf(pre)) { var pr = finishVideoTask(taskIdOf(pre), cookie, 1) done += pr.ok if (pr.ok < pr.total) return { name: "每日任务", ok: false, msg: resultMsg(pr.last) || "惊喜福利任务失败", path: "/argus/api/v1/video/adv/finishWatch" } } - var tasks = firstArray(nested(main, ["Data", "VideoRewardTab", "TaskList"]), nested(main, ["data", "VideoRewardTab", "TaskList"])) + var tasks = dailyTaskList(main) if (!tasks.length) return { name: "每日任务", ok: false, msg: "任务列表为空", path: "/argus/api/v2/video/adv/mainPage" } var oneAd = null var target = null @@ -721,7 +776,7 @@ function doDailyTask(cookie) { done += tr.ok if (tr.ok < tr.total) return { name: "每日任务", ok: false, msg: resultMsg(tr.last) || "每日任务广告失败", path: "/argus/api/v1/video/adv/finishWatch" } var verify = qdGet("/argus/api/v2/video/adv/mainPage", {}, cookie) - var after = firstArray(nested(verify, ["Data", "VideoRewardTab", "TaskList"]), nested(verify, ["data", "VideoRewardTab", "TaskList"])) + var after = dailyTaskList(verify) var finished = false for (var v = 0; v < after.length; v++) if (taskTitle(after[v]) === "完成3个广告任务得奖励" && taskReceived(after[v])) { finished = true; break } return { name: "每日任务", ok: finished, msg: finished ? "任务已完成" : "任务未完成", path: "/argus/api/v2/video/adv/mainPage" } diff --git a/plugins/qdreader/qdreader_sign_autman.js b/plugins/qdreader/qdreader_sign_autman.js index 912980d..b15b8e8 100644 --- a/plugins/qdreader/qdreader_sign_autman.js +++ b/plugins/qdreader/qdreader_sign_autman.js @@ -618,6 +618,61 @@ function nested(obj, path) { } return cur } +function payloadData(obj) { return (obj && (obj.Data || obj.data)) || {} } +function looksLikeVideoTask(task) { return isObject(task) && !!taskIdOf(task) && (task["IsFinished"] !== undefined || task["IsReceived"] !== undefined || task["Title"] !== undefined || task["TaskName"] !== undefined) } +function taskArrayScore(list, kind) { + if (!Array.isArray(list) || !list.length) return 0 + var score = 0 + for (var i = 0; i < list.length; i++) { + var t = list[i] || {} + if (!looksLikeVideoTask(t)) continue + var title = taskTitle(t) + score += 2 + if (kind === "daily" && (title === "完成1个广告任务得奖励" || title === "完成3个广告任务得奖励" || t["IsReceived"] !== undefined)) score += 5 + if (kind === "adv" && (t["IsFinished"] !== undefined || /激励|碎片|福利|广告/.test(title))) score += 3 + } + return score +} +function findTaskListByShape(root, kind) { + var best = [] + var bestScore = 0 + var seen = [] + function walk(node, depth) { + if (!node || depth > 8) return + if (Array.isArray(node)) { + var score = taskArrayScore(node, kind) + if (score > bestScore) { bestScore = score; best = node } + for (var i = 0; i < node.length; i++) walk(node[i], depth + 1) + return + } + if (!isObject(node) || seen.indexOf(node) >= 0) return + seen.push(node) + for (var k in node) walk(node[k], depth + 1) + } + walk(root, 0) + return best +} +function advTaskList(main) { + return firstArray( + nested(main, ["Data", "DailyBenefitModule", "TaskList"]), + nested(main, ["data", "DailyBenefitModule", "TaskList"]), + nested(main, ["Data", "AdvTaskList"]), + nested(main, ["data", "AdvTaskList"]), + findTaskListByShape(payloadData(main), "adv") + ) +} +function dailyTaskList(main) { + return firstArray( + nested(main, ["Data", "VideoRewardTab", "TaskList"]), + nested(main, ["data", "VideoRewardTab", "TaskList"]), + nested(main, ["Data", "TaskList"]), + nested(main, ["data", "TaskList"]), + findTaskListByShape(payloadData(main), "daily") + ) +} +function surpriseBenefit(data) { + return data.SurpriseBenefit || data.SurpriseTask || data.SurpriseBenefitTask || null +} function makeVideoPayload(taskId) { return { taskId: String(taskId || ""), BanId: "0", BanMessage: "", CaptchaAId: "", CaptchaURL: "", CaptchaType: "0", Challenge: "", Gt: "", NewCaptcha: "0", Offline: "0", PhoneNumber: "", SessionKey: "" } } @@ -667,7 +722,7 @@ function doReceiveGifts(cookie) { 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", "DailyBenefitModule", "TaskList"]), nested(main, ["data", "DailyBenefitModule", "TaskList"])) + var tasks = advTaskList(main) if (!tasks.length) return { name: "激励任务", ok: false, 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" } var submittedCount = 0 @@ -681,7 +736,7 @@ function doAdvTask(cookie) { if (fr.ok < fr.total) return { name: "激励任务", ok: false, msg: resultMsg(fr.last) || ("第 " + (i + 1) + " 个任务失败"), path: "/argus/api/v1/video/adv/finishWatch" } } var verify = qdGet("/argus/api/v2/video/adv/mainPage", {}, cookie) - var after = firstArray(nested(verify, ["Data", "DailyBenefitModule", "TaskList"]), nested(verify, ["data", "DailyBenefitModule", "TaskList"])) + var after = advTaskList(verify) var finished = !!(after && after.length && after.every(function (t) { return taskFinished(t) })) return { name: "激励任务", ok: finished, msg: finished ? "任务已完成" : (submittedCount ? "部分任务未完成" : "未找到可提交任务"), path: "/argus/api/v2/video/adv/mainPage" } } @@ -691,15 +746,15 @@ function taskReceived(task) { function doDailyTask(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 data = main.Data || main.data || {} + var data = payloadData(main) var done = 0 - var pre = data.SurpriseBenefit + var pre = surpriseBenefit(data) if (pre && !taskFinished(pre) && taskIdOf(pre)) { var pr = finishVideoTask(taskIdOf(pre), cookie, 1) done += pr.ok if (pr.ok < pr.total) return { name: "每日任务", ok: false, msg: resultMsg(pr.last) || "惊喜福利任务失败", path: "/argus/api/v1/video/adv/finishWatch" } } - var tasks = firstArray(nested(main, ["Data", "VideoRewardTab", "TaskList"]), nested(main, ["data", "VideoRewardTab", "TaskList"])) + var tasks = dailyTaskList(main) if (!tasks.length) return { name: "每日任务", ok: false, msg: "任务列表为空", path: "/argus/api/v2/video/adv/mainPage" } var oneAd = null var target = null @@ -721,7 +776,7 @@ function doDailyTask(cookie) { done += tr.ok if (tr.ok < tr.total) return { name: "每日任务", ok: false, msg: resultMsg(tr.last) || "每日任务广告失败", path: "/argus/api/v1/video/adv/finishWatch" } var verify = qdGet("/argus/api/v2/video/adv/mainPage", {}, cookie) - var after = firstArray(nested(verify, ["Data", "VideoRewardTab", "TaskList"]), nested(verify, ["data", "VideoRewardTab", "TaskList"])) + var after = dailyTaskList(verify) var finished = false for (var v = 0; v < after.length; v++) if (taskTitle(after[v]) === "完成3个广告任务得奖励" && taskReceived(after[v])) { finished = true; break } return { name: "每日任务", ok: finished, msg: finished ? "任务已完成" : "任务未完成", path: "/argus/api/v2/video/adv/mainPage" } diff --git a/tests/qdreader_plugin.test.mjs b/tests/qdreader_plugin.test.mjs index cf897dc..6641bd7 100644 --- a/tests/qdreader_plugin.test.mjs +++ b/tests/qdreader_plugin.test.mjs @@ -1245,6 +1245,43 @@ test('adv task ignores count metadata and submits each unfinished upstream task assert.ok(finishCalls.every(c => /partial-task/.test(c.body))); }); +test('adv task accepts discovered task list when upstream module name changes', () => { + 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: { WelfareCenter: { AdvTasks: [ + { TaskId: 'new-adv-a', Title: '激励碎片', IsFinished: 0 }, + { TaskId: 'new-adv-b', 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: { WelfareCenter: { AdvTasks: [ + { TaskId: 'new-adv-a', Title: '激励碎片', IsFinished: 1 }, + { TaskId: 'new-adv-b', Title: '广告福利', IsFinished: 1 }, + ] } } } }, + ], + }); + assert.match(r.replies[0], /激励任务✅\(任务已完成\)/); + assert.doesNotMatch(r.replies[0], /任务列表为空/); + assert.equal(r.calls.filter(c => /video\/adv\/finishWatch$/.test(c.url)).length, 2); +}); + + 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 }), @@ -1294,6 +1331,54 @@ test('daily task follows upstream sequence: surprise task, one-ad task, then thr }); +test('daily task accepts discovered reward list when upstream module name changes', () => { + 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': { extraAdv: 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: { + RewardModules: { DailyAd: [ + { TaskId: 'new-one-ad', Title: '完成1个广告任务得奖励', IsReceived: 0 }, + { TaskId: 'new-three-ad', Title: '完成3个广告任务得奖励', IsReceived: 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: { 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: { + RewardModules: { DailyAd: [ + { TaskId: 'new-one-ad', Title: '完成1个广告任务得奖励', IsReceived: 1 }, + { TaskId: 'new-three-ad', Title: '完成3个广告任务得奖励', IsReceived: 1 }, + ] } + } } }, + ], + }); + assert.match(r.replies[0], /每日任务✅\(任务已完成\)/); + assert.doesNotMatch(r.replies[0], /任务列表为空/); + const bodies = r.calls.filter(c => /video\/adv\/finishWatch$/.test(c.url)).map(c => c.body); + assert.equal(bodies.length, 4); + assert.match(bodies[0], /new-one-ad/); + assert.equal(bodies.filter(b => /new-three-ad/.test(b)).length, 3); +}); + + test('message box runs outside former Beijing 20-22 window and calls message API', () => { const store = { qdreader_cookie_json: JSON.stringify({ '12345': cookie }),