// ==UserScript== // @name 微软积分商城签到(AI重构版) // @namespace https://scriptcat.org/zh-CN/script-show-page/8249 // @version 3.0.3 // @description 每天在后台自动完成 Microsoft Rewards 任务获取积分奖励,✅签入(PC+App静默)、✅阅读、✅活动、✅搜索、✅Quiz、✅拼图、✅Image Creator、✅热搜API、✅二次扫描、✅积分通知、✅拟人化搜索、✅PC/移动端分离签到 // @author liyan20001124-byte // @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/* // @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 此脚本为开源免费使用,请勿购买,官方地址:https://scriptcat.org/zh-CN/script-show-page/6241 // ==/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'; // ===================================================== // 【改动说明】授权码捕获逻辑,继承自 Untitled-1.txt // 当用户在 login.live.com 完成授权时自动捕获 code // ===================================================== 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); GM_notification({ title: "🟢 授权成功", text: "授权码已捕获,可关闭此页" }); try { history.replaceState({}, "", "about:blank"); } catch(_) {} setTimeout(() => { try { window.close(); } catch(_) {} }, 200); } return; } // ===================================================== // 【新增】统一错误类型 // 【说明】用于区分 HTTP 错 / 网络错 / 超时 / 认证失败 // - kind: http | network | timeout | abort | parse | auth // - status: HTTP 状态码(0 表示非 HTTP 层错误) // ===================================================== class RewardsError extends Error { constructor(message, { status = 0, kind = "unknown", cause = null } = {}) { super(message); this.name = "RewardsError"; this.status = status; this.kind = kind; this.cause = cause; } } // ===================================================== // 核心配置与状态管理 // ===================================================== const RewardsAuto = { // 浏览器 UA 配置 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", }, // App 端配置 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", "天气预报", "今日新闻热点", "美食食谱家常菜", "旅游攻略", "健康养生知识", "科技资讯", "电影推荐", "股票行情", "体育赛事", "历史上的今天" ], // Copilot 提示词 copilotPrompts: [ "Give me three quick dinner ideas using chicken and pasta.", "Explain quantum entanglement in a paragraph for a beginner.", "What's a fun weekend project I can do with a Raspberry Pi?", "Suggest a 7-day Tokyo travel itinerary focused on food.", "Help me write a polite reminder email to a coworker.", ], // 热搜API配置 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, }, // Image Creator 提示词 imagePrompts: [ "A peaceful forest at dawn with golden sunlight filtering through trees", "Cozy coffee shop interior with warm lighting and wooden tables", "Futuristic city skyline at dusk with flying cars and neon lights", "Japanese garden with koi pond and cherry blossoms", "Mountain landscape with snow-capped peaks and crystal lake" ], // 跳过规则 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, mobileProgress: 0, mobileMax: 60, sendMSG: "", lastSearchProgress: -1, restrictedTimes: 0, ip: "", ipInfo: "", startTime: 0, } }; // ===================================================== // 通知接口配置 // 【改动说明】由 key 改为 keyName,运行时按名读取,配置修改后无需刷新 // ===================================================== const Webhooks = [ { name: "企业微信", keyName: "Notice.wework", url: "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=", msg: { "msgtype": "text", "text": { get content() { return `> ${new Date().toLocaleString()}\n\n ## ${GM_info.script.name}\n ${RewardsAuto.state.sendMSG}` } }, }, }, { name: "钉钉", keyName: "Notice.dingding", url: "https://oapi.dingtalk.com/robot/send?access_token=", 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: "飞书", keyName: "Notice.feishu", url: "https://open.feishu.cn/open-apis/bot/v2/hook/", 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", keyName: "Notice.pushme", url: "https://push.i-i.me/?push_key=", msg: { "type": "markdown", "title": `${GM_info.script.name}[#rewards!https://rewards.bing.com/rewards.png]`, get content() { return `\n ${RewardsAuto.state.sendMSG}` } }, }, { name: "Bark", keyName: "Notice.bark", url: "https://api.day.app/", msg: { "group": "rewards", "icon": "https://rewards.bing.com/rewards.png", "title": GM_info.script.name, get markdown() { return `\n ${RewardsAuto.state.sendMSG}` } }, }, ]; // ===================================================== // 工具类 // 【改动说明】 // - xhr 加固:单次结算保护 + RewardsError 分类 // - 新增 xhrRetry:带退避重试(仅对可重试错误) // - 新增 safeRun:任务级错误隔离 // - 新增 fmtErr:统一错误格式化 // ===================================================== const Utils = { log(icon, msg, push = false) { GM_log(`${icon} ${msg}`); if (push && GM_getValue("Notice.bro", true)) { GM_notification({ title: GM_info.script.name + ` ${icon}`, text: msg, onclick: () => GM_openInTab("https://rewards.bing.com/dashboard", { active: true }) }); } if (push) { RewardsAuto.state.sendMSG = `${icon} ${msg}`; this.sendWebhook(); } }, // 发送webhook通知(allSettled,一个失败不影响其它) async sendWebhook() { const jobs = Webhooks.map(async (i) => { const rawKey = GM_getValue(i.keyName, false); // 运行时读取,配置修改立即生效 if (!rawKey) return; const safeKey = String(rawKey).trim(); const targetUrl = safeKey.startsWith("http") ? safeKey : i.url + safeKey; try { await this.xhr({ method: "POST", url: targetUrl, headers: { "content-type": "application/json; charset=UTF-8" }, data: JSON.stringify(i.msg), }); GM_log(`🔵 「${i.name}」消息推送完成`); } catch (e) { GM_log(`🔴 「${i.name}」消息推送出错: ${this.fmtErr(e)}`); } }); await Promise.allSettled(jobs); }, // ===================================================== // 网络请求封装(加固版) // 【特性】 // - 单次结算保护(settled 标志) // - 错误分类:RewardsError(kind, status) // - 支持重定向(301/302/307/308) // - 15 秒默认超时 // ===================================================== xhr(options) { return new Promise((resolve, reject) => { const start = Date.now(); let settled = false; const finish = (fn, arg) => { if (settled) return; settled = true; fn(arg); }; GM_xmlhttpRequest({ ...options, timeout: options.timeout || 15000, onload: (res) => { const cost = ((Date.now() - start) / 1000).toFixed(2); if (res.status >= 200 && res.status < 300) { finish(resolve, res.responseText); } else if ([301, 302, 307, 308].includes(res.status)) { const m = res.responseHeaders?.match(/Location:\s*(.*?)\s*\r?\n/i); finish(resolve, m ? m[1] : false); } else { finish(reject, new RewardsError(`HTTP ${res.status},用时 ${cost} 秒`, { status: res.status, kind: "http" })); } }, onerror: (err) => { const cost = ((Date.now() - start) / 1000).toFixed(2); finish(reject, new RewardsError(`${err?.error || "网络错误"},用时 ${cost} 秒`, { kind: "network", cause: err })); }, ontimeout: () => { const cost = ((Date.now() - start) / 1000).toFixed(2); finish(reject, new RewardsError(`请求超时,用时 ${cost} 秒`, { kind: "timeout" })); }, onabort: () => finish(reject, new RewardsError("请求被中止", { kind: "abort" })) }); }); }, // ===================================================== // 带退避重试的 xhr // 【默认重试错误】网络错误、超时、408、429、5xx // 【不重试】4xx(认证/参数错误,重试无意义) // ===================================================== async xhrRetry(options, { retries = 2, delays = [1500, 4000, 8000], retryOn = ["network", "timeout", 408, 429, 500, 502, 503, 504] } = {}) { let lastErr; for (let attempt = 0; attempt <= retries; attempt++) { try { return await this.xhr(options); } catch (e) { lastErr = e; const isRetryable = (e instanceof RewardsError) ? (retryOn.includes(e.kind) || retryOn.includes(e.status)) : true; if (!isRetryable || attempt === retries) break; await this.delay(delays[attempt] ?? 3000); } } throw lastErr; }, // ===================================================== // 任务级错误隔离 // 【说明】任何子任务抛错都被捕获并记录,不影响后续任务 // ===================================================== async safeRun(label, fn, fallback = null) { try { return await fn(); } catch (e) { GM_log(`🔴 [${label}] ${this.fmtErr(e)}`); return fallback; } }, // 统一错误格式化 fmtErr(e) { if (e instanceof RewardsError) { return `[${e.kind}${e.status ? " " + e.status : ""}] ${e.message}`; } return e?.message || String(e); }, // 随机数范围生成 randomRange(min, max) { return Math.floor(Math.random() * (max - min + 1) + min); }, // 时间戳获取 getTimestamp() { return Date.now(); }, // 获取今日日期数字格式 YYYYMMDD getTodayNum() { const d = new Date(); return Number(`${d.getFullYear()}${String(d.getMonth()+1).padStart(2,"0")}${String(d.getDate()).padStart(2,"0")}`); }, // 获取今日日期字符串格式 M/D/YYYY getTodayStr() { const d = new Date(); return `${d.getMonth()+1}/${d.getDate()}/${d.getFullYear()}`; }, // 生成随机 UUID getRandomUUID() { return crypto.randomUUID().replace(/-/g, "").toUpperCase(); }, // JSON 验证 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)); }, // 等待 DOM 元素出现 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); }); } }; // ===================================================== // API 交互层 // 【改动说明】 // - getToken 明确区分「认证失败=false」与「网络异常=null」 // - 使用 xhrRetry 提升网络容错 // ===================================================== const API = { // Token 获取 // 返回值语义: // true —— 成功 // false —— 认证层面失败(会清空本地 token) // null —— 网络层面失败(保留 token) async getToken(url, maxRetries = 3) { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { const res = await Utils.xhrRetry({ url }, { retries: 1 }); 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) { const isAuth = (e instanceof RewardsError) && (e.status === 400 || e.status === 401); if (isAuth) { GM_setValue("Config.token", false); GM_setValue("Config.code", ""); return false; } if (attempt < maxRetries) { await Utils.delay(3210 * attempt); continue; } Utils.log("🔴", `Token请求失败: ${Utils.fmtErr(e)}`); return null; } } return null; }, // Token 续期 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); // Token 超过 7 天提前续期 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 }); GM_notification({ text: "完成后粘贴地址栏URL到脚本设置的「授权码链接」", title: "🟡 需要授权", timeout: 0 }); // 等待用户粘贴授权码(最长 3 分钟) for (let i = 0; i < 180; i++) { await Utils.delay(1000); const code = GM_getValue("Config.code", ""); const match = code?.match(/M\.[^&]+/); if (match) { Utils.log("🟢", "授权码获取成功"); return [decodeURIComponent(match[0])]; } } 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 === null) return false; 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 === null) return false; if (!token) { const retry = await fetchCode("Token失效"); if (!retry) return false; return await this.renewToken(); } return true; } }, // 获取积分仪表盘信息 async getRewardsInfo(maxRetries = 3) { // 优先使用 DAPI API 查询 if (RewardsAuto.state.token) { const apiResult = await Utils.safeRun("DAPI查询", () => this.getSearchQuotaFromAPI(), false); if (apiResult) { Utils.log("🟢", "使用 DAPI API 获取配额成功"); return apiResult; } Utils.log("🟡", "DAPI API 查询失败,回退到页面解析"); } // 回退到页面解析 for (let attempt = 1; attempt <= maxRetries; attempt++) { try { const html = await Utils.xhrRetry({ url: "https://rewards.bing.com/earn" }, { retries: 1 }); const clean = html.replace(/\\"/g, '"'); let balance = 0; let pcMax = 60, pcCur = 0, mobMax = 0, mobCur = 0; let dailyOffer = 0; // 方法1: 从RSC JSON数据中解析 const rscMatch = clean.match(/"pointsCounters":\{[^}]*"pc":\{"max":(\d+),"progress":(\d+)\}[^}]*"mobile":\{"max":(\d+),"progress":(\d+)\}[^}]*"dailyOffer":(\d+)[^}]*"totalPoints":(\d+)/); if (rscMatch) { pcMax = parseInt(rscMatch[1]); pcCur = parseInt(rscMatch[2]); mobMax = parseInt(rscMatch[3]); mobCur = parseInt(rscMatch[4]); dailyOffer = parseInt(rscMatch[5]); balance = parseInt(rscMatch[6]); } else { // 方法2: 传统格式解析 const balMatch = clean.match(/"balance":(\d+)/) || clean.match(/"availablePoints":(\d+)/) || clean.match(/"totalPoints":(\d+)/); if (balMatch) balance = parseInt(balMatch[1]); const idx = clean.indexOf('"pointsCounters":{'); if (idx !== -1) { let start = idx + 17, 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; } } } 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; } catch {} } } if (balance === 0) { const balMatch2 = clean.match(/"balance":(\d+)/) || clean.match(/"availablePoints":(\d+)/); if (balMatch2) balance = parseInt(balMatch2[1]); } // 解析今日积分明细 const todayDetails = []; const activityCardsMatch = clean.match(/"activityCards":\[([^\]]+)\]/); if (activityCardsMatch) { try { const cardsStr = activityCardsMatch[1]; 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 searchHtmlMatch = clean.match(/必应搜索.*?]*>(\d+)<\/span>.*?]*>\/(\d+)<\/span>/); if (searchHtmlMatch) { todayDetails.push({ title: '必应搜索', points: parseInt(searchHtmlMatch[1]), max: parseInt(searchHtmlMatch[2]) }); } const searchRscMatch = clean.match(/"combinedSearch":\{[^}]*"progress":(\d+)[^}]*"max":(\d+)/); if (searchRscMatch && !todayDetails.some(d => d.title === '必应搜索')) { todayDetails.push({ title: '必应搜索', points: parseInt(searchRscMatch[1]), max: parseInt(searchRscMatch[2]) }); } 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]); } 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("🔴", `仪表盘获取失败: ${Utils.fmtErr(e)}`); return false; } } return false; }, // App 端签入任务 async signApp() { const region = GM_getValue("Config.lock", true) ? "cn" : RewardsAuto.state.region.toLowerCase(); try { const res = await 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 ${RewardsAuto.state.token}`, "x-rewards-appid": RewardsAuto.appConfig.rewardsAppId, "x-rewards-ismobile": "true", "x-rewards-country": region, "x-rewards-partnerid": "startapp", "x-rewards-flights": "rwgobig" }, data: JSON.stringify({ amount: 1, id: Utils.getRandomUUID(), type: 103, country: region, channel: RewardsAuto.appConfig.channel }) }); if (Utils.isJSON(res)) return JSON.parse(res).response?.activity?.p || 0; } catch (e) { Utils.log("🔴", `App签入失败: ${Utils.fmtErr(e)}`); } return -1; }, // PC 端签入任务 async signPC() { try { const res = await Utils.xhr({ method: "POST", url: "https://rewards.bing.com/api/reportactivity?X-Requested-With=XMLHttpRequest", headers: { "content-type": "application/x-www-form-urlencoded", "user-agent": RewardsAuto.ua.pc, "referer": "https://rewards.bing.com/" }, data: "id=Gamification_DailyCheckIn&hash=1&activityAmount=1" }); if (Utils.isJSON(res)) { const data = JSON.parse(res); return data.points || 0; } } catch (e) { Utils.log("🟡", `PC签入失败: ${Utils.fmtErr(e)}`); } return -1; }, // App 端活动卡片任务 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(), type: type, channel: RewardsAuto.appConfig.channel }; if (offerid) { body.attributes = { offerid: offerid }; } try { const res = await 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 ${RewardsAuto.state.token}`, "x-rewards-appid": RewardsAuto.appConfig.rewardsAppId, "x-rewards-ismobile": "true", "x-rewards-country": region }, data: JSON.stringify(body) }); if (Utils.isJSON(res)) { const data = JSON.parse(res); const points = data.response?.activity?.p || 0; const isDuplicate = data.response?.isDuplicate || false; const balance = data.response?.balance || 0; return { points, isDuplicate, balance }; } } catch (e) { Utils.log("🔴", `App活动失败(${offerid}): ${Utils.fmtErr(e)}`); } return null; }, // 获取阅读进度 async getReadProgress() { try { const res = await 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 ${RewardsAuto.state.token}`, "x-rewards-appid": RewardsAuto.appConfig.rewardsAppId, "x-rewards-ismobile": "true" } }); if (Utils.isJSON(res)) { const promos = JSON.parse(res).response?.promotions || []; const task = promos.find(x => x.attributes?.offerid === RewardsAuto.appConfig.offerIds.readArticle); if (task && task.attributes) return { progress: parseInt(task.attributes.progress) || 0, max: parseInt(task.attributes.max) || 30 }; } } catch (e) { Utils.log("🔴", `阅读进度获取失败: ${Utils.fmtErr(e)}`); } return false; }, // 通过 DAPI API 获取搜索配额 async getSearchQuotaFromAPI() { try { const res = await 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 ${RewardsAuto.state.token}`, "x-rewards-appid": RewardsAuto.appConfig.rewardsAppId, "x-rewards-ismobile": "true" } }); if (Utils.isJSON(res)) { const data = JSON.parse(res); const response = data.response || {}; const counters = response.counters || response.userStatus?.counters; if (!counters) { Utils.log("🟡", "DAPI 未返回 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; } let mobCur = 0, mobMax = 0; if (counters.mobileSearch && counters.mobileSearch.length > 0) { mobCur = counters.mobileSearch[0].pointProgress || 0; mobMax = counters.mobileSearch[0].pointProgressMax || 0; } if (pcMax === 0 && mobMax === 0) { Utils.log("🟡", "DAPI 返回配额 0/0,回退到页面解析"); return false; } const balance = response.balance || response.userStatus?.availablePoints || 0; Utils.log("📊", `DAPI查询: PC ${pcCur}/${pcMax}, Mobile ${mobCur}/${mobMax}, 积分 ${balance}`); return { balance: balance, pc: { progress: pcCur, max: pcMax }, mobile: { progress: mobCur, max: mobMax }, dailyOffer: 0, todayDetails: [], history: null }; } } catch (e) { Utils.log("🟡", `DAPI查询失败: ${Utils.fmtErr(e)}`); } return false; }, // 执行阅读 async doRead() { const region = GM_getValue("Config.lock", true) ? "cn" : RewardsAuto.state.region.toLowerCase(); try { await 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 ${RewardsAuto.state.token}`, "x-rewards-appid": RewardsAuto.appConfig.rewardsAppId, "x-rewards-ismobile": "true", "x-rewards-country": region }, data: JSON.stringify({ amount: 1, country: region, id: Utils.getRandomUUID(), type: 101, attributes: { offerid: RewardsAuto.appConfig.offerIds.readArticle } }) }); return true; } catch (e) { Utils.log("🔴", `阅读请求失败: ${Utils.fmtErr(e)}`); return false; } }, // 活动卡片发现 async discoverCards() { const cards = []; try { const html = await Utils.xhrRetry({ url: "https://rewards.bing.com/earn" }, { retries: 1 }); const clean = html.replace(/\\"/g, '"'); // 方法1: 从RSC数据中解析activityCards数组 const activityCardsMatch = clean.match(/"activityCards":\[([\s\S]*?)\](?=,"|$)/); if (activityCardsMatch) { try { const cardsStr = activityCardsMatch[1]; const cardObjRegex = /\{[^{}]*\}/g; let cardObjMatch; while ((cardObjMatch = cardObjRegex.exec(cardsStr)) !== null) { const cardObj = cardObjMatch[0]; const offerIdMatch = cardObj.match(/"offerId":"([^"]+)"/); const hashMatch = cardObj.match(/"hash":"([^"]+)"/); const pointsMatch = cardObj.match(/"points":(\d+)/); const isCompletedMatch = cardObj.match(/"isCompleted":(true|false)/); if (!offerIdMatch || !hashMatch) continue; const offerId = offerIdMatch[1]; const hash = hashMatch[1]; const points = pointsMatch ? parseInt(pointsMatch[1]) : 0; const isCompleted = isCompletedMatch ? isCompletedMatch[1] === "true" : false; if (isCompleted || points <= 0) continue; const titleMatch = cardObj.match(/"title":"([^"]+)"/); let title = titleMatch ? titleMatch[1] : ""; const skip = RewardsAuto.skipPatterns.some(p => title.toLowerCase().includes(p.toLowerCase()) || offerId.toLowerCase().includes(p.toLowerCase()) ); if (skip) continue; let kind = "unknown"; if (/quiz|trivia/i.test(offerId)) kind = "quiz"; else if (/puzzle/i.test(offerId)) kind = "puzzle"; else if (/image/i.test(offerId)) kind = "image_creator"; else if (/explore|search/i.test(offerId)) kind = "explore_search"; else if (/dailyset|daily/i.test(offerId)) kind = "daily"; else if (/streak/i.test(offerId)) kind = "streak"; else kind = "open_only"; cards.push({ title, points, offerId, hash, kind }); } } catch (e) { Utils.log("🟡", `RSC卡片解析异常: ${e.message}`); } } // 方法2: 传统正则匹配(备用) if (cards.length === 0) { const objRegex = /\{[^{}]*"offerId":"[^"]+"[^{}]*"hash":"[^"]+"[^{}]*\}/g; let objMatch; while ((objMatch = objRegex.exec(clean)) !== null) { const cardObj = objMatch[0]; const offerIdMatch = cardObj.match(/"offerId":"([^"]+)"/); const hashMatch = cardObj.match(/"hash":"([^"]+)"/); const pointsMatch = cardObj.match(/"points":(\d+)/); const isCompletedMatch = cardObj.match(/"isCompleted":(true|false)/); const titleMatch = cardObj.match(/"title":"([^"]+)"/); if (!offerIdMatch || !hashMatch) continue; const offerId = offerIdMatch[1]; const hash = hashMatch[1]; const points = pointsMatch ? parseInt(pointsMatch[1]) : 0; const isCompleted = isCompletedMatch ? isCompletedMatch[1] === "true" : false; const title = titleMatch ? titleMatch[1] : ""; if (isCompleted || points <= 0) continue; const skip = RewardsAuto.skipPatterns.some(p => title.toLowerCase().includes(p.toLowerCase()) || offerId.toLowerCase().includes(p.toLowerCase()) ); if (skip) continue; let kind = "unknown"; if (/quiz|trivia/i.test(offerId)) kind = "quiz"; else if (/puzzle/i.test(offerId)) kind = "puzzle"; else if (/image/i.test(offerId)) kind = "image_creator"; else if (/explore|search/i.test(offerId)) kind = "explore_search"; else if (/dailyset|daily/i.test(offerId)) kind = "daily"; else if (/streak/i.test(offerId)) kind = "streak"; else kind = "open_only"; cards.push({ title, points: parseInt(points), offerId, hash, kind }); } } } catch (e) { Utils.log("🔴", `卡片解析失败: ${Utils.fmtErr(e)}`); } return cards; }, // 领取卡片奖励 async claimCard(card) { try { await Utils.xhr({ method: "POST", url: "https://rewards.bing.com/earn", headers: { "content-type": "text/plain;charset=UTF-8", "next-action": "70babbc81d2724f60d29a95c03b3d739cba77cea92", "referer": "https://rewards.bing.com/earn" }, data: JSON.stringify([card.hash, 11, { offerid: card.offerId, isPromotional: "$undefined", timezoneOffset: "-480" }]) }); return true; } catch (e) { Utils.log("🟡", `卡片领取失败: ${Utils.fmtErr(e)}`); 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 clean = html.replace(/\s/g, ""); const ig = clean.match(/,IG:"([^"]+)"/)?.[1] || Utils.getRandomUUID(); const data = clean.match(/class="b_algo(.*?)href="(.*?)"h="ID=(.*?)">(.*?)<\/h2/); 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` }); if (data) { await Utils.xhr({ url: `https://${RewardsAuto.state.host}/fd/ls/GLinkPingPost.aspx?IG=${ig}&ID=${data[3]}&url=${data[2]}`, headers }); } return true; } catch (e) { Utils.log("🟡", `搜索上报失败: ${Utils.fmtErr(e)}`); return false; } }, // 地区检测(增加重试机制和IP信息获取) async checkRegion(retryCount = 0) { if (!GM_getValue("Config.lock", true)) return true; try { const html = await Utils.xhrRetry({ url: `https://${RewardsAuto.state.host}/` }, { retries: 1 }); if (!html) { if (retryCount < 2) { Utils.log("🟡", `地区检测返回空,第${retryCount + 1}次重试...`); await Utils.randomDelay(3000, 5000); 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, 5000); return await this.checkRegion(retryCount + 1); } Utils.log("🔴", "地区检测失败(格式不匹配)"); return false; } catch (e) { if (retryCount < 2) { Utils.log("🟡", `地区检测异常: ${Utils.fmtErr(e)},第${retryCount + 1}次重试...`); await Utils.randomDelay(3000, 5000); return await this.checkRegion(retryCount + 1); } Utils.log("🔴", `地区检测失败: ${Utils.fmtErr(e)}`); return false; } }, // 获取IP详细信息 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("🟡", `热搜词获取失败: ${Utils.fmtErr(e)}`); } } 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 currentTotalSearch = RewardsAuto.state.pcProgress + RewardsAuto.state.mobileProgress; const lastTotalSearch = RewardsAuto.state.lastSearchProgress; if (lastTotalSearch !== -1) { if (currentTotalSearch === lastTotalSearch && currentTotalSearch < (RewardsAuto.state.pcMax + RewardsAuto.state.mobileMax)) { RewardsAuto.state.restrictedTimes++; } else { RewardsAuto.state.restrictedTimes = 0; } } RewardsAuto.state.lastSearchProgress = currentTotalSearch; GM_setValue("Config.lastSearchProgress", currentTotalSearch); GM_setValue("Config.restrictedTimes", RewardsAuto.state.restrictedTimes); if (RewardsAuto.state.restrictedTimes >= 3) { Utils.log("🔴", "搜索受限或账号异常,已中断今日搜索!", true); return true; } return false; } }; // ===================================================== // 任务管理器 // 【改动说明】runAll 使用 Utils.safeRun 做任务级隔离, // 保证任一子任务失败不影响后续流程;reportSummary 独立成方法。 // ===================================================== const TaskManager = { signDate: 0, readDate: 0, promosDate: 0, searchDate: 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.signPoint = GM_getValue("Config.signPoint", -1); }, save() { GM_setValue("Config.tasks", { sign: this.signDate, read: this.readDate, promos: this.promosDate, search: this.searchDate }); }, // 签入任务处理 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; // App 端签到(静默执行) const appPoint = await Utils.safeRun("App签入", () => API.signApp(), -1); if (appPoint > 0) { GM_log(`📱 App签入静默成功 +${appPoint}积分`); totalPoint += appPoint; } // PC 端签到 await Utils.randomDelay(2000, 4000); const pcPoint = await Utils.safeRun("PC签入", () => API.signPC(), -1); if (pcPoint >= 0) { Utils.log("💻", `PC签入成功!+${pcPoint}积分`); totalPoint += pcPoint; } if (totalPoint > 0) { 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) { Utils.log("✅", "阅读任务已完成"); return; } const progress = await Utils.safeRun("阅读进度查询", () => API.getReadProgress(), false); if (!progress) { this.readTimes++; return; } const { progress: cur, max } = progress; Utils.log("📖", `阅读进度: ${cur}/${max}`); if (cur >= max) { this.readDate = RewardsAuto.state.dateNowNum; this.save(); Utils.log("✅", "阅读任务已完成"); return; } const needed = Math.ceil((max - cur) / 3); for (let i = 0; i < needed; i++) { await Utils.safeRun(`阅读#${i+1}`, () => API.doRead()); Utils.log("📖", `阅读 ${i+1}/${needed}`); await Utils.randomDelay(3000, 7000); } this.readDate = RewardsAuto.state.dateNowNum; this.save(); Utils.log("🔵", "阅读任务完成!", true); }, // 活动卡片处理 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 Utils.safeRun("扫描活动卡片", () => 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(2000, 4000); const result = await Utils.safeRun("领取卡片", () => API.claimCard(card), false); 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 Utils.safeRun("获取积分信息", () => API.getRewardsInfo(), false); if (!info) { Utils.log("🔴", "无法获取积分信息"); return; } RewardsAuto.state.pcProgress = info.pc.progress; RewardsAuto.state.pcMax = info.pc.max; RewardsAuto.state.mobileProgress = info.mobile.progress; RewardsAuto.state.mobileMax = info.mobile.max; Utils.log("🔍", `配额: PC ${info.pc.progress}/${info.pc.max}, Mobile ${info.mobile.progress}/${info.mobile.max}`); if (info.mobile.max === 0) { Utils.log("ℹ️", "你的账号没有移动端搜索配额,将只执行 PC 端搜索"); } const hasMobile = info.mobile.max > 0; const pcDone = info.pc.progress >= info.pc.max; const mobileDone = !hasMobile || info.mobile.progress >= info.mobile.max; if (pcDone && mobileDone) { this.searchDate = RewardsAuto.state.dateNowNum; this.save(); const mobileReport = hasMobile ? `\n📱 移动端: ${info.mobile.progress}/${info.mobile.max}` : ""; Utils.log("✅", `搜索配额已满\n💻 PC端: ${info.pc.progress}/${info.pc.max}${mobileReport}`); return; } if (this.searchDate === RewardsAuto.state.dateNowNum) { Utils.log("🟡", "搜索配额未满,继续执行搜索任务"); this.searchDate = 0; } const isRestricted = await Utils.safeRun("搜索受限检测", () => API.checkSearchRestricted(), false); if (isRestricted) { this.searchDate = RewardsAuto.state.dateNowNum; this.save(); return; } // 执行搜索(随机 4-7 次,硬上限 10 次防死循环) const limit = Utils.randomRange(4, 7); const maxIterations = Math.min(limit + 3, 10); const canSearchMobile = RewardsAuto.state.mobileMax > 0; for (let i = 0; i < maxIterations; i++) { if (RewardsAuto.state.pcProgress >= RewardsAuto.state.pcMax && (!canSearchMobile || RewardsAuto.state.mobileProgress >= RewardsAuto.state.mobileMax)) break; let isMobile = false; if (canSearchMobile && RewardsAuto.state.mobileProgress < RewardsAuto.state.mobileMax) { isMobile = Math.random() > 0.6; } if (isMobile) RewardsAuto.state.mobileProgress += 3; else RewardsAuto.state.pcProgress += 3; let query; if (RewardsAuto.apiConfig.mode !== "offline") { query = await Utils.safeRun("获取热搜词", () => API.getHotSearchWord(), RewardsAuto.searchPool[Utils.randomRange(0, RewardsAuto.searchPool.length - 1)]); } else { query = RewardsAuto.searchPool[Utils.randomRange(0, RewardsAuto.searchPool.length - 1)]; } Utils.log("🔍", `[${isMobile?"Mobile":"PC"}] 搜索 ${i+1}/${maxIterations}: ${query}`); await Utils.safeRun(`搜索#${i+1}`, async () => { const html = await API.getSearchPage(query, isMobile); if (html) await API.reportSearch(html, query, isMobile); }); 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 Utils.safeRun("最终配额查询", () => API.getRewardsInfo(), false); if (finalInfo) { const hasMobile2 = finalInfo.mobile.max > 0; const pcDone2 = finalInfo.pc.progress >= finalInfo.pc.max; const mobileDone2 = !hasMobile2 || finalInfo.mobile.progress >= finalInfo.mobile.max; if (pcDone2 && mobileDone2) { this.searchDate = RewardsAuto.state.dateNowNum; this.save(); RewardsAuto.state.restrictedTimes = 0; GM_setValue("Config.restrictedTimes", 0); GM_setValue("Config.lastSearchProgress", -1); const mobileReport = hasMobile2 ? `\n📱 移动端: ${finalInfo.mobile.progress}/${finalInfo.mobile.max}` : ""; Utils.log("🔵", `🔍 搜索任务完成!\n💻 PC端: ${finalInfo.pc.progress}/${finalInfo.pc.max}${mobileReport}`, true); } else { const mobileReport = hasMobile2 ? `\n📱 移动端: ${finalInfo.mobile.progress}/${finalInfo.mobile.max}` : ""; Utils.log("🟡", `搜索已执行,配额未满\n💻 PC端: ${finalInfo.pc.progress}/${finalInfo.pc.max}${mobileReport}`); } } else { Utils.log("🟡", "搜索已执行,但无法获取最终配额状态"); } }, // 主任务调度(任务级隔离) async runAll() { RewardsAuto.state.startTime = Utils.getTimestamp(); Utils.log("🚀", "启动全能自动化任务..."); this.init(); // 1. 地区检测 const regionOK = await Utils.safeRun("地区检测", () => API.checkRegion(), false); // 2. Token 续期 let isTokenOK = false; if (regionOK) { isTokenOK = await Utils.safeRun("Token续期", () => API.renewToken(), false); if (!isTokenOK) { Utils.log("🟡", "Token失败,跳过签入/阅读", true); } } else { Utils.log("🟡", "地区检测失败,尝试执行搜索任务..."); } // 3. 签入 + 阅读(依赖 token) if (regionOK && isTokenOK) { await Utils.safeRun("签入", () => this.doSign()); await Utils.randomDelay(); await Utils.safeRun("阅读", () => this.doRead()); await Utils.randomDelay(); } else if (regionOK) { await Utils.safeRun("签入", () => this.doSign()); await Utils.randomDelay(); } // 4. 活动卡片 await Utils.safeRun("活动卡片", () => this.doPromos()); await Utils.randomDelay(); // 5. 搜索 await Utils.safeRun("搜索", () => this.doSearch()); // 6. 二次扫描 await Utils.safeRun("二次扫描", async () => { Utils.log("🔄", "二次扫描:检查是否有新解锁的卡片..."); await Utils.randomDelay(2000, 4000); const newCards = await API.discoverCards(); if (newCards.length === 0) { Utils.log("✅", "二次扫描:无新卡片"); return; } 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(2000, 4000); const result = await Utils.safeRun("二次扫描领取", () => API.claimCard(card), false); result ? ok++ : fail++; } Utils.log("🔵", `二次扫描完成: ${ok}成功/${fail}失败`); }); // 7. 汇总 await Utils.safeRun("汇总", () => this.reportSummary()); }, // 汇总通知(从 runAll 末尾提取,方便独立测试) async reportSummary() { const endTime = Utils.getTimestamp(); const totalTime = ((endTime - RewardsAuto.state.startTime) / 1000).toFixed(1); const info = await API.getRewardsInfo(); if (!info) { Utils.log("🎉", `任务执行完成!用时 ${totalTime} 秒`, true); return; } // 推送消息(简洁版) let notifyMsg = `🎉 任务完成!\n`; notifyMsg += `💰 当前积分: ${info.balance}\n`; notifyMsg += `🔍 PC搜索: ${info.pc.progress}/${info.pc.max}\n`; notifyMsg += `📱 移动搜索: ${info.mobile.progress}/${info.mobile.max}\n`; if (info.todayDetails && info.todayDetails.length > 0) { for (const detail of info.todayDetails) { if (detail.title !== '必应搜索') { notifyMsg += `✨ ${detail.title}: +${detail.points}\n`; } } } if (info.history) { notifyMsg += `📅 本月: ${info.history.month.toLocaleString()}\n`; } notifyMsg += `⏱️ 用时: ${totalTime}秒`; RewardsAuto.state.sendMSG = notifyMsg; Utils.log("🎉", notifyMsg, true); // 控制台明细 let detailMsg = `📊 积分明细:\n`; detailMsg += `┌─────────────────────────\n`; detailMsg += `│ 💰 今日积分: ${info.balance}\n`; detailMsg += `├─────────────────────────\n`; detailMsg += `│ 🔍 搜索积分:\n`; detailMsg += `│ 💻 PC端: ${info.pc.progress}/${info.pc.max} ${info.pc.progress >= info.pc.max ? '✅' : '⏳'}\n`; detailMsg += `│ 📱 移动端: ${info.mobile.progress}/${info.mobile.max} ${info.mobile.progress >= info.mobile.max ? '✅' : '⏳'}\n`; detailMsg += `│ 📊 合计: ${info.pc.progress + info.mobile.progress}/${info.pc.max + info.mobile.max}\n`; detailMsg += `├─────────────────────────\n`; if (info.todayDetails && info.todayDetails.length > 0) { detailMsg += `│ 📋 今日活动:\n`; for (const detail of info.todayDetails) { if (detail.max) { detailMsg += `│ 🔍 ${detail.title}: ${detail.points}/${detail.max}\n`; } else { detailMsg += `│ ✨ ${detail.title}: +${detail.points}\n`; } } detailMsg += `├─────────────────────────\n`; } if (info.history) { detailMsg += `│ 📈 历史积分:\n`; detailMsg += `│ 📅 本月: ${info.history.month.toLocaleString()}\n`; detailMsg += `│ 📆 今年: ${info.history.year.toLocaleString()}\n`; detailMsg += `│ 🏆 生存期: ${info.history.lifetime.toLocaleString()}\n`; detailMsg += `├─────────────────────────\n`; } detailMsg += `│ ⏱️ 用时: ${totalTime} 秒\n`; detailMsg += `└─────────────────────────`; Utils.log("📊", detailMsg); } }; // ===================================================== // 打卡任务处理(rewards.bing.com 页面内执行) // ===================================================== if (location.hostname === "rewards.bing.com") { const punchCardSelectors = [ "a[href*='punchcard']", "a[href*='quest']", "a[data-rac][href*='earn']", "a.cursor-pointer[href]" ]; const textPatterns = ["盗贼之海", "五月亮点来袭"]; const clickPunchCards = async (depth = 0) => { if (depth > 5) { console.log("[Rewards Auto] 打卡递归深度超限,停止"); return; } const today = Utils.getTodayNum(); const stateKey = "Config.punchCardState"; const dateKey = "Config.punchCardDate"; const savedDate = GM_getValue(dateKey, 0); let state = savedDate === today ? GM_getValue(stateKey, 0) : 0; if (state >= 2) { console.log("[Rewards Auto] 打卡任务已完成"); return; } console.log(`[Rewards Auto] 打卡任务: ${state}/2`); await Utils.randomDelay(3000, 8000); let found = []; for (const sel of punchCardSelectors) { try { found = await Utils.waitForElementsByText(sel, textPatterns, 10000); if (found.length > 0) break; } catch {} } if (found.length === 0) { console.log("[Rewards Auto] 未找到打卡卡片"); return; } if (state < found.length) { const target = found[state]; console.log(`[Rewards Auto] 点击: ${target.pattern}`); await Utils.safeRun("打卡点击", async () => { await Utils.randomDelay(); target.element.click(); GM_setValue(stateKey, state + 1); GM_setValue(dateKey, today); if (state + 1 < 2) { await Utils.randomDelay(5000, 10000); await clickPunchCards(depth + 1); } }); } }; const startPunchCards = () => { setTimeout(() => { const path = location.pathname; if (path.includes("/earn/quest/") || path.includes("punchcard")) { console.log("[Rewards Auto] 打卡详情页"); } else { console.log("[Rewards Auto] 奖励主页,开始打卡..."); Utils.safeRun("打卡任务", () => clickPunchCards()); } }, 3000); }; if (document.readyState === "complete" || document.readyState === "interactive") { startPunchCards(); } else { document.addEventListener("DOMContentLoaded", startPunchCards); } } // ===================================================== // 菜单注册 // ===================================================== 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); const days = time > 0 ? Math.floor((Utils.getTimestamp() - time) / 86400000) : "未知"; alert(`Token: ${token?"已保存":"无"}\n年龄: ${days}天\n授权码: ${GM_getValue("Config.code","")?"有":"无"}`); }); GM_registerMenuCommand("🚀 立即运行", () => { Utils.safeRun("手动运行", () => 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"; configStr += "==================\n\n"; configNames.forEach((item, index) => { const saved = GM_getValue(item.key, ""); configStr += `${index + 1}. ${item.name}\n`; configStr += ` 状态: ${saved ? "✅ 已配置" : "❌ 未配置"}\n`; configStr += ` 说明: ${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("测试消息已发送,请检查各通知渠道!"); }); 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 = () => { try { 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(() => { Utils.safeRun("runAll", () => TaskManager.runAll()); }, delay); } catch (e) { GM_log(`🔴 初始化失败: ${Utils.fmtErr(e)}`); } }; // 清除可能影响搜索的 Cookie(带保护) try { if (typeof GM_cookie === "function") { GM_cookie("delete", { url: "https://bing.com", name: "_EDGE_S" }, () => {}); } } catch (e) { GM_log(`🟡 GM_cookie 清理跳过: ${Utils.fmtErr(e)}`); } init(); })();