// ==UserScript== // @name 微软积分(持续修复版) // @namespace https://rewards.bing.com/ // @version 1.0.1 // @description 每天在后台自动完成 Microsoft Rewards 任务获取积分奖励,✅签入(PC+App静默)、✅阅读、✅活动、✅搜索、✅Quiz、✅拼图、✅热搜API、✅二次扫描、✅积分通知、✅连签任务检测、✅每日活动自动上报、✅桌面应用每周专属任务(全程后台运行,不打开多余标签页) // @icon https://bing.com/th?id=OMR.icon-96.png&pid=Rewards // @license MIT // @crontab */20 * * * * // @connect bing.com // @connect login.live.com // @connect rewards.bing.com // @connect prod.rewardsplatform.microsoft.com // @connect hotapi.nntool.cc // @connect hot.baiwumm.com // @connect cnxiaobai.com // @connect disp-qryapi.3g.qq.com // @connect qyapi.weixin.qq.com // @connect oapi.dingtalk.com // @connect open.feishu.cn // @connect push.i-i.me // @connect api.day.app // @match https://login.live.com/oauth20_desktop.srf* // @match https://rewards.bing.com/* // @match https://www.bing.com/* // @match https://cn.bing.com/* // @run-at document-start // @grant unsafeWindow // @grant GM_xmlhttpRequest // @grant GM_notification // @grant GM_openInTab // @grant GM_getValue // @grant GM_setValue // @grant GM_cookie // @grant GM_info // @grant GM_log // @grant GM_registerMenuCommand // @storageName BingRewardsAuto_Shared // @tips 此脚本为开源免费使用,请勿购买 // ==/UserScript== /* global GM_cookie, GM_getValue, GM_setValue, GM_xmlhttpRequest, GM_log, GM_info, GM_notification, GM_openInTab */ /* ==UserConfig== Config: keep: title: 持续检测(全部完成后是否继续) type: checkbox default: true lock: title: 锁定国区(非大陆IP自动停止) type: checkbox default: true span: title: 搜索间隔(秒) type: number default: 30 min: 30 unit: ±15秒 api: title: 搜索词接口(offline为随机搜索词) type: select default: offline values: [offline, hot.nntool.cc, hot.baiwumm.com, hot.cnxiaobai.com] code: title: 授权码链接 type: textarea description: 粘贴 login.live.com 跳转后的完整URL Tasks: sign: title: 每日签入 type: checkbox default: true read: title: 新闻阅读 type: checkbox default: true promos: title: 活动卡片(含打卡) type: checkbox default: true quiz: title: Quiz 自动答题 type: checkbox default: true search: title: PC搜索 type: checkbox default: true Notice: bro: title: 浏览器通知(当前脚本) type: checkbox default: true wework: title: 企业微信消息推送(群机器人) type: text password: true description: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx dingding: title: 钉钉群机器人(不加签,关键词:#) type: text password: true description: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx feishu: title: 飞书群机器人(不加签,关键词:#) type: text password: true description: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx pushme: title: PushMe(push.i-i.me) type: text password: true description: xxxxxxxxxxxxxxxxxxxx bark: title: Bark(bark.day.app) type: text password: true description: xxxxxxxxxxxxxxxxxxxx ==/UserConfig== */ (function() { 'use strict'; if (location.hostname === "login.live.com" && location.pathname === "/oauth20_desktop.srf") { const code = new URLSearchParams(location.search).get("code"); if (code) { GM_setValue("Config.code", location.href); GM_setValue("Config.token", false); if (GM_getValue("Notice.bro", true)) { try { GM_notification({ title: "🟢 授权成功", text: "授权码已捕获,可关闭此页" }); } catch(_) {} } try { history.replaceState({}, "", "about:blank"); } catch(_) {} setTimeout(() => { try { window.close(); } catch(_) {} }, 200); } return; } const RewardsAuto = { ua: { pc: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0", mobile: "Mozilla/5.0 (Linux; Android 16; Redmi K20 Pro Build/BP4A.251205.006; ) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/144.0.7559.132 Mobile Safari/537.36 EdgA/131.0.0.0", app: "Mozilla/5.0 (Linux; Android 16; Redmi K20 Pro Build/BP4A.251205.006; ) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/144.0.7559.132 Mobile Safari/537.36 BingSapphire/32.6.2110003560", }, desktopAppHeaders: { "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) MSRewards/Desktop/1.4.56", "X-MSRewards-App": "1", "X-Rewards-Source": "msrewards-desktop" }, appConfig: { rewardsAppId: "SAAndroid/32.6.2110003560", channel: "SAAndroid", offerIds: { dailyCheckIn: "Gamification_Sapphire_DailyCheckIn", readArticle: "ENUS_readarticle3_30points", } }, searchPool: [ "what is the weather forecast tomorrow", "how do I make sourdough bread at home", "where can I find cheap flights to tokyo", "why is the sky blue scientific explanation", "how to learn rust programming in 2026", "what time does the world cup final start", "how to fix a leaky kitchen faucet step by step", "what are the best vr games of 2026", "how to start a vegetable garden in spring", "where to watch new movies this week", "how to take care of a bonsai tree", "what is the difference between python async and threading", "how to meditate properly for beginners", "what is the origin of halloween traditions", "how do solar panels actually work", "what is the best mechanical keyboard for typing", "how to tie a windsor knot tie", "what causes northern lights aurora borealis", "how to brew the perfect espresso at home", "what are the symptoms of vitamin d deficiency", "how to sleep better naturally tonight", "why do cats purr when they are happy", "best coffee shops in san francisco downtown", "italian restaurants near times square", "tokyo cherry blossom season 2026 forecast", "rtx 5070 ti benchmark vs rtx 4080 super", "iphone 17 release date and features", "tesla stock price today nasdaq", "best noise cancelling headphones under 300", "fastest electric cars 0 to 60 mph", "vintage camera brands collectors guide", "budget gaming laptop with rtx 4070 2026", "easy chocolate chip cookies recipe from scratch", "30 minute home workout routine no equipment", "stretching exercises for lower back pain relief", "easy origami crane folding instructions", "git rebase vs merge which one to use", "markdown cheat sheet with examples", "japanese hiragana chart pronunciation", "ancient rome history quick overview", "pomodoro technique for focus and productivity", "healthy breakfast ideas under 10 minutes", "天气预报", "今日新闻热点", "美食食谱家常菜", "旅游攻略", "健康养生知识", "科技资讯", "电影推荐", "股票行情", "体育赛事", "历史上的今天" ], apiConfig: { mode: GM_getValue("Config.api", "offline"), arr: [ ["hot.baiwumm.com", { url: "https://hot.baiwumm.com/api/", hot: ["weibo", "douyin", "baidu", "toutiao", "thepaper", "qq", "netease", "zhihu"], }], ["hot.cnxiaobai.com", { url: "https://cnxiaobai.com/DailyHotApi/", hot: ["weibo", "douyin", "baidu", "toutiao", "thepaper", "qq-news", "netease-news", "zhihu"], }], ["hot.nntool.cc", { url: "https://hotapi.nntool.cc/", hot: ["weibo", "douyin", "baidu", "toutiao", "thepaper", "qq-news", "netease-news", "zhihu"], }], ], url: "", hot: [], wordList: [], wordIndex: 0, }, skipPatterns: [ "referral", "refer and earn", "sweepstake", "entries", "install the", "set bing as your default", "bing wallpaper", "punch card", "ancient coin", "sea of thieves", "rewards extension", "redemption goal", "order history", "claim your gift", "shop to earn", "set goal", "Available tomorrow", "Offer is Locked", "Earn -1 points", /* "拼图", "quiz" allowed */ ], skipHrefs: [ "sweepstakes/", "referandearn", "aka.ms/win", "workinprogress", "punchcard", "microsoft-store", "goal/all", "orderhistory", "/redeem", "/redeemgoal", "xbox.com/rewards" ], state: { token: false, region: "CN", host: "www.bing.com", dateNowNum: 0, dateNowStr: "", pcProgress: 0, pcMax: 90, readProgress: 0, readMax: 30, sendMSG: "", lastSearchProgress: -1, restrictedTimes: 0, ip: "", ipInfo: "", startTime: 0, } }; const Webhooks = [ { name: "企业微信", url: "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=", key: GM_getValue("Notice.wework", false), msg: { "msgtype": "text", "text": { get content() { return `> ${new Date().toLocaleString()}\n\n ## ${GM_info.script.name}\n ${RewardsAuto.state.sendMSG}` } }, }, }, { name: "钉钉", url: "https://oapi.dingtalk.com/robot/send?access_token=", key: GM_getValue("Notice.dingding", false), msg: { "msgtype": "markdown", "markdown": { "title": GM_info.script.name, get text() { return `> ${new Date().toLocaleString()}\n ### ${GM_info.script.name}\n ${RewardsAuto.state.sendMSG}` } }, }, }, { name: "飞书", url: "https://open.feishu.cn/open-apis/bot/v2/hook/", key: GM_getValue("Notice.feishu", false), msg: { "msg_type": "interactive", "card": { "schema": "2.0", "header": { "title": { "tag": "plain_text", "content": GM_info.script.name }, "template": "orange" }, "body": { "elements": [{ "tag": "markdown", "text_align": "center", get content() { return `#### ${new Date().toLocaleString()}\n ${RewardsAuto.state.sendMSG}` } }] } } }, }, { name: "PushMe", url: "https://push.i-i.me/?push_key=", key: GM_getValue("Notice.pushme", false), msg: { "type": "markdown", "title": `${GM_info.script.name}[#rewards!https://rewards.bing.com/rewards.png]`, get content() { return `\n ${RewardsAuto.state.sendMSG}` } }, }, { name: "Bark", url: "https://api.day.app/", key: GM_getValue("Notice.bark", false), msg: { "group": "rewards", "icon": "https://rewards.bing.com/rewards.png", "title": GM_info.script.name, get markdown() { return `\n ${RewardsAuto.state.sendMSG}` } }, }, ]; const Utils = { log(icon, msg, push = false) { GM_log(`${icon} ${msg}`); if (push && GM_getValue("Notice.bro", true)) { try { GM_notification({ title: GM_info.script.name + ` ${icon}`, text: msg, onclick: () => GM_openInTab("https://rewards.bing.com/dashboard", { active: true }) }); } catch(_) {} } if (push) { RewardsAuto.state.sendMSG = `${icon} ${msg}`; this.sendWebhook(); } }, async sendWebhook() { await Promise.all(Webhooks.map(async (i) => { if (!i.key) return; const safeKey = String(i.key).trim(); const targetUrl = safeKey.startsWith("http") ? safeKey : i.url + safeKey; try { const result = await this.xhr({ method: "POST", url: targetUrl, headers: { "content-type": "application/json; charset=UTF-8" }, data: JSON.stringify(i.msg), }); if (result) GM_log(`🔵 「${i.name}」消息推送完成`); } catch (e) { GM_log(`🔴 「${i.name}」消息推送出错: ${e.message}`); } })); }, xhr(options) { return new Promise((resolve, reject) => { const start = Date.now(); GM_xmlhttpRequest({ anonymous: false, ...options, timeout: 15000, onload: (res) => { const cost = ((Date.now() - start) / 1000).toFixed(2); if (res.status >= 200 && res.status < 300) { resolve(res.responseText); } else if ([301, 302, 307, 308].includes(res.status)) { const match = res.responseHeaders?.match(/Location:\s*(.*?)\s*\r?\n/i); resolve(match ? match[1] : false); } else { reject(new Error(`HTTP ${res.status},用时 ${cost} 秒`)); } }, onerror: (err) => { const cost = ((Date.now() - start) / 1000).toFixed(2); reject(new Error(`${err?.error || "网络错误"},用时 ${cost} 秒`)); }, ontimeout: () => { const cost = ((Date.now() - start) / 1000).toFixed(2); reject(new Error(`请求超时,用时 ${cost} 秒`)); } }); }); }, randomRange(min, max) { return Math.floor(Math.random() * (max - min + 1) + min); }, getTimestamp() { return Date.now(); }, getTodayNum() { const d = new Date(); return Number(`${d.getFullYear()}${String(d.getMonth()+1).padStart(2,"0")}${String(d.getDate()).padStart(2,"0")}`); }, getTodayStr() { const d = new Date(); return `${d.getMonth()+1}/${d.getDate()}/${d.getFullYear()}`; }, getRandomUUID() { return crypto.randomUUID().replace(/-/g, "").toUpperCase(); }, isJSON(s) { try { const j = JSON.parse(s); return Array.isArray(j) || (typeof j === "object" && j !== null); } catch { return false; } }, /** * 从文本(完整跳转URL或裸授权码)中提取 OAuth 授权码 * @param {string} text 用户粘贴的完整URL / 授权码 / 响应文本 * @returns {string|false} 提取到的授权码,失败返回 false */ extractAuthCode(text) { if (!text) return false; const str = String(text).trim(); // 优先按 URL 参数提取(?code=xxx 或 &code=xxx) const urlMatch = str.match(/[?&]code=([^&\s"'\\]+)/i); if (urlMatch && urlMatch[1]) { try { return decodeURIComponent(urlMatch[1]); } catch(_) { return urlMatch[1]; } } // 兜底:直接匹配 login.live.com 授权码固定格式(M.xxx-x-x-x-x) const codeMatch = str.match(/M\.[\w.+]+(?:-[\w+]+){4}/); return codeMatch ? codeMatch[0] : false; }, delay(ms) { return new Promise(r => setTimeout(r, ms)); }, randomDelay(min = 3000, max = 8000) { return this.delay(this.randomRange(min, max)); }, waitForElement(selector, timeout = 30000) { return new Promise((resolve, reject) => { const element = document.querySelector(selector); if (element) return resolve(element); const observer = new MutationObserver((_, obs) => { const el = document.querySelector(selector); if (el) { obs.disconnect(); resolve(el); } }); observer.observe(document.body, { childList: true, subtree: true }); setTimeout(() => { observer.disconnect(); const el = document.querySelector(selector); el ? resolve(el) : reject(new Error(`等待元素超时: ${selector}`)); }, timeout); }); }, waitForElementsByText(containerSelector, textPatterns, timeout = 30000) { return new Promise((resolve, reject) => { const findElements = () => { const containers = document.querySelectorAll(containerSelector); const results = []; for (const container of containers) { const text = container.textContent || ""; for (const pattern of textPatterns) { if (text.includes(pattern)) { results.push({ element: container, pattern }); break; } } } return results; }; const found = findElements(); if (found.length > 0) return resolve(found); const observer = new MutationObserver((_, obs) => { const found = findElements(); if (found.length > 0) { obs.disconnect(); resolve(found); } }); observer.observe(document.body, { childList: true, subtree: true }); setTimeout(() => { observer.disconnect(); const found = findElements(); found.length > 0 ? resolve(found) : reject(new Error("等待文本元素超时")); }, timeout); }); } }; const API = { async getToken(url, maxRetries = 3) { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { const res = await Utils.xhr({ url }); if (!Utils.isJSON(res)) { if (attempt < maxRetries) { await Utils.delay(3210); continue; } return false; } const data = JSON.parse(res); if (data.error) { Utils.log("🔴", `Token错误: ${data.error} - ${data.error_description || ''}`); if (["invalid_grant","invalid_request"].includes(data.error)) { GM_setValue("Config.token", false); GM_setValue("Config.code", ""); } return false; } if (data.refresh_token && data.access_token) { GM_setValue("Config.token", data.refresh_token); GM_setValue("Config.tokenTime", Utils.getTimestamp()); RewardsAuto.state.token = data.access_token; return true; } if (attempt < maxRetries) { await Utils.delay(3210); continue; } return false; } catch (e) { if (e.message.includes("400") || e.message.includes("401")) { GM_setValue("Config.token", false); GM_setValue("Config.code", ""); return false; } if (attempt < maxRetries) { await Utils.delay(3210); continue; } Utils.log("🔴", `Token请求失败: ${e.message}`); return false; } } return false; }, async withTokenRetry(requestFn) { let token = RewardsAuto.state.token; if (!token) return null; try { return await requestFn(token); } catch (e) { if (e.message && e.message.includes("401")) { Utils.log("🟡", "Token 过期,强制重新授权..."); RewardsAuto.state.token = null; GM_setValue("Config.token", false); GM_setValue("Config.tokenTime", 0); const refreshed = await this.renewToken(); if (!refreshed) return null; return await requestFn(RewardsAuto.state.token); } throw e; } }, async renewToken() { if (!GM_getValue("Tasks.sign", true) && !GM_getValue("Tasks.read", true)) return true; // 每次授权流程重置一次性授权码标志,允许使用新粘贴的授权码 RewardsAuto._codeExchanged = false; let refreshToken = GM_getValue("Config.token", false); const tokenTime = GM_getValue("Config.tokenTime", 0); if (tokenTime > 0) { const days = (Utils.getTimestamp() - tokenTime) / (1000 * 60 * 60 * 24); if (days > 7) { Utils.log("🟡", `Token已${Math.floor(days)}天,提前续期`); refreshToken = false; } } const authUrl = "https://login.live.com/oauth20_authorize.srf?client_id=0000000040170455&response_type=code&scope=service::prod.rewardsplatform.microsoft.com::MBI_SSL&redirect_uri=https://login.live.com/oauth20_desktop.srf"; /** * 获取授权码:优先使用用户手动粘贴的授权码,其次尝试自动跟随重定向抓取 * @param {string} msg 提示信息 * @returns {Promise} [授权码] 或 false */ const fetchCode = async (msg) => { // 1) 优先读取用户手动粘贴的授权码(不再清空,避免手动授权失效) const manualCode = Utils.extractAuthCode(GM_getValue("Config.code", "")); if (manualCode) { Utils.log("🟢", `${msg},使用手动粘贴的授权码`); return [manualCode]; } Utils.log("🟡", `${msg},尝试自动获取授权码...`); // 2) 自动获取:跟随重定向从 finalUrl 中提取 code try { const res = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: authUrl, headers: { "User-Agent": navigator.userAgent }, onload: (r) => resolve(r), onerror: () => reject(new Error("请求失败")), ontimeout: () => reject(new Error("超时")), timeout: 15000 }); }); const code = Utils.extractAuthCode(res.finalUrl || ""); if (code) { Utils.log("🟢", "自动获取授权码成功"); return [code]; } // 3) 兜底:redirect:manual 场景下 code 可能藏在响应体中 const bodyCode = Utils.extractAuthCode(res.responseText || ""); if (bodyCode) { Utils.log("🟢", "自动获取授权码成功(响应体)"); return [bodyCode]; } } catch (e) { Utils.log("🟡", `自动获取失败: ${e.message}`); } Utils.log("🔴", "自动获取授权码失败,请在脚本菜单中点击「🔑 手动授权」进行授权,然后复制完整的跳转URL到「📋 粘贴授权码」。", true); if (GM_getValue("Notice.bro", true)) { try { GM_notification({ text: "自动授权失败,请手动点击脚本菜单「🔑 手动授权」", title: "🔴 需要手动授权", timeout: 0 }); } catch(_) {} } return false; // 阻止后续的循环等待,直接退出授权流程 }; if (!refreshToken) { if (RewardsAuto._codeExchanged) { Utils.log("🔴", "授权码已使用过仍无效,请重新手动授权获取新的授权码", true); return false; } const codeMatch = await fetchCode("检测到授权码为空"); if (!codeMatch) return false; // 授权码一次性:无论成败都标记已使用,防止失效码死循环 RewardsAuto._codeExchanged = true; const url = `https://login.live.com/oauth20_token.srf?client_id=0000000040170455&code=${encodeURIComponent(codeMatch[0])}&redirect_uri=https://login.live.com/oauth20_desktop.srf&grant_type=authorization_code`; const token = await this.getToken(url); if (!token) { // 授权码已失效,清除存储的失效值,等待用户重新粘贴 GM_setValue("Config.code", ""); Utils.log("🔴", "授权码已失效或已被使用,请重新点击「🔑 手动授权」并粘贴新的跳转URL", true); return false; } GM_setValue("Config.code", ""); Utils.log("🟢", "Token获取成功!", true); return true; } else { const url = `https://login.live.com/oauth20_token.srf?client_id=0000000040170455&refresh_token=${encodeURIComponent(refreshToken)}&scope=service::prod.rewardsplatform.microsoft.com::MBI_SSL&grant_type=REFRESH_TOKEN`; const token = await this.getToken(url); if (!token) { if (RewardsAuto._codeExchanged) { Utils.log("🔴", "Token续期失败,且授权码已使用,请重新手动授权", true); return false; } const retry = await fetchCode("Token失效"); if (!retry) return false; // refresh_token 失效 → 清除后走授权码换 token 分支 GM_setValue("Config.token", false); RewardsAuto._codeExchanged = true; const codeUrl = `https://login.live.com/oauth20_token.srf?client_id=0000000040170455&code=${encodeURIComponent(retry[0])}&redirect_uri=https://login.live.com/oauth20_desktop.srf&grant_type=authorization_code`; const codeToken = await this.getToken(codeUrl); if (!codeToken) { GM_setValue("Config.code", ""); Utils.log("🔴", "授权码已失效或已被使用,请重新手动授权", true); return false; } GM_setValue("Config.code", ""); Utils.log("🟢", "Token获取成功!", true); return true; } return true; } }, async getRewardsInfo(maxRetries = 3) { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { const html = await Utils.xhr({ url: "https://rewards.bing.com/earn" }); const clean = html.replace(/\\"/g, '"'); let balance = 0; let pcMax = 60, pcCur = 0, mobMax = 0, mobCur = 0; let dailyOffer = 0; let searchQuotaFound = false; const pcIdx = clean.indexOf('"pointsCounters":{'); if (pcIdx !== -1) { let start = pcIdx + 17, depth = 0, end = start; for (let i = start; i < clean.length && i < start + 500; i++) { if (clean[i] === '{') depth++; if (clean[i] === '}') { depth--; if (depth === 0) { end = i + 1; break; } } } try { const pts = JSON.parse(clean.substring(start, end)); pcMax = pts.pc?.max ?? 60; pcCur = pts.pc?.progress ?? 0; mobMax = pts.mobile?.max ?? 0; mobCur = pts.mobile?.progress ?? 0; dailyOffer = pts.dailyOffer ?? 0; if (pts.totalPoints != null) balance = Number(pts.totalPoints); } catch {} } if (balance === 0) { const balMatch = clean.match(/"balance":(\d+)/) || clean.match(/"availablePoints":(\d+)/); if (balMatch) balance = parseInt(balMatch[1]); } const todayDetails = []; const acIdx = clean.indexOf('"activityCards":['); if (acIdx !== -1) { let acStart = acIdx + 16, acDepth = 0, acEnd = acStart; for (let i = acStart; i < clean.length && i < acStart + 5000; i++) { if (clean[i] === '[') acDepth++; if (clean[i] === ']') { acDepth--; if (acDepth === 0) { acEnd = i + 1; break; } } } try { const cardsStr = clean.substring(acStart, acEnd); const cardRegex = /"title":"([^"]+)".*?"points":(\d+).*?"isCompleted":(true|false)/g; let cardMatch; while ((cardMatch = cardRegex.exec(cardsStr)) !== null) { const title = cardMatch[1]; const points = parseInt(cardMatch[2]); const isCompleted = cardMatch[3] === "true"; if (points > 0 && isCompleted) todayDetails.push({ title, points }); } } catch {} } const toNum = value => parseInt(String(value).replace(/,/g, ''), 10) || 0; const searchRowMatch = clean.match(/

\s*必应搜索\s*<\/p>\s*<\/div>\s*]*justify-self-end)[^>]*>([\s\S]{0,500}?)<\/div>/i); const searchHtmlMatch = searchRowMatch ? (searchRowMatch[1].match(/]*>([\d,]+)<\/span>\s*]*>\s*\/\s*([\d,]+)\s*<\/span>/i) || searchRowMatch[1].match(/([\d,]+)\s*\/\s*([\d,]+)/)) : null; if (searchHtmlMatch) { pcCur = toNum(searchHtmlMatch[1]); pcMax = toNum(searchHtmlMatch[2]); searchQuotaFound = pcMax > 0; todayDetails.push({ title: '必应搜索', points: pcCur, max: pcMax }); Utils.log("🔍", `页面表格配额: PC ${pcCur}/${pcMax}`); } const searchRscMatch = clean.match(/"combinedSearch":\{[^}]*"progress":(\d+)[^}]*"max":(\d+)/); if (searchRscMatch && !todayDetails.some(d => d.title === '必应搜索')) { pcCur = toNum(searchRscMatch[1]); pcMax = toNum(searchRscMatch[2]); searchQuotaFound = pcMax > 0; todayDetails.push({ title: '必应搜索', points: pcCur, max: pcMax }); } if (dailyOffer > 0) todayDetails.push({ title: '优惠', points: dailyOffer }); const otherActivityRegex = /

([^<]+)<\/p><\/div>]*>(\d+)<\/div>/g; let otherMatch; while ((otherMatch = otherActivityRegex.exec(clean)) !== null) { const title = otherMatch[1]; const points = parseInt(otherMatch[2]); if (points > 0 && !todayDetails.some(d => d.title === title)) { todayDetails.push({ title, points }); } } const history = { month: 0, year: 0, lifetime: 0 }; const historyRscMatch = clean.match(/"pointsHistory":\{[^}]*"thisMonth":\{"earn":(\d+)[^}]*"thisYear":\{"earn":(\d+)[^}]*"lifetime":\{"earn":(\d+)/); if (historyRscMatch) { history.month = parseInt(historyRscMatch[1]); history.year = parseInt(historyRscMatch[2]); history.lifetime = parseInt(historyRscMatch[3]); } else { const monthHtmlMatch = clean.match(/本月.*?(\d[\d,]*)<\/div>/); const yearHtmlMatch = clean.match(/今年.*?(\d[\d,]*)<\/div>/); const lifetimeHtmlMatch = clean.match(/生存期.*?(\d[\d,]*)<\/div>/); const monthJsonMatch = clean.match(/"monthlyPoints":(\d+)/); const yearJsonMatch = clean.match(/"yearlyPoints":(\d+)/); const lifetimeJsonMatch = clean.match(/"lifetimePoints":(\d+)/); if (monthHtmlMatch) history.month = parseInt(monthHtmlMatch[1].replace(/,/g, '')); else if (monthJsonMatch) history.month = parseInt(monthJsonMatch[1]); if (yearHtmlMatch) history.year = parseInt(yearHtmlMatch[1].replace(/,/g, '')); else if (yearJsonMatch) history.year = parseInt(yearJsonMatch[1]); if (lifetimeHtmlMatch) history.lifetime = parseInt(lifetimeHtmlMatch[1].replace(/,/g, '')); else if (lifetimeJsonMatch) history.lifetime = parseInt(lifetimeJsonMatch[1]); } if (!searchQuotaFound) { const userInfoResult = await this.getSearchQuotaFromUserInfo(); if (userInfoResult) { Utils.log("🟢", "页面未命中搜索配额,使用 getuserinfo 兜底"); return userInfoResult; } } if (!searchQuotaFound && RewardsAuto.state.token) { const apiResult = await this.getSearchQuotaFromAPI(); if (apiResult) { Utils.log("🟢", "页面未命中搜索配额,使用 DAPI 兜底"); return apiResult; } } return { balance, pc: { progress: pcCur, max: pcMax }, mobile: { progress: mobCur, max: mobMax }, dailyOffer, todayDetails, history }; } catch (e) { if (attempt < maxRetries) { await Utils.delay(3210); continue; } Utils.log("🔴", `仪表盘获取失败: ${e.message}`); return false; } } return false; }, async signApp() { const region = GM_getValue("Config.lock", true) ? "cn" : RewardsAuto.state.region.toLowerCase(); try { const res = await this.withTokenRetry(token => Utils.xhr({ method: "POST", url: "https://prod.rewardsplatform.microsoft.com/dapi/me/activities", headers: { "content-type": "application/json; charset=UTF-8", "user-agent": RewardsAuto.ua.app, "authorization": `Bearer ${token}`, "x-rewards-appid": RewardsAuto.appConfig.rewardsAppId, "x-rewards-ismobile": "true", "x-rewards-country": region, "x-rewards-language": "zh", "x-rewards-partnerid": "startapp", "x-rewards-flights": "rwgobig" }, data: JSON.stringify({ amount: 1, id: Utils.getRandomUUID().replace(/-/g, '') + Utils.getRandomUUID().replace(/-/g, '').slice(0, 24), type: 103, country: region, channel: RewardsAuto.appConfig.channel }) })); if (Utils.isJSON(res)) { const data = JSON.parse(res); const response = data.response || {}; if (response.activity) return Number(response.activity.p || response.activity.points || 0); if (response.isDuplicate || response.activity === null) return 0; Utils.log("🟡", `App签入响应未确认: ${String(res).slice(0, 120)}`); } } catch (e) { Utils.log("🔴", `App签入失败: ${e.message}`); } return -1; }, async getRequestVerificationToken(pageUrl = "https://rewards.bing.com/") { try { const html = await Utils.xhr({ url: pageUrl, headers: { "user-agent": RewardsAuto.ua.pc, "referer": "https://rewards.bing.com/" }, anonymous: false }); const tokenMatch = html.match(/name=["']__RequestVerificationToken["'][^>]*value=["']([^"']+)["']/i) || html.match(/RequestVerificationToken.*?value=["']([^"']+)["']/i) || html.match(/"verificationToken"\s*:\s*"([^"]+)"/i) || html.match(/"__RequestVerificationToken"\s*:\s*"([^"]+)"/i); return tokenMatch ? tokenMatch[1].replace(/&/g, "&") : ""; } catch (e) { Utils.log("🟡", `活动Token获取失败: ${e.message}`); return ""; } }, async reportActivity(offerId, hash, referer = "https://rewards.bing.com/") { const token = await this.getRequestVerificationToken(referer); const params = new URLSearchParams({ id: offerId, hash: hash || "1", activityAmount: "1" }); if (token) params.set("__RequestVerificationToken", token); const headers = { "content-type": "application/x-www-form-urlencoded; charset=UTF-8", "user-agent": RewardsAuto.ua.pc, "referer": referer, "origin": "https://rewards.bing.com", "x-requested-with": "XMLHttpRequest" }; if (token) headers["RequestVerificationToken"] = token; return await Utils.xhr({ method: "POST", url: "https://rewards.bing.com/api/reportactivity?X-Requested-With=XMLHttpRequest", headers, data: params.toString(), anonymous: false }); }, async reportDailyActivity(searchUrl) { try { const fullUrl = searchUrl.startsWith("http") ? searchUrl : `https://cn.bing.com${searchUrl}`; const ig = Utils.getRandomUUID().replace(/-/g, '').substring(0, 32).toUpperCase(); const cnUrl = fullUrl.replace(/^https?:\/\/www\.bing\.com/, "https://cn.bing.com") .replace(/^https:\/\/bing\.com/, "https://cn.bing.com"); const cnUrlObj = new URL(cnUrl); const cnSp = cnUrlObj.searchParams; const reportParams = new URLSearchParams(); reportParams.set("IG", ig); reportParams.set("IID", "commerce.5067"); if (cnSp.get("form")) reportParams.set("form", cnSp.get("form")); if (cnSp.get("ocid") || cnSp.get("OCID")) reportParams.set("ocid", cnSp.get("ocid") || cnSp.get("OCID")); if (cnSp.get("rnoreward")) reportParams.set("rnoreward", cnSp.get("rnoreward")); try { await Utils.xhr({ method: "GET", url: cnUrl, headers: { "user-agent": RewardsAuto.ua.pc, "referer": "https://rewards.bing.com/", "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "accept-language": "zh-CN,zh;q=0.9,en;q=0.8" } }); } catch (_) {} const ncheaderParams = new URLSearchParams(); ncheaderParams.set("ver", String(Date.now()).substring(0, 8)); ncheaderParams.set("IID", "commerce.5057"); ncheaderParams.set("IG", ig); try { await Utils.xhr({ method: "POST", url: `https://cn.bing.com/rewardsapp/ncheader?${ncheaderParams.toString()}`, headers: { "content-type": "application/x-www-form-urlencoded", "user-agent": RewardsAuto.ua.pc, "referer": cnUrl, "origin": "https://cn.bing.com", "accept": "*/*", "accept-language": "zh-CN,zh;q=0.9,en;q=0.8" }, data: "wb=1;i=1;v=1" }); } catch (_) {} const bodyParams = new URLSearchParams(); bodyParams.set("url", cnUrl); bodyParams.set("V", "web"); await Utils.xhr({ method: "POST", url: `https://cn.bing.com/rewardsapp/reportActivity?${reportParams.toString()}`, headers: { "content-type": "application/x-www-form-urlencoded", "user-agent": RewardsAuto.ua.pc, "referer": cnUrl, "origin": "https://cn.bing.com", "accept": "*/*", "accept-language": "zh-CN,zh;q=0.9,en;q=0.8" }, data: bodyParams.toString() }); return true; } catch (e) { Utils.log("🟡", `每日活动上报失败: ${e.message}`); return false; } }, async signPC() { try { const res = await this.reportActivity("Gamification_DailyCheckIn", "1", "https://rewards.bing.com/"); if (Utils.isJSON(res)) { const data = JSON.parse(res); return Number(data.points || data.response?.activity?.p || 0); } } catch (e) { if (e.message?.includes("401")) RewardsAuto.state.pc401 = true; Utils.log("🟡", `PC签入失败: ${e.message}`); } return -1; }, async appActivity(type, offerid) { const region = GM_getValue("Config.lock", true) ? "cn" : RewardsAuto.state.region.toLowerCase(); const body = { amount: 1, country: region, id: Utils.getRandomUUID().replace(/-/g, '') + Utils.getRandomUUID().replace(/-/g, '').slice(0, 24), type: type, channel: RewardsAuto.appConfig.channel }; if (offerid) body.attributes = { offerid: offerid }; try { const res = await this.withTokenRetry(token => Utils.xhr({ method: "POST", url: "https://prod.rewardsplatform.microsoft.com/dapi/me/activities", headers: { "content-type": "application/json; charset=utf-8", "user-agent": RewardsAuto.ua.app, "authorization": `Bearer ${token}`, "x-rewards-appid": RewardsAuto.appConfig.rewardsAppId, "x-rewards-ismobile": "true", "x-rewards-country": region, "x-rewards-language": "zh" }, data: JSON.stringify(body) })); if (Utils.isJSON(res)) { const data = JSON.parse(res); return { points: data.response?.activity?.p || 0, isDuplicate: data.response?.isDuplicate || false, balance: data.response?.balance || 0 }; } } catch (e) { Utils.log("🔴", `App活动失败(${offerid}): ${e.message}`); } return null; }, /** * 获取 DAPI 任务列表(新版后台接口,任务数据最可靠) * @returns {Promise} 标准化任务数组,失败返回 false */ async getDAPIPromos() { try { const res = await this.withTokenRetry(token => Utils.xhr({ url: "https://prod.rewardsplatform.microsoft.com/dapi/me?channel=SAAndroid&options=511", headers: { "content-type": "application/json; charset=UTF-8", "user-agent": RewardsAuto.ua.app, "authorization": `Bearer ${token}`, "x-rewards-appid": RewardsAuto.appConfig.rewardsAppId, "x-rewards-ismobile": "true", "x-rewards-country": "cn", "x-rewards-language": "zh" } })); if (!Utils.isJSON(res)) return false; const response = JSON.parse(res).response || {}; const promos = response.promotions || []; const list = []; for (const p of promos) { if (!p || typeof p !== "object") continue; const a = p.attributes || {}; const offerId = a.offerid || p.name || ""; if (!offerId) continue; list.push({ offerId, title: a.title || p.name || offerId, type: String(a.type || "").toLowerCase(), complete: String(a.complete).toLowerCase() === "true" || p.complete === true, hidden: String(a.hidden).toLowerCase() === "true", progress: Number(a.progress ?? a.activityprogress ?? 0) || 0, max: Number(a.max ?? a.activitymax ?? 0) || 0, points: Number(p.pointProgressMax ?? a.pointmax ?? 0) || 0, destination: a.destination || p.destinationUrl || "", hash: p.hash || "" }); } return list; } catch (e) { Utils.log("🟡", `DAPI任务列表获取失败: ${e.message}`); return false; } }, /** * 通过 DAPI 完成/领取单个任务(urlreward/checkin/msnreadearn 类型通用) * @param {string} offerId 任务ID * @param {number} type 活动类型(默认 101) * @returns {Promise} {points, isDuplicate, balance} 或 null */ async claimDAPI(offerId, type = 101) { const region = GM_getValue("Config.lock", true) ? "cn" : (RewardsAuto.state.region || "cn").toLowerCase(); try { const res = await this.withTokenRetry(token => Utils.xhr({ method: "POST", url: "https://prod.rewardsplatform.microsoft.com/dapi/me/activities", headers: { "content-type": "application/json; charset=utf-8", "user-agent": RewardsAuto.ua.app, "authorization": `Bearer ${token}`, "x-rewards-appid": RewardsAuto.appConfig.rewardsAppId, "x-rewards-ismobile": "true", "x-rewards-country": region, "x-rewards-language": "zh", "x-rewards-partnerid": "startapp" }, data: JSON.stringify({ amount: 1, country: region, id: Utils.getRandomUUID().replace(/-/g, "").toLowerCase(), type: type, channel: RewardsAuto.appConfig.channel, attributes: { offerid: offerId } }) })); if (Utils.isJSON(res)) { const data = JSON.parse(res); return { points: Number(data.response?.activity?.p || 0), isDuplicate: Boolean(data.response?.isDuplicate), balance: Number(data.response?.balance || 0) }; } } catch (e) { Utils.log("🟡", `DAPI任务执行失败(${offerId}): ${e.message}`); } return null; }, async getReadProgress() { // 优先从 DAPI 任务列表动态识别阅读任务(兼容各区域 offerid 差异) const findReadOffer = async () => { const promos = await this.getDAPIPromos(); if (Array.isArray(promos)) { const hit = promos.find(x => x.type === "msnreadearn" || /readarticle/i.test(x.offerId)); if (hit) return hit; } return null; }; try { const offer = await findReadOffer(); if (offer) { Utils.log("📊", `阅读进度查询: ${offer.progress}/${offer.max} (offerid: ${offer.offerId})`); RewardsAuto._readOfferId = offer.offerId; return { progress: offer.progress, max: offer.max || 30, offerId: offer.offerId, complete: offer.complete }; } } catch (e) {} // 兜底:按配置的固定 offerid 查询 try { const res = await this.withTokenRetry(token => Utils.xhr({ url: "https://prod.rewardsplatform.microsoft.com/dapi/me?channel=SAAndroid&options=613", headers: { "content-type": "application/json; charset=UTF-8", "user-agent": RewardsAuto.ua.app, "authorization": `Bearer ${token}`, "x-rewards-appid": RewardsAuto.appConfig.rewardsAppId, "x-rewards-ismobile": "true", "x-rewards-country": "cn", "x-rewards-language": "zh" } })); if (Utils.isJSON(res)) { const promos = JSON.parse(res).response?.promotions || []; const readOfferId = RewardsAuto.appConfig.offerIds.readArticle; const task = promos.find(x => x.attributes?.offerid === readOfferId); if (task && task.attributes) { const progress = parseInt(task.attributes.progress) || 0; const max = parseInt(task.attributes.max) || 30; Utils.log("📊", `阅读进度查询: ${progress}/${max} (offerid: ${readOfferId})`); return { progress, max, offerId: readOfferId }; } else { Utils.log("🟡", `阅读任务未找到 (offerid: ${readOfferId})`); } } else { Utils.log("🟡", `DAPI 响应不是 JSON: ${String(res).substring(0, 100)}`); } } catch (e) { Utils.log("🔴", `阅读进度获取失败: ${e.message}`); } return false; }, async getRewardsToken() { try { const html = await Utils.xhr({ url: "https://rewards.bing.com/", headers: { "user-agent": RewardsAuto.ua.pc, "referer": "https://rewards.bing.com/" } }); if (html) { const match = html.replace(/\s/g, "").match(/RequestVerificationToken(.*?)value="(.*?)"/); if (match) return match[2]; } } catch (e) { Utils.log("🟡", `RequestVerificationToken 获取失败: ${e.message}`); } return false; }, async getSearchQuotaFromUserInfo() { try { const res = await Utils.xhr({ url: `https://rewards.bing.com/api/getuserinfo?type=1&X-Requested-With=XMLHttpRequest&_=${Date.now()}`, headers: { "content-type": "application/x-www-form-urlencoded; charset=UTF-8", "user-agent": RewardsAuto.ua.pc, "referer": "https://rewards.bing.com/", "x-requested-with": "XMLHttpRequest" }, anonymous: false }); if (!Utils.isJSON(res)) return false; const data = JSON.parse(res); const dashboard = data.dashboard || data; const userStatus = dashboard.userStatus || {}; const counters = userStatus.counters || {}; const sumCounter = items => { if (!Array.isArray(items)) return { progress: 0, max: 0 }; return items.reduce((acc, item) => { acc.progress += Number(item.pointProgress || 0); acc.max += Number(item.pointProgressMax || item.pointMax || 0); return acc; }, { progress: 0, max: 0 }); }; const pc = sumCounter(counters.pcSearch); if (pc.max === 0) return false; const balance = Number(userStatus.availablePoints || dashboard.availablePoints || 0); let readProgress = 0, readMax = 30; try { const readInfo = await API.getReadProgress(); if (readInfo) { readProgress = readInfo.progress; readMax = readInfo.max; } } catch (e) {} Utils.log("📊", `getuserinfo查询: PC ${pc.progress}/${pc.max}, 阅读 ${readProgress}/${readMax}, 积分 ${balance}`); return { balance, pc, readProgress, readMax, dailyOffer: 0, todayDetails: pc.max > 0 ? [{ title: "必应搜索", points: pc.progress, max: pc.max }] : [], history: null }; } catch (e) { if (GM_getValue("Config.debugDAPI", false)) { Utils.log("🟡", `getuserinfo 查询失败: ${e.message}`); } return false; } }, async getSearchQuotaFromAPI() { try { const res = await this.withTokenRetry(token => Utils.xhr({ url: "https://prod.rewardsplatform.microsoft.com/dapi/me?channel=SAAndroid&options=613", headers: { "content-type": "application/json; charset=UTF-8", "user-agent": RewardsAuto.ua.app, "authorization": `Bearer ${token}`, "x-rewards-appid": RewardsAuto.appConfig.rewardsAppId, "x-rewards-ismobile": "true", "x-rewards-country": "cn", "x-rewards-language": "zh" } })); if (Utils.isJSON(res)) { const data = JSON.parse(res); const response = data.response || {}; const promos = response.promotions || []; const counters = response.counters || response.userStatus?.counters; if (!counters) return false; let pcCur = 0, pcMax = 0; if (counters?.pcSearch && counters.pcSearch.length > 0) { pcCur = counters.pcSearch[0].pointProgress || 0; pcMax = counters.pcSearch[0].pointProgressMax || 0; } if (pcMax === 0) return false; const balance = response.balance || response.userStatus?.availablePoints || 0; let readProgress = 0, readMax = 30; try { const readInfo = await API.getReadProgress(); if (readInfo) { readProgress = readInfo.progress; readMax = readInfo.max; } } catch (e) {} Utils.log("📊", `DAPI查询: PC ${pcCur}/${pcMax}, 阅读 ${readProgress}/${readMax}, 积分 ${balance}`); return { balance, pc: { progress: pcCur, max: pcMax }, readProgress, readMax, dailyOffer: 0, todayDetails: [], history: null }; } } catch (e) { Utils.log("🟡", `DAPI查询失败: ${e.message}`); } return false; }, async getBalance() { try { const res = await this.withTokenRetry(token => Utils.xhr({ url: "https://prod.rewardsplatform.microsoft.com/dapi/me?channel=SAAndroid&options=105", headers: { "content-type": "application/json; charset=UTF-8", "user-agent": RewardsAuto.ua.app, "authorization": `Bearer ${token}`, "x-rewards-appid": RewardsAuto.appConfig.rewardsAppId, "x-rewards-ismobile": "true", "x-rewards-country": "cn", "x-rewards-language": "zh" } })); if (Utils.isJSON(res)) { const data = JSON.parse(res); return data.response?.balance || 0; } } catch (e) {} try { const res2 = await Utils.xhr({ url: `https://rewards.bing.com/api/getuserinfo?type=1&X-Requested-With=XMLHttpRequest&_=${Date.now()}`, headers: { "user-agent": RewardsAuto.ua.pc, "referer": "https://rewards.bing.com/", "x-requested-with": "XMLHttpRequest" }, }); if (Utils.isJSON(res2)) { const data2 = JSON.parse(res2); return data2.dashboard?.availablePoints || data2.balance || 0; } } catch (e) {} return 0; }, async doRead(offerId) { const region = GM_getValue("Config.lock", true) ? "cn" : (RewardsAuto.state.region || "cn").toLowerCase(); const readOffer = offerId || RewardsAuto._readOfferId || RewardsAuto.appConfig.offerIds.readArticle; try { const id = Utils.getRandomUUID().replace(/-/g, '').toLowerCase() + Utils.getRandomUUID().replace(/-/g, '').toLowerCase().slice(0, 24); const res = await this.withTokenRetry(token => Utils.xhr({ method: "POST", url: "https://prod.rewardsplatform.microsoft.com/dapi/me/activities", headers: { "content-type": "application/json; charset=utf-8", "user-agent": RewardsAuto.ua.app, "authorization": `Bearer ${token}`, "x-rewards-appid": RewardsAuto.appConfig.rewardsAppId, "x-rewards-ismobile": "true", "x-rewards-country": region, "x-rewards-language": "zh" }, data: JSON.stringify({ amount: 1, country: region, id: id, type: 101, attributes: { offerid: readOffer } }) })); if (Utils.isJSON(res)) { const data = JSON.parse(res); return { points: data.response?.activity?.p || 0, isDuplicate: data.response?.isDuplicate || false }; } return null; } catch (e) { Utils.log("🔴", `阅读请求失败: ${e.message}`); return false; } }, async discoverCards() { const cards = []; const seenCardKeys = new Set(); let html = ""; // 入列卡片(去重、过滤无效项) const pushCard = (card) => { if (!card || !card.offerId || !card.hash || card.points <= 0) return; const key = `${card.offerId}:${card.hash}`; if (seenCardKeys.has(key)) return; seenCardKeys.add(key); cards.push(card); }; try { html = await Utils.xhr({ url: "https://rewards.bing.com/earn" }); if (!html) { Utils.log("🔴", "earn 页面返回空"); return cards; } const clean = html.replace(/\\"/g, '"'); const inferKind = (offerId, title = "") => { const text = `${offerId} ${title}`.toLowerCase(); if (/quiz|trivia|question|测验|答案/.test(text)) return "quiz"; if (/puzzle|拼图/.test(text)) return "puzzle"; if (/image|图片|设计/.test(text)) return "image_creator"; if (/explore|search|浏览|探索/.test(text)) return "explore_search"; if (/dailyset|daily|每日/.test(text)) return "daily"; if (/streak|打卡|连续/.test(text)) return "streak"; return "open_only"; }; const parseCard = (obj) => { const offerIdMatch = obj.match(/"offerId":"([^"]+)"/i) || obj.match(/"offerid":"([^"]+)"/i); const hashMatch = obj.match(/"hash":"([^"]+)"/) || obj.match(/"activityId":"([^"]+)"/) || obj.match(/"id":"([^"]+)"/); if (!offerIdMatch || !hashMatch) return null; const offerId = offerIdMatch[1]; const hash = hashMatch[1]; const pointsMatch = obj.match(/"points":(\d+)/); const isCompletedMatch = obj.match(/"isCompleted":(true|false)/i) || obj.match(/"state":"(completed|CLAIMED)"/i); const titleMatch = obj.match(/"title":"([^"]+)"/) || obj.match(/"name":"([^"]+)"/); const points = pointsMatch ? parseInt(pointsMatch[1]) : 0; const isCompleted = isCompletedMatch ? (isCompletedMatch[1] === "true" || isCompletedMatch[1] === "completed") : false; if (isCompleted) return null; const title = titleMatch ? titleMatch[1] : ""; const skip = RewardsAuto.skipPatterns.some(p => title.toLowerCase().includes(p.toLowerCase()) || offerId.toLowerCase().includes(p.toLowerCase())); if (skip) return null; // 保留目标链接(访问型卡片需靠它完成上报) const destMatch = obj.match(/"destination(?:Url)?":"([^"]+)"/i); return { title, points, offerId, hash, kind: inferKind(offerId, title), destination: destMatch ? destMatch[1] : "" }; }; try { const userInfo = await Utils.xhr({ url: `https://rewards.bing.com/api/getuserinfo?type=1&X-Requested-With=XMLHttpRequest&_=${Date.now()}`, headers: { "user-agent": RewardsAuto.ua.pc, "referer": "https://rewards.bing.com/", "x-requested-with": "XMLHttpRequest" }, anonymous: false }); if (Utils.isJSON(userInfo)) { const data = JSON.parse(userInfo); const dashboard = data.dashboard || data; const now = new Date(); const todayKeys = new Set([ `${String(now.getMonth() + 1).padStart(2, "0")}/${String(now.getDate()).padStart(2, "0")}/${now.getFullYear()}`, `${now.getMonth() + 1}/${now.getDate()}/${now.getFullYear()}` ]); const normalizeDashboardCard = (item, kind) => { if (!item) return null; const offerId = item.offerId || item.offerid || item.id || item.name; const hash = item.hash || item.activityId; const title = item.title || item.name || item.description || ""; const points = Number(item.points ?? item.pointProgressMax ?? item.max ?? 0); const doneMax = Number(item.pointProgressMax || 0); const doneCur = Number(item.pointProgress || 0); const isCompleted = item.isCompleted || item.complete || (doneMax > 0 && doneCur >= doneMax); if (!offerId || !hash || points <= 0 || isCompleted) return null; const skip = RewardsAuto.skipPatterns.some(p => title.toLowerCase().includes(p.toLowerCase()) || offerId.toLowerCase().includes(p.toLowerCase())); if (skip) return null; // 保留目标链接(访问型卡片需靠它完成上报) const destination = item.destinationUrl || item.destination || ""; return { title, points, offerId, hash, kind: kind || inferKind(offerId, title), source: "getuserinfo", destination, url: destination || "https://rewards.bing.com/" }; }; const dailySetPromotions = dashboard.dailySetPromotions || {}; for (const dateKey of todayKeys) { const dailyItems = dailySetPromotions[dateKey]; if (Array.isArray(dailyItems)) { for (const item of dailyItems) pushCard(normalizeDashboardCard(item, "daily")); } } const morePromotions = dashboard.morePromotions || dashboard.promotions || []; if (Array.isArray(morePromotions)) { for (const item of morePromotions) pushCard(normalizeDashboardCard(item)); } } } catch (e) { Utils.log("🟡", `getuserinfo 活动解析跳过: ${e.message}`); } // --- [Next.js Flight Chunks & DAPI parsing] --- try { const chunks = []; const pushRegex = /self\.__next_f\.push\(\[\d+,\s*"(.*?)"\]\)/gs; let mFlight; while ((mFlight = pushRegex.exec(html)) !== null) { try { const unescaped = JSON.parse(`"${mFlight[1]}"`); chunks.push(unescaped); } catch (_) {} } const fullFlightText = chunks.length > 0 ? chunks.join("") : html.replace(/\\"/g, '"'); const offerObjRegex = /\{[^{}]*?"offerId"\s*:\s*"([^"]+)"[^{}]*?\}/g; let om; while ((om = offerObjRegex.exec(fullFlightText)) !== null) { try { const jsonStr = om[0].replace(/:\$undefined/g, ":null").replace(/:\$D[^\",}]+/g, ":null"); const item = JSON.parse(jsonStr); if (!item.offerId || !item.hash) continue; if (item.isCompleted === true || item.isLocked === true) continue; const points = Number(item.points || 0); if (points <= 0 && !item.offerId.includes("DailySet")) continue; pushCard({ title: item.title || item.name || item.offerId, points: points || 10, offerId: item.offerId, hash: item.hash, kind: inferKind(item.offerId, item.title || ""), destination: item.destination || item.destinationUrl || "", url: "https://rewards.bing.com/earn" }); } catch (_) {} } } catch (e) { Utils.log("🟡", `Flight 数据解析异常: ${e.message}`); } if (cards.length === 0) { const dataRegex = /data-offer-id="([^"]+)"[^>]*data-hash="([^"]+)"/gi; let m; while ((m = dataRegex.exec(html)) !== null) { const offerId = m[1]; const hash = m[2]; const skip = RewardsAuto.skipPatterns.some(p => offerId.toLowerCase().includes(p.toLowerCase())); if (skip) continue; pushCard({ title: "日常任务", points: 10, offerId, hash, kind: "daily" }); } } if (cards.length > 0) { Utils.log("🧩", `成功获取 ${cards.length} 个可执行卡片`); } else { Utils.log("🟡", "未发现可执行的活动卡片(可能均已手动完成或被过滤)"); } } catch (e) { Utils.log("🔴", `卡片解析失败: ${e.message}`); } // --- [打卡/任务卡(PunchCard)子任务解析] --- // 发现页面中所有 /earn/quest/{parentOfferId} 链接,逐个解析子任务并入列 try { const questIds = [...new Set( [...(html.matchAll(/\/earn\/quest\/([A-Za-z0-9_]+)/g) || [])].map(m => m[1]) .concat([...(html.matchAll(/[A-Za-z0-9_]*pcparent[A-Za-z0-9_]*/g) || [])].map(m => m[0])) )]; Utils.log("🧩", `发现 ${questIds.length} 个任务卡(quest)入口`); for (const questId of questIds.slice(0, 12)) { try { const questUrl = `https://rewards.bing.com/earn/quest/${questId}`; const isDesktop = /RewardsApp_weekly_Exclusive/i.test(questId); const questHtml = await Utils.xhr({ url: questUrl, headers: isDesktop ? RewardsAuto.desktopAppHeaders : { "user-agent": RewardsAuto.ua.pc, "referer": "https://rewards.bing.com/earn" } }); if (!questHtml) continue; const qChunks = []; const qPushRegex = /self\.__next_f\.push\(\[\d+,\s*"(.*?)"\]\)/gs; let qm; while ((qm = qPushRegex.exec(questHtml)) !== null) { try { qChunks.push(JSON.parse(`"${qm[1]}"`)); } catch (_) {} } const qText = qChunks.length > 0 ? qChunks.join("") : questHtml.replace(/\\"/g, '"'); // 提取含 offerId 的对象(兼容任意 pcchild 命名) const qOfferRegex = /\{[^{}]*?"offerId"\s*:\s*"([^"]+)"[^{}]*?\}/g; let qo; while ((qo = qOfferRegex.exec(qText)) !== null) { try { const qItem = JSON.parse(qo[0].replace(/:\$undefined/g, ":null").replace(/:\$D[^\",}]+/g, ":null")); if (!qItem.offerId || !qItem.hash) continue; if (qItem.isLocked === true || qItem.isCompleted === true) continue; // 多日搜索类子任务需真实搜索,跳过 if (/search/i.test(qItem.offerId) && /(day|streak|\dx)/i.test(qItem.offerId)) continue; const isDesktopChild = /pcchild|RewardsApp/i.test(qItem.offerId); Utils.log("🧩", `发现任务卡子项: ${qItem.title || qItem.offerId}`); pushCard({ title: `${isDesktopChild ? "[桌面专享] " : "[任务卡] "}${qItem.title || qItem.name || qItem.offerId}`, points: Number(qItem.points || qItem.pointProgressMax || 10), offerId: qItem.offerId, hash: qItem.hash, kind: isDesktopChild ? "desktop_app" : "quest_child", destination: qItem.destination || qItem.destinationUrl || qItem.href || "", url: questUrl }); } catch (_) {} } } catch (eInner) { Utils.log("🟡", `任务卡 ${questId} 解析跳过: ${eInner.message}`); } } } catch (eQuest) { Utils.log("🟡", `任务卡解析跳过: ${eQuest.message}`); } return cards; }, /** * 从页面 JS chunk 中解析 Next.js Server Action ID(支持按动作名识别) * @param {string} html 页面 HTML(含 chunk 引用) * @returns {Promise} {reportActivity, reportClaimAllPoints, all:[]} */ async resolveServerActions(html) { const cache = RewardsAuto._serverActions || GM_getValue("Config.serverActions", null); if (cache && cache.reportActivity) return cache; const result = { reportActivity: "", reportClaimAllPoints: "", all: [] }; try { const hex = "[a-f0-9]{40,64}"; const KNOWN_NON_NAMES = new Set(["callServer", "findSourceMapURL", "encodeFormAction", "default"]); // 从 chunk 文本中按 createServerReference/registerServerReference 提取 id 与动作名 const scan = (jsText) => { const createRegex = new RegExp(`createServerReference\\s*\\)?\\s*\\(\\s*"(${hex})"([\\s\\S]{0,800}?)\\)`, "g"); const registerRegex = new RegExp(`registerServerReference\\s*\\)?\\s*\\([^,]+,\\s*"(${hex})"([\\s\\S]{0,800}?)\\)`, "g"); const strLitRe = /"([A-Za-z_$][\w$]*)"/g; for (const re of [createRegex, registerRegex]) { let m; while ((m = re.exec(jsText)) !== null) { const id = m[1]; if (!result.all.includes(id)) result.all.push(id); const argsBlock = m[2] || ""; const candidates = [...argsBlock.matchAll(strLitRe)] .map(x => x[1]) .filter(n => !KNOWN_NON_NAMES.has(n) && n.length > 3); const name = candidates.length ? candidates[candidates.length - 1] : ""; if (name === "reportActivity" && !result.reportActivity) result.reportActivity = id; if (name === "reportClaimAllPoints" && !result.reportClaimAllPoints) result.reportClaimAllPoints = id; } } }; if (html && html.includes("_next/static/chunks/")) { const scriptSrcs = [...new Set(html.match(/\/_next\/static\/chunks\/[a-zA-Z0-9_\-/.]+\.js[^"']*/g) || [])]; for (const src of scriptSrcs.slice(0, 20)) { try { const chunkText = await Utils.xhr({ url: "https://rewards.bing.com" + src.split("?")[0] }); if (chunkText) scan(chunkText); if (result.reportActivity && result.reportClaimAllPoints) break; } catch (_) {} } } if (!result.reportActivity && result.all.length) result.reportActivity = result.all[0]; RewardsAuto._serverActions = result; GM_setValue("Config.serverActions", result); } catch (e) { Utils.log("🟡", `Server Action 解析异常: ${e.message}`); } return result; }, /** * 兼容旧调用:返回 reportActivity 的 action id */ async getNextActionHash(html) { const actions = await this.resolveServerActions(html); return actions.reportActivity || GM_getValue("Config.nextAction", "707e6eb15bdfdd5fba193f0a77e934f7018faf87ce"); }, /** * 领取/完成单个任务卡片(多策略:DAPI → Server Action → 老版 reportactivity) * @param {Object} card 任务卡片 {offerId, hash, title, kind, destination, url} * @returns {Promise} 是否确认完成 */ async claimCard(card) { const url = card.url || "https://rewards.bing.com/earn"; const referer = card.url || "https://rewards.bing.com/earn"; // 策略优先: 访问型卡片(explore_search/daily/open_only 且带目标链接) // 这类卡片必须真实访问目标链接触发 /rewardsapp/reportActivity 上报才能完成 const isVisitType = ["explore_search", "daily", "open_only"].includes(card.kind) || /explore|search|浏览|探索|learn|了解/i.test(`${card.offerId} ${card.title || ""}`); if (card.destination && card.destination.startsWith("http") && isVisitType) { const ok = await this.reportDailyActivity(card.destination); if (ok) { Utils.log("🟢", `目标链接访问上报成功(${card.offerId})`); return true; } } // 策略 0: DAPI 后台接口(与必应 App 同源,最可靠) if (card.offerId) { const region = GM_getValue("Config.lock", true) ? "cn" : (RewardsAuto.state.region || "cn").toLowerCase(); try { const res = await this.withTokenRetry(token => Utils.xhr({ method: "POST", url: "https://prod.rewardsplatform.microsoft.com/dapi/me/activities", headers: { "content-type": "application/json; charset=utf-8", "user-agent": RewardsAuto.ua.app, "authorization": `Bearer ${token}`, "x-rewards-appid": RewardsAuto.appConfig.rewardsAppId, "x-rewards-ismobile": "true", "x-rewards-country": region, "x-rewards-language": "zh", "x-rewards-partnerid": "startapp" }, data: JSON.stringify({ amount: 1, country: region, id: Utils.getRandomUUID().replace(/-/g, "").toLowerCase(), type: 101, channel: RewardsAuto.appConfig.channel, attributes: { offerid: card.offerId } }) })); if (Utils.isJSON(res)) { const data = JSON.parse(res); const points = Number(data.response?.activity?.p || 0); const dup = Boolean(data.response?.isDuplicate); if (points > 0 || dup) { Utils.log("🟢", `DAPI领取成功(${card.offerId}) ${points > 0 ? `+${points}积分` : "已重复"}`); return true; } } } catch (_) {} } // 策略 1: Next.js Server Action(需动态 action id + 路由树 + 部署ID) try { const earnHtml = await Utils.xhr({ url: "https://rewards.bing.com/earn" }); const actions = await this.resolveServerActions(earnHtml); const nextAction = actions.reportActivity; const deployMatch = (earnHtml || "").match(/[?&](?:amp;)?dpl=([A-Za-z0-9._-]+)/) || (earnHtml || "").match(/"buildId":"([A-Za-z0-9._-]+)"/); const deployId = deployMatch ? deployMatch[1] : ""; if (nextAction) { // 构造与 earn 页面一致的路由树 const tree = ["", { children: ["(nav)", { children: ["earn", { children: ["PAGE", {}, null, null, 4096] }, null, null, 4096] }, null, null, 4096] }, null, null, 4096 + 16]; const res = await Utils.xhr({ method: "POST", url, headers: { "accept": "text/x-component", "content-type": "text/plain;charset=UTF-8", "next-action": nextAction, "next-router-state-tree": encodeURIComponent(JSON.stringify(tree)), "referer": referer, "origin": "https://rewards.bing.com", ...(deployId ? { "x-deployment-id": deployId } : {}), ...(card.kind === "desktop_app" ? RewardsAuto.desktopAppHeaders : {}) }, data: JSON.stringify([card.hash, 11, { offerid: card.offerId, isPromotional: "$undefined", timezoneOffset: String(new Date().getTimezoneOffset()) }]) }); // 成功判定:RSC 响应中形如 "0:true" 的行(依据开源实现) if (res && /^\d+:true\s*$/m.test(res)) { Utils.log("🟢", `Server Action 领取成功(${card.offerId})`); return true; } } } catch (e2) { if (e2.message.includes("404")) { Utils.log("🟡", "Server Action ID 可能已更新,下次运行将重新探测"); RewardsAuto._serverActions = null; GM_setValue("Config.serverActions", null); } } // 策略 2: 目标链接如果是搜索页,模拟执行每日活动上报 if (card.destination && card.destination.startsWith("http")) { try { await this.reportDailyActivity(card.destination); return true; } catch (_) {} } // 策略 3: 老版 reportactivity try { const token = await this.getRewardsToken(); if (token) { await Utils.xhr({ method: "POST", url: "https://rewards.bing.com/api/reportactivity?X-Requested-With=XMLHttpRequest", headers: { "content-type": "application/x-www-form-urlencoded; charset=UTF-8", "user-agent": RewardsAuto.ua.pc, "referer": referer, "origin": "https://rewards.bing.com", "x-requested-with": "XMLHttpRequest", "RequestVerificationToken": token }, data: new URLSearchParams({ id: card.offerId, hash: card.hash, timeZone: new Date().getTimezoneOffset(), activityAmount: 1, dbs: 0, form: "", type: "", __RequestVerificationToken: token }).toString() }); return true; } } catch (_) {} Utils.log("🟡", `卡片领取未确认(${card.offerId})`); return false; }, async getSearchPage(query, isMobile = false) { const mkt = GM_getValue("Config.lock", true) ? "&mkt=zh-CN" : ""; const deviceType = isMobile ? "m" : "d"; return Utils.xhr({ url: `https://${RewardsAuto.state.host}/search?q=${encodeURIComponent(query)}&form=QBLH${mkt}`, headers: { "user-agent": isMobile ? RewardsAuto.ua.mobile : RewardsAuto.ua.pc, "cookie": `_Rwho=u=${deviceType}&ts=${RewardsAuto.state.dateNowStr}`, "referer": `https://${RewardsAuto.state.host}/?form=QBLH` } }); }, async reportSearch(html, query, isMobile = false) { try { const ig = Utils.getRandomUUID(); const mkt = GM_getValue("Config.lock", true) ? "&mkt=zh-CN" : ""; const params = `q=${encodeURIComponent(query)}&form=QBLH${mkt}`; const deviceType = isMobile ? "m" : "d"; const headers = { "content-type": "application/x-www-form-urlencoded; charset=UTF-8", "user-agent": isMobile ? RewardsAuto.ua.mobile : RewardsAuto.ua.pc, "referer": `https://${RewardsAuto.state.host}/?form=QBLH`, "cookie": `_Rwho=u=${deviceType}&ts=${RewardsAuto.state.dateNowStr}` }; await Utils.xhr({ method: "POST", url: `https://${RewardsAuto.state.host}/rewardsapp/ncheader?ver=88888888&IID=SERP.5047&IG=${ig}&ajaxreq=1`, headers, data: "wb=1%3bi%3d1%3bv%3d1" }); await Utils.xhr({ method: "POST", url: `https://${RewardsAuto.state.host}/rewardsapp/reportActivity?IG=${ig}&IID=SERP.5047&${params}&ajaxreq=1`, headers, data: `url=${encodeURIComponent(`https://${RewardsAuto.state.host}/search?${params}`)}&V=web` }); return true; } catch (e) { Utils.log("🟡", `搜索上报失败: ${e.message}`); return false; } }, async checkRegion(retryCount = 0) { if (!GM_getValue("Config.lock", true)) return true; try { const html = await Utils.xhr({ url: `https://${RewardsAuto.state.host}/` }); if (!html) { if (retryCount < 2) { Utils.log("🟡", `地区检测返回空,第${retryCount + 1}次重试...`); await Utils.randomDelay(3000, 8000); return await this.checkRegion(retryCount + 1); } Utils.log("🔴", "地区检测失败(无响应)"); return false; } const match = html.replace(/\s/g, "").match(/Region:"(.*?)"(.*?)RevIpCC:"(.*?)"/); if (match) { RewardsAuto.state.region = match[3].toUpperCase(); if (RewardsAuto.state.region !== "CN") { await this.getIPInfo(); Utils.log("🔴", `IP非大陆(${RewardsAuto.state.region}),已停止\n${RewardsAuto.state.ipInfo}`, true); return false; } Utils.log("🟢", `地区检测通过: ${RewardsAuto.state.region}`); return true; } if (retryCount < 2) { Utils.log("🟡", `地区检测格式异常,第${retryCount + 1}次重试...`); await Utils.randomDelay(3000, 8000); return await this.checkRegion(retryCount + 1); } Utils.log("🔴", "地区检测失败(格式不匹配)"); return false; } catch (e) { if (retryCount < 2) { Utils.log("🟡", `地区检测异常: ${e.message},第${retryCount + 1}次重试...`); await Utils.randomDelay(3000, 8000); return await this.checkRegion(retryCount + 1); } Utils.log("🔴", `地区检测失败: ${e.message}`); return false; } }, async getIPInfo() { try { const qryResult = await Utils.xhr({ url: "https://disp-qryapi.3g.qq.com/v1/dispatch", headers: { "referer": "https://3g.qq.com/" } }); if (qryResult && Utils.isJSON(qryResult)) { const resJSON = JSON.parse(qryResult); RewardsAuto.state.ip = (resJSON.code == 0 && resJSON.extra && resJSON.extra.ip) ? resJSON.extra.ip : ""; let rawInfo = (resJSON.code == 0 && resJSON.ipInfo) ? String(resJSON.ipInfo) : ""; rawInfo = rawInfo.replace(/[#*]+/g, " ").trim(); RewardsAuto.state.ipInfo = rawInfo ? `🌏所在地区:${rawInfo}` : ""; } } catch { console.debug("获取附加 IP 信息失败"); } }, async getHotSearchWord() { const keywords = ["天气预报", "今日新闻", "体育赛事", "股票行情", "电影推荐", "科技资讯", "美食食谱", "旅游攻略", "历史上的今天", "健康常识"]; const baseWord = keywords[Utils.randomRange(0, keywords.length - 1)]; const randomSuffix = Math.random().toString(36).slice(2, 6); let sentence = `${baseWord} ${randomSuffix}`; if (RewardsAuto.apiConfig.mode !== "offline") { if (RewardsAuto.apiConfig.wordIndex < 1 || RewardsAuto.apiConfig.wordList.length < 1) { const apiArr = RewardsAuto.apiConfig.arr; const lastApiIndex = parseInt(GM_getValue("Config.apiIndex", -1)); const filteredArr = apiArr.filter((_, index) => index !== lastApiIndex); const randomIndex = Utils.randomRange(0, filteredArr.length - 1); GM_setValue("Config.apiIndex", randomIndex); const [apiName, apiConfig] = filteredArr[randomIndex]; RewardsAuto.apiConfig.url = apiConfig.url; RewardsAuto.apiConfig.hot = apiConfig.hot; try { const hotSource = RewardsAuto.apiConfig.hot[Utils.randomRange(0, RewardsAuto.apiConfig.hot.length - 1)]; const result = await Utils.xhr({ url: RewardsAuto.apiConfig.url + hotSource }); if (result && Utils.isJSON(result)) { const res = JSON.parse(result); if (res.code == 200) { RewardsAuto.apiConfig.wordIndex = 1; RewardsAuto.apiConfig.wordList = []; for (let i = 0; i < res.data.length; i++) { RewardsAuto.apiConfig.wordList.push(res.data[i].title); } RewardsAuto.apiConfig.wordList.sort(() => Math.random() - 0.5); sentence = RewardsAuto.apiConfig.wordList[RewardsAuto.apiConfig.wordIndex]; sentence = sentence.substring(0, Utils.randomRange(20, 32)); return sentence; } } } catch (e) { Utils.log("🟡", `热搜词获取失败: ${e.message}`); } } else { RewardsAuto.apiConfig.wordIndex++; if (RewardsAuto.apiConfig.wordIndex > RewardsAuto.apiConfig.wordList.length - 1) { RewardsAuto.apiConfig.wordIndex = 0; } sentence = RewardsAuto.apiConfig.wordList[RewardsAuto.apiConfig.wordIndex]; sentence = sentence.substring(0, Utils.randomRange(20, 32)); return sentence; } Utils.log("🟡", "热搜词接口异常,已使用随机搜索词"); } return sentence; }, async checkSearchRestricted() { const info = await this.getRewardsInfo(); const currentTotal = info ? info.pc.progress : RewardsAuto.state.pcProgress; const lastTotal = RewardsAuto.state.lastSearchProgress; if (lastTotal !== -1) { if (currentTotal === lastTotal && currentTotal < RewardsAuto.state.pcMax) { RewardsAuto.state.restrictedTimes++; } else { RewardsAuto.state.restrictedTimes = 0; } } RewardsAuto.state.lastSearchProgress = currentTotal; GM_setValue("Config.lastSearchProgress", currentTotal); GM_setValue("Config.restrictedTimes", RewardsAuto.state.restrictedTimes); if (RewardsAuto.state.restrictedTimes >= 3) { Utils.log("🔴", "搜索受限或账号异常,已中断今日搜索!", true); return true; } return false; } }; const TaskManager = { signDate: 0, readDate: 0, promosDate: 0, searchDate: 0, streakDays: 0, signPoint: -1, signTimes: 0, readTimes: 0, promosTimes: 0, init() { RewardsAuto.state.dateNowNum = Utils.getTodayNum(); RewardsAuto.state.dateNowStr = Utils.getTodayStr(); const tasks = GM_getValue("Config.tasks", {}); this.signDate = tasks.sign || 0; this.readDate = tasks.read || 0; this.promosDate = tasks.promos || 0; this.searchDate = tasks.search || 0; this.streakDays = tasks.streakDays || 0; this.signPoint = GM_getValue("Config.signPoint", -1); }, save() { GM_setValue("Config.tasks", { sign: this.signDate, read: this.readDate, promos: this.promosDate, search: this.searchDate, streakDays: this.streakDays }); }, async doSign() { if (!GM_getValue("Tasks.sign", true) || this.signTimes > 2) return; if (this.signPoint >= 0 && this.signDate === RewardsAuto.state.dateNowNum) { Utils.log("✅", `签入已完成(${this.signPoint}积分)`); return; } await Utils.randomDelay(); let totalPoint = 0; let signOk = false; const appPoint = await API.signApp(); if (appPoint >= 0) { signOk = true; if (appPoint > 0) { GM_log(`📱 App签入静默成功 +${appPoint}积分`); totalPoint += appPoint; } else { GM_log("📱 App签入已确认,无新增积分"); } } await Utils.randomDelay(3000, 8000); const pcPoint = await API.signPC(); if (pcPoint >= 0) { signOk = true; Utils.log("💻", `PC签入成功!+${pcPoint}积分`); totalPoint += pcPoint; } if (signOk) { this.signPoint = totalPoint; this.signDate = RewardsAuto.state.dateNowNum; GM_setValue("Config.signPoint", totalPoint); this.save(); Utils.log("🔵", `签入任务完成!总积分 +${totalPoint}`, true); } else { this.signTimes++; Utils.log("🟡", `签入失败,稍后重试`); } }, async doRead() { if (!GM_getValue("Tasks.read", true) || this.readTimes > 2) return; if (this.readDate === RewardsAuto.state.dateNowNum) { const verifyProgress = await API.getReadProgress(); if (verifyProgress && verifyProgress.progress >= verifyProgress.max) { Utils.log("✅", `阅读任务已完成(已验证 ${verifyProgress.progress}/${verifyProgress.max})`); return; } else if (verifyProgress) { Utils.log("🟡", `阅读标记有误(${verifyProgress.progress}/${verifyProgress.max}),重置并继续`); this.readDate = 0; this.save(); } else { Utils.log("🟡", "无法验证阅读进度,跳过"); return; } } const progress = await API.getReadProgress(); if (!progress) { this.readTimes++; Utils.log("🟡", "无法获取阅读进度,稍后重试"); return; } const { progress: cur, max } = progress; Utils.log("📖", `阅读进度: ${cur}/${max}`); if (cur >= max) { this.readDate = RewardsAuto.state.dateNowNum; this.save(); Utils.log("✅", "阅读任务已完成"); return; } let successCount = 0; const maxPerDay = 10; const remaining = Math.min(max - cur, maxPerDay); Utils.log("📖", `今日还可阅读 ${remaining} 篇(上限 ${maxPerDay} 篇/天)`); for (let i = 0; i < remaining; i++) { const result = await API.doRead(progress.offerId); if (!result) { Utils.log("🟡", `阅读第 ${i + 1} 篇失败,中止`); break; } successCount++; Utils.log("📖", `阅读文章 ${i + 1}/${remaining} +${result.points}积分`); await Utils.randomDelay(3000, 8000); } if (successCount === 0) { this.readTimes++; Utils.log("🟡", "阅读全部失败,稍后重试"); return; } const verify = await API.getReadProgress(); if (verify && verify.progress >= verify.max) { this.readDate = RewardsAuto.state.dateNowNum; this.save(); Utils.log("🔵", `阅读任务完成!共 ${successCount} 篇`, true); } else { this.readTimes++; Utils.log("🟡", `阅读已执行但未完成,下次继续`); } }, async doPromos() { if (!GM_getValue("Tasks.promos", true) || this.promosTimes > 2) return; if (this.promosDate === RewardsAuto.state.dateNowNum) { Utils.log("✅", "活动卡片已完成"); return; } let ok = 0, fail = 0; // 第一优先:DAPI 任务列表(最可靠,覆盖日常任务/打卡活动/升级活动卡片) const promos = await API.getDAPIPromos(); if (Array.isArray(promos) && promos.length > 0) { const claimable = promos.filter(p => { if (p.complete) return false; // checkin 即使 hidden 也可领取;其余 hidden 不可领取 if (p.hidden && p.type !== "checkin") return false; // 搜索类需真实搜索,跳过;信息/抽奖类跳过 if (p.type === "search") return false; if (/sweepstake|refer|抽奖|兑换/i.test(p.title)) return false; return p.type === "urlreward" || p.type === "msnreadearn" || p.type === "checkin" || p.type === "streak"; }); Utils.log("🧩", `DAPI 发现 ${claimable.length} 个可执行任务`); for (const p of claimable) { if (p.type === "msnreadearn") continue; // 阅读任务由 doRead 专门处理 Utils.log(" ", `[DAPI:${p.type}] ${p.title} +${p.points}p`); await Utils.randomDelay(3000, 8000); const res = await API.claimDAPI(p.offerId, 101); if (res && (res.points > 0 || res.isDuplicate)) { ok++; Utils.log("🟢", `任务完成: ${p.title} +${res.points}p`); } else { // DAPI 不支持的类型,交由卡片策略处理 fail++; } } } // 第二优先:HTML/Flight 发现的卡片(含桌面专享、拼图、quiz 等) Utils.log("🧩", "扫描活动卡片..."); const cards = await API.discoverCards(); if (cards.length === 0) { Utils.log("✅", "未发现待处理活动卡片"); } else { Utils.log("🧩", `发现 ${cards.length} 个卡片`); for (const card of cards) { Utils.log(" ", `[${card.kind}] ${card.title} +${card.points}p`); if (card.kind === "quiz" && !GM_getValue("Tasks.quiz", true)) continue; await Utils.randomDelay(3000, 8000); const result = await API.claimCard(card); result ? ok++ : fail++; } } this.promosDate = RewardsAuto.state.dateNowNum; this.save(); Utils.log("🔵", `活动完成: ${ok}成功/${fail}失败`, true); }, async doSearch() { if (!GM_getValue("Tasks.search", true)) return; const info = await API.getRewardsInfo(); if (!info) { Utils.log("🔴", "无法获取积分信息"); return; } RewardsAuto.state.pcProgress = info.pc.progress; RewardsAuto.state.pcMax = info.pc.max; Utils.log("🔍", `搜索配额: PC ${info.pc.progress}/${info.pc.max}`); const pcDone = info.pc.progress >= info.pc.max; if (pcDone) { this.searchDate = RewardsAuto.state.dateNowNum; this.save(); Utils.log("✅", `搜索配额已满 PC: ${info.pc.progress}/${info.pc.max}`); return; } if (this.searchDate === RewardsAuto.state.dateNowNum) { Utils.log("🟡", "搜索配额未满,继续执行搜索任务"); this.searchDate = 0; } const isRestricted = await API.checkSearchRestricted(); if (isRestricted) { this.searchDate = RewardsAuto.state.dateNowNum; this.save(); return; } const limit = Utils.randomRange(4, 7); for (let i = 0; i < limit; i++) { if (RewardsAuto.state.pcProgress >= RewardsAuto.state.pcMax) break; let query; if (RewardsAuto.apiConfig.mode !== "offline") { query = await API.getHotSearchWord(); } else { query = RewardsAuto.searchPool[Utils.randomRange(0, RewardsAuto.searchPool.length - 1)]; } Utils.log("🔍", `[PC] 搜索 ${i+1}/${limit}: ${query}`); try { const html = await API.getSearchPage(query, false); if (html) { await API.reportSearch(html, query, false); RewardsAuto.state.pcProgress += 3; } } catch (e) { Utils.log("🟡", `搜索失败: ${e.message}`); } const span = Number(GM_getValue("Config.span", 30)); const wait = Utils.randomRange((span-15)*1000, (span+15)*1000); Utils.log("⏳", `等待 ${wait/1000}秒`); await Utils.delay(wait); } const finalInfo = await API.getRewardsInfo(); if (finalInfo) { const pcDone2 = finalInfo.pc.progress >= finalInfo.pc.max; if (pcDone2) { this.searchDate = RewardsAuto.state.dateNowNum; this.save(); RewardsAuto.state.restrictedTimes = 0; GM_setValue("Config.restrictedTimes", 0); GM_setValue("Config.lastSearchProgress", -1); Utils.log("🔵", `🔍 搜索任务完成!PC: ${finalInfo.pc.progress}/${finalInfo.pc.max}`, true); } else { Utils.log("🟡", `搜索已执行,配额未满 PC: ${finalInfo.pc.progress}/${finalInfo.pc.max}`); } } else { Utils.log("🟡", "搜索已执行,但无法获取最终配额状态"); } }, async doDailySet() { const today = Utils.getTodayNum(); const processedKey = "Config.dailySetProcessed"; let processed = GM_getValue(processedKey, []); if (processed.length > 0 && processed[0]?.date !== today) processed = []; const processedIds = new Set(processed.map(p => p.offerId)); Utils.log("📅", `开始执行每日活动(已处理 ${processedIds.size} 个)...`); await Utils.randomDelay(2000, 5000); let ok = 0, fail = 0; try { // 直接通过发现卡片获取未完成的 DailySet / 日常任务 const allCards = await API.discoverCards(); const dailyCards = allCards.filter(c => (c.kind === "daily" || c.offerId.includes("DailySet")) && !processedIds.has(c.offerId)); Utils.log("📅", `发现 ${dailyCards.length} 个待处理每日活动`); for (const card of dailyCards) { Utils.log("📅", `领取每日活动: ${card.title} +${card.points}p`); await Utils.randomDelay(3000, 6000); const res = await API.claimCard(card); if (res) { ok++; processedIds.add(card.offerId); } else { fail++; } } } catch (e) { Utils.log("🔴", `每日活动执行异常: ${e.message}`); } GM_setValue(processedKey, [...processedIds].map(id => ({ date: today, offerId: id }))); if (ok > 0 || fail > 0) { Utils.log("🔵", `每日活动完成: ${ok}成功/${fail}失败`, true); } else { Utils.log("✅", "无待处理的每日活动"); } }, /** * 自动领取 dashboard 上待领取的积分(如「发现 115 积分待领取」) * 依据开源实现:reportClaimAllPoints Server Action(body 为 []) */ async doClaimPoints() { try { const before = await API.getBalance(); const dashboardHtml = await Utils.xhr({ url: "https://rewards.bing.com/dashboard" }); if (!dashboardHtml) { Utils.log("✅", "无法获取 dashboard 页面"); return; } // 检测待领取积分数(兼容多种页面文案) const claimableMatch = dashboardHtml.match(/alt="可领取"[^>]*>[\s\S]*?(\d[\d,]*)/) || dashboardHtml.match(/(?:可领取|待领取|to claim)[^0-9]{0,80}(\d[\d,]*)/i) || dashboardHtml.match(/(\d[\d,]*)[^0-9]{0,60}(?:可领取|待领取|to claim)/i); const amount = claimableMatch ? (parseInt(claimableMatch[1].replace(/,/g, '')) || 0) : 0; if (amount <= 0) { Utils.log("✅", "无可领取积分"); return; } Utils.log("🎁", `发现 ${amount} 积分待领取,尝试自动领取...`); // 策略 1: reportClaimAllPoints Server Action let claimed = false; const actions = await API.resolveServerActions(dashboardHtml); const claimAction = actions.reportClaimAllPoints; if (claimAction) { try { const deployMatch = dashboardHtml.match(/[?&](?:amp;)?dpl=([A-Za-z0-9._-]+)/) || dashboardHtml.match(/"buildId":"([A-Za-z0-9._-]+)"/); const tree = ["", { children: ["(nav)", { children: ["dashboard", { children: ["PAGE", {}, null, null, 4096] }, null, null, 4096] }, null, null, 4096] }, null, null, 4096 + 16]; const res = await Utils.xhr({ method: "POST", url: "https://rewards.bing.com/dashboard", headers: { "accept": "text/x-component", "content-type": "text/plain;charset=UTF-8", "next-action": claimAction, "next-router-state-tree": encodeURIComponent(JSON.stringify(tree)), "referer": "https://rewards.bing.com/dashboard", "origin": "https://rewards.bing.com", ...(deployMatch ? { "x-deployment-id": deployMatch[1] } : {}) }, data: "[]" }); if (res && /^\d+:true\s*$/m.test(res)) claimed = true; } catch (e) { Utils.log("🟡", `reportClaimAllPoints 领取失败: ${e.message}`); } } // 策略 2: 逐个领取可领取卡片(兜底) if (!claimed) { try { const cards = await API.discoverCards(); const claimCards = cards.filter(c => /claim|领取|reward/i.test(`${c.offerId} ${c.title}`)); for (const c of claimCards) { await Utils.randomDelay(2000, 5000); if (await API.claimCard(c)) claimed = true; } } catch (_) {} } // 校验领取结果 await Utils.randomDelay(3000, 6000); const after = await API.getBalance(); const gained = (before > 0 && after > 0) ? (after - before) : 0; if (claimed || gained > 0) { Utils.log("🎁", `待领取积分已自动领取${gained > 0 ? `,+${gained}积分` : ""}`, true); } else { Utils.log("🟡", `自动领取未确认,请手动前往 dashboard 领取 ${amount} 积分`, true); } } catch (e) { Utils.log("🟡", `检测/领取积分失败: ${e.message}`); } }, async doStreak() { Utils.log("📅", "开始检测连签任务..."); try { const earnHtml = await Utils.xhr({ url: "https://rewards.bing.com/earn" }); if (!earnHtml) { Utils.log("🟡", "无法获取 earn 页面"); return; } const text = earnHtml.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' '); const streakDaysMatch = text.match(/每日连续打卡\s*(\d+)/); if (streakDaysMatch) { this.streakDays = parseInt(streakDaysMatch[1]) || 0; Utils.log("📅", `每日连续打卡:${this.streakDays} 天`); } const taskPatterns = [ { name: "必应搜索连续打卡", pattern: /必应搜索连续打卡[\s\S]*?搜索:\s*(\d+)\/(\d+)/ }, { name: "每日连续打卡活动", pattern: /每日连续打卡活动[\s\S]*?活动:\s*(\d+)\/(\d+)/ }, { name: "必应应用连续打卡", pattern: /必应应用连续打卡[\s\S]*?签到:\s*(\d+)\/(\d+)/ }, { name: "视觉搜索连续打卡", pattern: /视觉搜索连续打卡[\s\S]*?活动:\s*(\d+)\/(\d+)/ }, ]; let visualSearchDone = true; for (const { name, pattern } of taskPatterns) { const m = text.match(pattern); if (m) { const cur = parseInt(m[1]), max = parseInt(m[2]); const done = cur >= max; Utils.log(done ? "✅" : "📅", `${name}: ${cur}/${max}${done ? " 已完成" : ""}`); if (name === "视觉搜索连续打卡" && !done) visualSearchDone = false; } } if (!visualSearchDone) { Utils.log("📅", "视觉搜索未完成,尝试后台打卡..."); try { await Utils.xhr({ url: "https://www.bing.com/?features=vsstreak,vstooltip&form=ML2XES", headers: { "user-agent": RewardsAuto.ua.pc, "referer": "https://www.bing.com/" } }); Utils.log("✅", "视觉搜索打卡请求已发送(后台)"); await Utils.randomDelay(3000, 5000); } catch (e) { Utils.log("🟡", `视觉搜索打卡失败: ${e.message}`); } } const stampMatch = text.match(/(\d+)\s*个印章/) || text.match(/印章\s*(\d+)/); if (stampMatch) { Utils.log("📅", `连签奖励印章进度: ${stampMatch[1]}/12`); } Utils.log("📅", "连签任务检测完成,未完成任务将由活动卡片模块处理"); } catch (e) { Utils.log("🔴", `连签任务异常: ${e.message}`); } }, async runAll() { if (this.running) { Utils.log("🟡", "任务正在运行中,请勿重复触发"); return; } this.running = true; try { RewardsAuto.state.startTime = Utils.getTimestamp(); Utils.log("🚀", "启动全能自动化任务..."); this.init(); const startBalance = await API.getBalance(); Utils.log("📊", `初始积分: ${startBalance}`); const regionOK = await API.checkRegion(); let isTokenOK = false; if (regionOK) { isTokenOK = await API.renewToken(); if (!isTokenOK) Utils.log("🟡", "Token失败,跳过签入/阅读", true); } else { Utils.log("🔴", "IP非国内,已暂停全部任务", true); return; } const retryDelay = 60000; const maxRetries = 2; const withRetry = async (taskFn, taskName, retries = 0) => { try { const result = await taskFn(); if (result === false && retries < maxRetries) { Utils.log("🟡", `${taskName} 失败,${retryDelay/1000}秒后重试 (${retries + 1}/${maxRetries})`); await Utils.delay(retryDelay); return withRetry(taskFn, taskName, retries + 1); } return result; } catch (e) { if (retries < maxRetries) { Utils.log("🟡", `${taskName} 异常: ${e.message},${retryDelay/1000}秒后重试`); await Utils.delay(retryDelay); return withRetry(taskFn, taskName, retries + 1); } Utils.log("🔴", `${taskName} 失败: ${e.message}`); return false; } }; if (regionOK && isTokenOK) { await withRetry(() => this.doSign(), "签到"); await Utils.randomDelay(); if (!RewardsAuto.state.pc401) { await withRetry(() => this.doRead(), "阅读"); await Utils.randomDelay(); } else { Utils.log("🟡", "PC会话已过期,跳过阅读任务"); } } else if (regionOK) { await withRetry(() => this.doSign(), "签到"); await Utils.randomDelay(); } await withRetry(() => this.doPromos(), "活动卡片"); await Utils.randomDelay(); await withRetry(() => this.doSearch(), "搜索"); await withRetry(() => this.doStreak(), "连签检测"); await Utils.randomDelay(); Utils.log("📅", "开始执行每日活动任务..."); await Utils.randomDelay(); await withRetry(() => this.doDailySet(), "每日活动"); try { await this.doClaimPoints(); } catch (e) { Utils.log("🟡", `领取积分执行异常: ${e.message}`); } Utils.log("🔄", "二次扫描:检查是否有新解锁的卡片..."); await Utils.randomDelay(3000, 8000); const newCards = await API.discoverCards(); if (newCards.length > 0) { Utils.log("🧩", `二次扫描发现 ${newCards.length} 个新卡片`); let ok = 0, fail = 0; for (const card of newCards) { Utils.log(" ", `[${card.kind}] ${card.title} +${card.points}p`); if (card.kind === "quiz" && !GM_getValue("Tasks.quiz", true)) continue; await Utils.randomDelay(3000, 8000); const result = await API.claimCard(card); result ? ok++ : fail++; } Utils.log("🔵", `二次扫描完成: ${ok}成功/${fail}失败`); } else { Utils.log("✅", "二次扫描:无新卡片"); } const endTime = Utils.getTimestamp(); const totalTime = ((endTime - RewardsAuto.state.startTime) / 1000).toFixed(1); const endBalance = await API.getBalance(); const earned = (startBalance > 0 && endBalance > 0) ? (endBalance - startBalance) : 0; const info = await API.getRewardsInfo(); if (info) { const signOk = this.signDate === RewardsAuto.state.dateNowNum; const readOk = this.readDate === RewardsAuto.state.dateNowNum; const searchOk = info.pc.progress >= info.pc.max; const promosOk = this.promosDate === RewardsAuto.state.dateNowNum; let logMsg = `签到\t\t${signOk ? '✅' : '❌'}\n`; logMsg += `阅读\t\t${readOk ? '✅' : '❌'} ${info.readProgress || 0}/${info.readMax || 30}\n`; logMsg += `PC 搜索\t${searchOk ? '✅' : '⏳'} ${info.pc.progress}/${info.pc.max}\n`; logMsg += `活动卡片\t${promosOk ? '✅' : '❌'}\n`; logMsg += `连签\t\t${this.streakDays || 0} 天\n`; logMsg += `今日获取\t+${earned}\n`; logMsg += `总积分\t\t${info.balance || endBalance}`; RewardsAuto.state.sendMSG = logMsg; Utils.log("📊", logMsg, true); } else { Utils.log("🎉", `任务执行完成!用时 ${totalTime} 秒`, true); } } finally { this.running = false; } } }; GM_registerMenuCommand("🔑 手动授权", () => { GM_openInTab("https://login.live.com/oauth20_authorize.srf?client_id=0000000040170455&response_type=code&scope=service::prod.rewardsplatform.microsoft.com::MBI_SSL&redirect_uri=https://login.live.com/oauth20_desktop.srf", { active: true }); }); GM_registerMenuCommand("📋 粘贴授权码", async () => { const input = prompt("粘贴授权页面跳转后的完整URL(或直接粘贴授权码):"); if (!input?.trim()) return; const code = Utils.extractAuthCode(input); if (!code) { alert("⚠️ 未识别到有效授权码!\n请粘贴 oauth20_desktop.srf?code=... 格式的完整跳转URL。"); return; } GM_setValue("Config.code", code); GM_setValue("Config.token", false); RewardsAuto._codeExchanged = false; alert("✅ 授权码已保存,正在尝试获取 Token..."); try { const ok = await API.renewToken(); alert(ok ? "🎉 Token获取成功!授权已完成" : "⚠️ Token获取失败,授权码可能已失效,请重新授权后再试"); } catch (e) { alert(`⚠️ Token获取出错: ${e.message}`); } }); GM_registerMenuCommand("📊 Token状态", () => { const token = GM_getValue("Config.token", false); const time = GM_getValue("Config.tokenTime", 0); let ageStr = "未知"; if (time > 0) { const diff = Utils.getTimestamp() - time; const days = Math.floor(diff / 86400000); const hours = Math.floor((diff % 86400000) / 3600000); const minutes = Math.floor((diff % 3600000) / 60000); const parts = []; if (days > 0) parts.push(`${days}天`); if (hours > 0) parts.push(`${hours}小时`); parts.push(`${minutes}分钟`); ageStr = parts.join(""); } const tokenDate = time > 0 ? new Date(time).toLocaleString("zh-CN") : "未知"; alert(`Token: ${token ? "已保存" : "无"}\n获取时间: ${tokenDate}\n已过: ${ageStr}\n授权码: ${GM_getValue("Config.code", "") ? "有" : "无"}`); }); GM_registerMenuCommand("🚀 立即运行", () => TaskManager.runAll()); GM_registerMenuCommand("🔔 配置通知接口", () => { const configNames = [ { key: "Notice.wework", name: "企业微信 Webhook", hint: "群机器人webhook key" }, { key: "Notice.dingding", name: "钉钉机器人 Access Token", hint: "不加签,关键词需包含 #" }, { key: "Notice.feishu", name: "飞书机器人 Webhook", hint: "不加签,关键词需包含 #" }, { key: "Notice.pushme", name: "PushMe Key", hint: "push.i-i.me 推送key" }, { key: "Notice.bark", name: "Bark Key", hint: "bark.day.app 推送key" } ]; let configStr = "🔔 通知接口配置\n==================\n\n"; configNames.forEach((item, index) => { const saved = GM_getValue(item.key, ""); configStr += `${index + 1}. ${item.name}\n 状态: ${saved ? "✅ 已配置" : "❌ 未配置"}\n 说明: ${item.hint}\n\n`; }); configStr += "请输入要配置的编号 (1-5),或输入 0 清除所有配置:"; const choice = prompt(configStr); if (!choice) return; const num = parseInt(choice); if (num === 0) { if (confirm("确定要清除所有通知接口配置吗?")) { configNames.forEach(item => GM_setValue(item.key, "")); alert("所有通知接口配置已清除!"); } return; } if (num >= 1 && num <= 5) { const selected = configNames[num - 1]; const current = GM_getValue(selected.key, ""); const newValue = prompt(`配置 ${selected.name}\n\n当前值: ${current || "(空)"}\n\n请输入新的值:`, current); if (newValue !== null) { GM_setValue(selected.key, newValue.trim()); alert(`${selected.name} 已${newValue.trim() ? "配置" : "清除"}!`); } } else { alert("无效的编号!"); } }); GM_registerMenuCommand("📢 测试通知", () => { RewardsAuto.state.sendMSG = "🧪 这是一条测试消息\n如果你看到这条消息,说明通知接口配置成功!"; Utils.log("📢", "测试通知已发送", true); alert("测试消息已发送,请检查各通知渠道!"); }); const updateBroMenu = () => { const enabled = GM_getValue("Notice.bro", true); return enabled ? "🔕 关闭浏览器通知" : "🔔 开启浏览器通知"; }; GM_registerMenuCommand(updateBroMenu(), () => { const current = GM_getValue("Notice.bro", true); GM_setValue("Notice.bro", !current); alert(`浏览器通知已${!current ? "开启" : "关闭"}`); location.reload(); }); GM_registerMenuCommand("📋 查看通知状态", () => { const wework = GM_getValue("Notice.wework", ""); const dingding = GM_getValue("Notice.dingding", ""); const feishu = GM_getValue("Notice.feishu", ""); const pushme = GM_getValue("Notice.pushme", ""); const bark = GM_getValue("Notice.bark", ""); let status = "📊 通知接口配置状态:\n\n"; status += `企业微信: ${wework ? "✅ 已配置" : "❌ 未配置"}\n`; status += `钉钉: ${dingding ? "✅ 已配置" : "❌ 未配置"}\n`; status += `飞书: ${feishu ? "✅ 已配置" : "❌ 未配置"}\n`; status += `PushMe: ${pushme ? "✅ 已配置" : "❌ 未配置"}\n`; status += `Bark: ${bark ? "✅ 已配置" : "❌ 未配置"}\n`; alert(status); }); const init = () => { TaskManager.init(); const isKeep = GM_getValue("Config.keep", true); const checkDone = (enabled, date) => !enabled || date === RewardsAuto.state.dateNowNum; const isAllDone = checkDone(GM_getValue("Tasks.sign"), TaskManager.signDate) && checkDone(GM_getValue("Tasks.read"), TaskManager.readDate) && checkDone(GM_getValue("Tasks.promos"), TaskManager.promosDate) && checkDone(GM_getValue("Tasks.search"), TaskManager.searchDate); if (!isKeep && isAllDone) { Utils.log("💤", "今日任务已全部完成"); return; } const delay = Utils.randomRange(5000, 95000); Utils.log("⏳", `${delay/1000}秒后启动...`); setTimeout(() => TaskManager.runAll(), delay); }; GM_cookie("delete", { url: "https://bing.com", name: "_EDGE_S" }); const isBingSearchPage = typeof location !== "undefined" && location.hostname && (location.hostname === "www.bing.com" || location.hostname === "cn.bing.com"); if (!isBingSearchPage) { init(); } else { GM_log("⏭️ 当前为 Bing 搜索页,跳过脚本初始化(防止重复执行/打开多余页面)"); } })();