// ==UserScript== // @name 微软积分自动刷刷刷 // @namespace https://scriptcat.org/zh-CN/script-show-page/6241 // @version 4.0.0 // @description 每天在后台自动完成 Microsoft Rewards 任务获取积分奖励,✅签入(PC+App静默)、✅阅读、✅活动、✅搜索、✅Quiz、✅拼图、✅热搜API、✅二次扫描、✅积分通知、✅连签任务检测、✅每日活动自动上报(全程后台运行,不打开多余标签页) // @author liyan20001124-byte (modified) // @icon https://bing.com/th?id=OMR.icon-96.png&pid=Rewards // @homepage https://scriptcat.org/zh-CN/script-show-page/6241 // @supportURL https://scriptcat.org/zh-CN/script-show-page/6241 // @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/* // @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", }, 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" ], 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; } }, 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; 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"; const fetchCode = async (msg) => { GM_setValue("Config.code", ""); Utils.log("🟡", `${msg},尝试自动获取授权码...`); 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 finalUrl = res.finalUrl || ""; const code = new URL(finalUrl).searchParams.get("code"); if (code) { Utils.log("🟢", "自动获取授权码成功"); return [code]; } } catch (e) { Utils.log("🟡", `自动获取失败: ${e.message}`); } Utils.log("🟡", "请手动完成授权..."); GM_openInTab(authUrl, { active: true, insert: true, setParent: true }); if (GM_getValue("Notice.bro", true)) { try { GM_notification({ text: "完成后粘贴地址栏URL到脚本设置的「授权码链接」", title: "🟡 需要授权", timeout: 0 }); } catch(_) {} } for (let i = 0; i < 180; i++) { await Utils.delay(1000); const raw = GM_getValue("Config.code", ""); if (!raw) continue; let code = null; if (raw.includes("code=")) { try { code = new URL(raw).searchParams.get("code"); } catch {} } if (!code && raw.length > 20 && !raw.includes("http")) { code = raw.trim(); } if (code && code.length > 10) { Utils.log("🟢", "授权码获取成功"); return [code]; } } Utils.log("🔴", "授权码获取超时", true); return false; }; if (!refreshToken) { const codeMatch = await fetchCode("检测到授权码为空"); if (!codeMatch) return false; 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) { const retry = await fetchCode("授权码失效"); if (!retry) return false; return await this.renewToken(); } 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) { const retry = await fetchCode("Token失效"); if (!retry) return false; return await this.renewToken(); } 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 urlObj = new URL(fullUrl); 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; }, async getReadProgress() { 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 }; } 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() { const region = GM_getValue("Config.lock", true) ? "cn" : RewardsAuto.state.region.toLowerCase(); try { const id = Utils.getRandomUUID().replace(/-/g, '') + Utils.getRandomUUID().replace(/-/g, '').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: RewardsAuto.appConfig.offerIds.readArticle } }) })); 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(); try { const html = await Utils.xhr({ url: "https://rewards.bing.com/earn" }); if (!html) { Utils.log("🔴", "earn 页面返回空"); return cards; } const clean = html.replace(/\\"/g, '"'); const naMatch = html.match(/name":"next-action"[^}]*"value":"([a-f0-9]{40,})"/) || html.match(/next-action["']\s*:\s*["']([a-f0-9]{40,})["']/) || clean.match(/"next-action"[^"]*"([a-f0-9]{40,})"/); if (naMatch) { RewardsAuto._nextAction = naMatch[1]; Utils.log("🟢", `动态 next-action: ${naMatch[1].slice(0, 12)}…`); } 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); }; const inferKind = (offerId, title = "") => { const text = `${offerId} ${title}`; if (/quiz|trivia/i.test(text)) return "quiz"; if (/puzzle/i.test(text)) return "puzzle"; if (/image/i.test(text)) return "image_creator"; if (/explore|search/i.test(text)) return "explore_search"; if (/dailyset|daily/i.test(text)) return "daily"; if (/streak/i.test(text)) return "streak"; return "open_only"; }; const parseCard = (obj) => { const offerIdMatch = obj.match(/"offerId":"([^"]+)"/i) || obj.match(/"offerid":"([^"]+)"/i) || obj.match(/"offer_id":"([^"]+)"/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(/"completed":(true|false)/i) || obj.match(/"state":"(completed|CLAIMED)"/i); const titleMatch = obj.match(/"title":"([^"]+)"/) || obj.match(/"name":"([^"]+)"/) || obj.match(/"displayName":"([^"]+)"/); const points = pointsMatch ? parseInt(pointsMatch[1]) : 0; const isCompleted = isCompletedMatch ? (isCompletedMatch[1] === "true" || isCompletedMatch[1] === "completed" || isCompletedMatch[1] === "CLAIMED") : 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; return { title, points, offerId, hash, kind: inferKind(offerId, title) }; }; 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 || item.completed || (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; return { title, points, offerId, hash, kind: kind || inferKind(offerId, title), source: "getuserinfo", url: item.destinationUrl || item.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)); } if (cards.length > 0) { Utils.log("🧩", `getuserinfo 命中 ${cards.length} 个活动卡片`); } } } catch (e) { Utils.log("🟡", `getuserinfo 活动解析跳过: ${e.message}`); } const m1 = clean.match(/"activityCards":\[([\s\S]*?)\](?=,"|,"[a-z]|}$)/i); if (m1) { Utils.log("🧩", "命中 activityCards 数组"); const cardObjRegex = /\{[^{}]*\}/g; let m; while ((m = cardObjRegex.exec(m1[1])) !== null) { const card = parseCard(m[0]); pushCard(card); } } if (cards.length === 0) { const m2 = clean.match(/"(?:promotionCards|promotions|dailySet|cards)":\[([\s\S]*?)\](?=,"|,"[a-z]|}$)/i); if (m2) { Utils.log("🧩", "命中 promotionCards/promotions 数组"); const cardObjRegex = /\{[^{}]*\}/g; let m; while ((m = cardObjRegex.exec(m2[1])) !== null) { const card = parseCard(m[0]); pushCard(card); } } } if (cards.length === 0) { Utils.log("🧩", "全局扫描 RSC payload 中的卡片对象"); const seen = new Set(); const offerIdRe = /"offerId"|"offerid"|"offer_id"/gi; let m; while ((m = offerIdRe.exec(clean)) !== null) { let start = m.index; while (start > 0 && clean[start] !== '{') start--; if (clean[start] !== '{') continue; let depth = 0, end = start; for (let i = start; i < clean.length; i++) { if (clean[i] === '{') depth++; if (clean[i] === '}') { depth--; if (depth === 0) { end = i + 1; break; } } } if (depth !== 0) continue; const obj = clean.slice(start, end); if (!/"(?:hash|activityId|id)"\s*:/.test(obj)) continue; const key = obj.slice(0, 80); if (seen.has(key)) continue; seen.add(key); const card = parseCard(obj); pushCard(card); } } if (cards.length === 0) { Utils.log("🧩", "尝试解析 RSC flight payload"); const flightRegex = /\d+:(\{[\s\S]*?"(?:offerId|offerid)"[\s\S]*?\})\n/g; let m; const seen = new Set(); while ((m = flightRegex.exec(clean)) !== null) { const key = m[1].slice(0, 80); if (seen.has(key)) continue; seen.add(key); const card = parseCard(m[1]); pushCard(card); } } if (cards.length === 0) { Utils.log("🧩", "尝试从 HTML data 属性中提取卡片"); 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: 1, offerId, hash, kind: "open_only" }); } } if (cards.length === 0) { const snippet = clean.slice(0, 500).replace(/[\r\n]+/g, ' '); Utils.log("🟡", `所有方法均未命中,页面前500字符: ${snippet}`); } } catch (e) { Utils.log("🔴", `卡片解析失败: ${e.message}`); } return cards; }, async claimCard(card) { const nextAction = RewardsAuto._nextAction || "70babbc81d2724f60d29a95c03b3d739cba77cea92"; const url = card.url || "https://rewards.bing.com/earn"; const referer = card.url || "https://rewards.bing.com/"; 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" }, data: new URLSearchParams({ id: card.offerId, hash: card.hash, timeZone: 480, activityAmount: 1, dbs: 0, form: "", type: "", __RequestVerificationToken: token }).toString() }); return true; } } catch (e1) { Utils.log("🟡", `reportactivity+token 失败: ${card.offerId}`); } try { await Utils.xhr({ method: "POST", url, headers: { "content-type": "text/plain;charset=UTF-8", "next-action": nextAction, referer }, data: JSON.stringify([card.hash, 11, { offerid: card.offerId, isPromotional: "$undefined", timezoneOffset: "-480" }]) }); return true; } catch (e2) { Utils.log("🟡", `Server Action 失败: ${card.offerId}`); } try { await this.reportActivity(card.offerId, card.hash, referer); return true; } catch (e3) { Utils.log("🟡", `reportActivity 失败: ${card.offerId}`); } try { await this.reportActivity(card.offerId, "1", referer); return true; } catch (e4) { 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(); 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; } Utils.log("🧩", "扫描活动卡片..."); const cards = await API.discoverCards(); if (cards.length === 0) { this.promosDate = RewardsAuto.state.dateNowNum; this.save(); Utils.log("✅", "无新活动卡片"); return; } Utils.log("🧩", `发现 ${cards.length} 个卡片`); let ok = 0, fail = 0; 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("🟡", "搜索已执行,但无法获取最终配额状态"); } }, // 【核心修改】每日活动:纯 XHR,不打开任何页面 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(3000, 8000); let ok = 0, fail = 0; 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)) { Utils.log("🟡", "每日活动:getuserinfo 返回非 JSON,跳过"); return; } const data = JSON.parse(userInfo); const dashboard = data.dashboard || data; const now = new Date(); const todayKeys = [ `${String(now.getMonth() + 1).padStart(2, "0")}/${String(now.getDate()).padStart(2, "0")}/${now.getFullYear()}`, `${now.getMonth() + 1}/${now.getDate()}/${now.getFullYear()}` ]; let dailyItems = []; const dailySetPromotions = dashboard.dailySetPromotions || {}; for (const dateKey of todayKeys) { if (Array.isArray(dailySetPromotions[dateKey])) { dailyItems = dailySetPromotions[dateKey]; break; } } Utils.log("📅", `发现 ${dailyItems.length} 个每日活动`); const rewardsToken = await API.getRewardsToken(); for (const item of dailyItems) { const offerId = item.offerId || item.offerid || item.id || item.name; const hash = item.hash || item.activityId; const title = item.title || item.name || "每日活动"; const points = Number(item.points ?? item.pointProgressMax ?? 0); const doneMax = Number(item.pointProgressMax || 0); const doneCur = Number(item.pointProgress || 0); const isCompleted = item.isCompleted || item.complete || item.completed || (doneMax > 0 && doneCur >= doneMax); if (!offerId || !hash || isCompleted) continue; if (processedIds.has(offerId)) continue; Utils.log("📅", `领取每日活动: ${title} +${points}p`); await Utils.randomDelay(3000, 8000); try { const params = new URLSearchParams({ id: offerId, hash: hash, timeZone: 480, activityAmount: 1, dbs: 0, form: "", type: "" }); if (rewardsToken) params.set("__RequestVerificationToken", rewardsToken); const headers = { "content-type": "application/x-www-form-urlencoded; charset=UTF-8", "user-agent": RewardsAuto.ua.pc, "referer": "https://rewards.bing.com/", "origin": "https://rewards.bing.com", "x-requested-with": "XMLHttpRequest" }; if (rewardsToken) headers["RequestVerificationToken"] = rewardsToken; await Utils.xhr({ method: "POST", url: "https://rewards.bing.com/api/reportactivity?X-Requested-With=XMLHttpRequest", headers, data: params.toString(), anonymous: false }); ok++; processedIds.add(offerId); } catch (e) { fail++; Utils.log("🟡", `每日活动领取失败: ${title} - ${e.message}`); } } } 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("✅", "无待处理的每日活动"); } }, // 【核心修改】领取积分:仅检测 + 通知,不打开页面 async doClaimPoints() { try { 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,]*)/i); if (!claimableMatch) { Utils.log("✅", "无可领取积分"); return; } const amount = parseInt(claimableMatch[1].replace(/,/g, '')) || 0; if (amount > 0) { Utils.log("🎁", `发现 ${amount} 积分待领取(请手动前往 dashboard 领取)`, true); } else { Utils.log("✅", "可领取积分为 0"); } } 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; } } // 【核心修改】视觉搜索改为后台 XHR 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("📋 粘贴授权码", () => { const code = prompt("粘贴授权页面跳转后的完整URL:"); if (code?.trim()) { GM_setValue("Config.code", code.trim()); alert("已保存!"); } }); 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 搜索页,跳过脚本初始化(防止重复执行/打开多余页面)"); } })();