// ==UserScript== // @name 微软积分商城签到 // @namespace https://github.com/geoi6sam1 // @version 3.0.6 // @description 每天自动定时完成 Microsoft Rewards 任务(Web 搜索/活动 、App 签到/阅读) // @author geoi6sam1&Soul233 // @icon https://store-images.s-microsoft.com/image/apps.58212.783a7d74-cf5a-4dca-aed6-b5722f311eca.f8c0cb0b-6b57-4f06-99b1-5d7ee04e38e6.517a44fd-f164-40ae-996b-f959198325c2 // @supportURL https://github.com/geoi6sam1/FuckScripts/issues // @crontab */20 * * * * // @run-at document-start // @grant unsafeWindow // @grant GM_xmlhttpRequest // @grant GM_notification // @grant GM_openInTab // @grant GM_getValue // @grant GM_setValue // @grant GM_addValueChangeListener // @grant GM_log // @grant GM_registerMenuCommand // @connect bing.com // @connect *.bing.com // @connect rewards.bing.com // @connect login.live.com // @connect prod.rewardsplatform.microsoft.com // @connect dailyapi.eray.cc // @connect hot.baiwumm.com // @connect cnxiaobai.com // @connect hotapi.zhusun.top // @connect api-hot.imsyy.top // @connect hotapi.nntool.cc // @license GPL-3.0 // @storageName rewards-bus // ==/UserScript== /* ==UserConfig== Config: app: title: ✅ 使用“微软必应App任务”(每日签到 + 文章阅读) type: checkbox default: false app_helper: title: ⚠️ 如启用 App 任务:请先安装并开启“前台授权助手”脚本 → https://scriptcat.org/zh-CN/script-show-page/3956 type: checkbox default: true limit: title: 限制搜索(每次运行只搜索 4-8 次) type: checkbox default: true span: title: 搜索间隔(默认15,即间隔 10-20 秒) type: number default: 15 min: 10 max: 300 unit: ±5秒 api: title: 搜索词接口(单机模式为随机汉字组句) type: select default: hot.nntool.cc values: [单机模式, hot.eray.cc, hot.baiwumm.com, hot.cnxiaobai.com, hot.zhusun.top, hot.imsyy.top, hot.nntool.cc] push: title: 全部任务完成后推送URL(留空则不推送) type: text default: "" testPush: title: 下次运行时测试一次Push URL type: checkbox default: false ==/UserConfig== */ const obj = { data: { time: { task: 3000 }, auth: { code: "https://login.live.com/oauth20_authorize.srf", token: "https://login.live.com/oauth20_token.srf", clientId: "0000000040170455", scope: "service::prod.rewardsplatform.microsoft.com::MBI_SSL", redirectUri: "https://login.live.com/oauth20_desktop.srf", }, ua: { pc: [ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0", "Mozilla/5.0 (Sonoma; Intel Mac OS X 14_4_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/604.1 Edg/122.0.2365.106", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.2210.181", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.140", ], m: [ "Mozilla/5.0 (Linux; Android 14; 2211133C Build/UKQ1.230804.001; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36 EdgA/124.0.2478.64", "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36 EdgA/131.0.0.0", "Mozilla/5.0 (Linux; Android 14; 2210132C Build/UP1A.231005.007) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.52 Version/4.0 Mobile Safari/537.36 EdgA/125.0.2535.51", "Mozilla/5.0 (iPad; CPU OS 16_7_8 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) EdgiOS/120.0.2210.150 Version/16.0 Mobile/15E148 Safari/604.1", "Mozilla/5.0 (iPhone; CPU iPhone OS 18_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) EdgiOS/123.0.2420.108 Version/18.0 Mobile/15E148 Safari/604.1", "Mozilla/5.0 (Linux; Android 10; HarmonyOS; ALN-AL10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Version/4.0 Mobile Safari/537.36 EdgA/110.0.1587.61" ], }, api: { arr: [ ["hot.eray.cc", { url: "https://dailyapi.eray.cc/", hot: ["weibo","douyin","baidu","toutiao","thepaper","qq-news","netease-news","zhihu"] }], ["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.zhusun.top", { url: "https://hotapi.zhusun.top/", hot: ["weibo","douyin","baidu","toutiao","thepaper","qq-news","netease-news","zhihu"] }], ["hot.imsyy.top", { url: "https://api-hot.imsyy.top/", 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","zhihu"] }] ] }, web: 0, app: 0 }, task: { sign: { times: 0, point: 1, end: 0 }, read: { times: 0, point: 0, end: 0 }, promo: { times: 0, token: 0, end: 0 }, search:{ word:{ list:[], index:0 }, times:0, progressNow:0, pc:{ progress:0, max:1 }, m:{ progress:0, max:1 }, limit:{ min:3, max:7 }, index:0, end:0, }, token: 0, }, }; // ========= 基础工具 ========= obj.getRandomNum = num => Math.floor(Math.random()*num); obj.getScopeRandomNum = (min,max)=> Math.floor(Math.random()*(max+1-min)+min); obj.getRandomArr = arr => arr.sort(()=>Math.random()-0.5); obj.getRandomChineseChar = ()=> String.fromCodePoint(Math.floor(Math.random()*(0x9FFF-0x4E00+1))+0x4E00); obj.generateRandomChineseStr = (min=6,max=32)=>{const len=Math.floor(Math.random()*(max-min+1))+min;let s="";for(let i=0;i 1 && idx === last) idx = (idx + 1) % arr.length; GM_setValue("hot_last_index", idx); return arr[idx]; }catch(e){ return "weibo"; } }; obj.pushMsg = function (title, text="") { title = "微软积分商城" + title; GM_log(title + (text?(" "+text):"")); GM_notification({ text, title, onclick: ()=> GM_openInTab("https://rewards.bing.com/pointsbreakdown",{active:true, insert:true, setParent:true}) }); }; obj.beforeStart = function(){ const dateTime = new Date(); const y = dateTime.getFullYear(); const m = ("0"+(dateTime.getMonth()+1)).slice(-2); const d = ("0"+dateTime.getDate()).slice(-2); obj.data.time.hoursNow = Number(dateTime.getHours()); obj.data.time.dateNow = `${m}/${d}/${y}`; obj.data.time.dateNowNum = Number(`${y}${m}${d}`); obj.task.search.limit.index = obj.getScopeRandomNum(obj.task.search.limit.min, obj.task.search.limit.max); // API 选择 if (GM_getValue("Config.api","单机模式")!="单机模式"){ const defaultApiName="hot.eray.cc"; const currentApiName=GM_getValue("Config.api",defaultApiName); const apiConfigMap=new Map(obj.data.api.arr); const getConfigApi=apiConfigMap.get(currentApiName)||apiConfigMap.get(defaultApiName); obj.data.api.url=getConfigApi.url; obj.data.api.hot=getConfigApi.hot; if(!apiConfigMap.has(currentApiName)){ GM_setValue("Config.api","单机模式"); obj.pushMsg("必应搜索🟣","当前搜索词接口失效!已替换成单机模式!"); } } }; // ========= Token 交换(通用) ========= obj.postToken = function (kv) { return new Promise((resolve) => { const body = Object.entries(kv).map(([k,v])=>`${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&"); GM_xmlhttpRequest({ method: "POST", url: obj.data.auth.token, headers: { "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", "Accept":"application/json" }, data: body, onload(xhr){ if (xhr.status==200){ try{ const res = JSON.parse(xhr.responseText||"{}"); const rt = res.refresh_token; const at = res.access_token; if (rt && at){ // 兼容老键 & 新键(共享用) GM_setValue("refresh_token", rt); GM_setValue("msa_refresh_token", rt); GM_setValue("msa_access_token", at); GM_setValue("msa_access_exp", Math.floor(Date.now()/1000) + (Number(res.expires_in||3600))); obj.task.token = at; resolve(1); return; } } catch(e){} resolve(0); } else { // 调试:打印头部前几行与 body 前 400 字 GM_log(`[token] http=${xhr.status} h=${(xhr.responseHeaders||"").split(/[\r\n]+/).slice(0,6).join(" | ")} bodyHead=${(xhr.responseText||"").slice(0,400)}`); resolve(xhr.status); } }, onerror(){ resolve(-1); }, ontimeout(){ resolve(-2); } }); }); }; // ========= storageName 总线(@storageName rewards-bus)========= const BUS = { PENDING: "auth:pending", RESULT: "auth:result" }; function buildAuthorizeUrl(){ const a = obj.data.auth; const p = new URLSearchParams({ client_id: a.clientId, scope: a.scope, response_type: "code", redirect_uri: a.redirectUri, response_mode: "query", lc: "2052", }); return `${a.code}?${p.toString()}`; } function waitCodeViaBus(pendingId, timeoutMs = 120000){ return new Promise((resolve)=>{ let done=false; const to=setTimeout(()=>{ if(!done){done=true; resolve(null);} }, timeoutMs); GM_addValueChangeListener(BUS.RESULT, (name, oldV, msg, remote)=>{ if (done || !remote || !msg) return; if (msg.id !== pendingId) return; done=true; clearTimeout(to); if (msg.err) { GM_log("[auth] FE error: "+msg.err); resolve(null); } else resolve(String(msg.code||"").trim() || null); }); }); } async function openAuthorizeAndWait(){ const id = String(Date.now()); GM_setValue(BUS.PENDING, { id, ts: Date.now() }); const url = buildAuthorizeUrl(); GM_log("[auth] open auth page (on-demand): " + url); GM_openInTab(url, { active: true, insert: true, setParent: true }); const code = await waitCodeViaBus(id, 120000); GM_setValue(BUS.PENDING, null); // 用完清理 return code; } // ========= 仅在需要时确保 App Token ========= let authOpening = false; // 单次保护,避免并发重复开页 async function ensureAppToken(){ // 先看本地未过期 AT const at = GM_getValue("msa_access_token","") || ""; const exp = Number(GM_getValue("msa_access_exp",0)); const now = Math.floor(Date.now()/1000); if (at && exp && exp - now > 300) { obj.task.token = at; return at; } // 再试 RT 刷新 const rt = GM_getValue("msa_refresh_token", GM_getValue("refresh_token", 0)); if (rt){ const ok = await obj.postToken({ grant_type: "refresh_token", client_id: obj.data.auth.clientId, refresh_token: rt, scope: obj.data.auth.scope }); if (ok === 1) return obj.task.token; } // —— 失败次数节流:每天最多 3 次 —— // function incFailAndMaybeDisable(){ const today = obj.data.time.dateNowNum; let st = GM_getValue("auth_fail_state", { date:0, count:0 }); if (!st || typeof st !== "object") st = { date:0, count:0 }; if (st.date !== today){ st.date = today; st.count = 0; } st.count += 1; GM_setValue("auth_fail_state", st); if (st.count >= 3){ // 自动关闭 App 任务,避免重复授权 GM_setValue("Config.app", false); obj.task.sign.end++; obj.task.read.end++; const msg = "App授权🔴 今日多次授权失败(已达 3 次),已自动关闭“微软必应App任务”。可稍后在设置中手动重新开启。"; GM_log(msg); obj.pushMsg("", msg.replace("App授权🔴 ","")); // 通知 throw new Error("授权失败超限,已关闭 App 任务"); } } // 已有其它授权流程在进行:等最多 120 秒 if (authOpening) { for (let i=0;i<120;i++){ await new Promise(r=>setTimeout(r,1000)); const at2 = GM_getValue("msa_access_token",""); const exp2 = Number(GM_getValue("msa_access_exp",0)); if (at2 && exp2 && exp2 - Math.floor(Date.now()/1000) > 300){ obj.task.token = at2; return at2; } } incFailAndMaybeDisable(); throw new Error("等待其它授权流程超时"); } try{ authOpening = true; GM_log("App授权🟡 按需打开授权页等待回传…"); const code = await openAuthorizeAndWait(); if (code){ GM_log("App授权🟢 捕获到授权码,换取 token…"); const ok = await obj.postToken({ grant_type: "authorization_code", client_id: obj.data.auth.clientId, code, redirect_uri: obj.data.auth.redirectUri, scope: obj.data.auth.scope }); if (ok === 1) return obj.task.token; // code 兑换失败 → 计失败 + 弹手动粘贴兜底 incFailAndMaybeDisable(); const manual = prompt("⚠️ code 兑换失败。\n请粘贴回调页 URL 里的 code 值:", ""); if (manual && manual.trim()){ const ok2 = await obj.postToken({ grant_type: "authorization_code", client_id: obj.data.auth.clientId, code: manual.trim(), redirect_uri: obj.data.auth.redirectUri, scope: obj.data.auth.scope }); if (ok2 === 1) return obj.task.token; } incFailAndMaybeDisable(); throw new Error("授权失败或超时"); } else { // 未捕获到 code:允许一次手动粘贴 incFailAndMaybeDisable(); const manual = prompt("⚠️ 未自动捕获到授权码。\n请粘贴回调页 URL 里的 code 值:", ""); if (manual && manual.trim()){ const ok = await obj.postToken({ grant_type: "authorization_code", client_id: obj.data.auth.clientId, code: manual.trim(), redirect_uri: obj.data.auth.redirectUri, scope: obj.data.auth.scope }); if (ok === 1) return obj.task.token; } incFailAndMaybeDisable(); throw new Error("授权失败或超时"); } } finally { authOpening = false; } } // ========= Web 侧 API ========= obj.getRewardsInfo = function(){ return new Promise((resolve)=>{ GM_xmlhttpRequest({ url: "https://rewards.bing.com/api/getuserinfo?type=1", onload(xhr){ if (xhr.status==200){ let res = xhr.responseText; const data = res.match(/(\"dashboard\"?)/); if (data && data[0]){ res = JSON.parse(res); resolve(res.dashboard); } else { obj.task.sign.end++; obj.task.read.end++; obj.task.promo.end++; obj.task.search.end++; obj.data.web>0 || obj.pushMsg("All任务🔴","账号状态失效,请检查Microsoft登录状态或重新登录!"); obj.data.web++; resolve(0); } } else { GM_log(`微软积分商城Web任务🟡getuserinfo失败(${xhr.status}),重试…`); resolve(0); } } }); }); }; obj.getRewardsToken = function(){ return new Promise((resolve)=>{ GM_xmlhttpRequest({ url: "https://rewards.bing.com", onload(xhr){ if (xhr.status==200){ const html = (xhr.responseText||"").replace(/\s/g,""); const token = html.match(/RequestVerificationToken"type="hidden"value="(.*?)"\/>/); if (token && token[1]) resolve(token[1]); else { obj.task.promo.end++; obj.pushMsg("活动推广🔴","请求验证失败,请检查登录!"); resolve(0); } } else { GM_log(`Web任务🟡RequestVerificationToken失败(${xhr.status}),重试…`); resolve(0); } } }); }); }; // ========= App 任务:阅读 ========= obj.getReadPro = function(){ return new Promise((resolve)=>{ let readArr = { max:1, progress:0 }; GM_xmlhttpRequest({ url: "https://prod.rewardsplatform.microsoft.com/dapi/me?channel=SAAndroid&options=613", headers: { "authorization": `Bearer ${obj.task.token}` }, onload(xhr){ if (xhr.status==200){ try{ const res = JSON.parse(xhr.responseText||"{}"); const pro = res.response && res.response.promotions; if (pro){ for (const o of pro){ if (o.attributes.offerid == "ENUS_readarticle3_30points"){ readArr = { max:Number(o.attributes.max), progress:Number(o.attributes.progress) }; return resolve(readArr); } } } }catch(e){} } resolve(readArr); } }); }); }; obj.taskRead = async function(){ if (obj.task.read.end>0) return true; else if (obj.data.time.hoursNow<12){ obj.task.read.end++; return true; } else if (obj.task.read.times>2){ obj.task.read.end++; obj.pushMsg("文章阅读🔴","未知原因出错,本次文章阅读结束!"); return true; } else { // 仅当启用了 App 任务时,才确保 Token(按需授权) if (!GM_getValue("Config.app", false)) { obj.task.read.end++; return true; } if (!obj.task.token){ try{ await ensureAppToken(); }catch(e){ return false; } } const readPro = await obj.getReadPro(); if (readPro.progress > obj.task.read.point){ obj.task.read.times=0; obj.task.read.point=readPro.progress; } else { obj.task.read.times++; } if (readPro.progress >= readPro.max){ obj.task.read.end++; if (GM_getValue("task_read",0)!=obj.data.time.dateNowNum) obj.pushMsg("文章阅读🟢","完成!"); GM_setValue("task_read", obj.data.time.dateNowNum); return true; } else { GM_setValue("task_read", 0); GM_xmlhttpRequest({ method: "POST", url: "https://prod.rewardsplatform.microsoft.com/dapi/me/activities", headers: { "Content-Type":"application/json", "authorization": `Bearer ${obj.task.token}` }, data: JSON.stringify({ amount:1, country:"cn", id:"", type:101, attributes:{ offerid:"ENUS_readarticle3_30points" } }), responseType: "json" }); return false; } } }; // ========= App 任务:签到 ========= obj.taskSign = async function(){ if (obj.task.sign.end>0 || GM_getValue("task_sign",0)==obj.data.time.dateNowNum){ obj.task.sign.end++; return true; } else if (obj.task.sign.times>2){ obj.task.sign.end++; obj.pushMsg("App签到🔴","未知原因出错,本次App签到结束!"); return true; } else if (obj.task.sign.point==0){ obj.task.sign.end++; if (GM_getValue("task_sign",0)!=obj.data.time.dateNowNum) obj.pushMsg("App签到🟢","完成!"); GM_setValue("task_sign", obj.data.time.dateNowNum); return true; } else { if (!GM_getValue("Config.app", false)) return true; // 未启用 App 任务则跳过 if (!obj.task.token){ try{ await ensureAppToken(); }catch(e){ return false; } } GM_xmlhttpRequest({ method: "POST", url: "https://prod.rewardsplatform.microsoft.com/dapi/me/activities", headers: { "Content-Type":"application/json", "authorization": `Bearer ${obj.task.token}` }, data: JSON.stringify({ amount:1, attributes:{ offerid:"Gamification_Sapphire_DailyCheckIn", date:obj.data.time.dateNowNum, signIn:false, timezoneOffset:"08:00:00" }, id:"", type:101, country:"cn", risk_context:{}, channel:"SAAndroid" }), responseType: "json", onload(xhr){ obj.task.sign.times=0; try{ const res = JSON.parse(xhr.responseText||"{}"); const point = res.response && res.response.activity && res.response.activity.p; obj.task.sign.point = point ? point : 0; }catch(e){ obj.task.sign.times++; } } }); return false; } }; // ========= 搜索 & 活动(与原逻辑一致,略小改日志) ========= obj.getTopKeyword = function(){ return new Promise((resolve)=>{ let sentence = obj.generateRandomChineseStr(); if (GM_getValue("Config.api","单机模式")=="单机模式"){ resolve(sentence); } else { if (obj.task.search.word.index<1 || obj.task.search.word.list.length<1){ const apiHot = (obj.data.api.url ? obj.getRandomApiHot() : null) || "weibo";//每次拉取热词都会在该 API 的多个榜单间轮换 GM_xmlhttpRequest({ timeout: 9999, url: obj.data.api.url + apiHot, onload(xhr){ if (xhr.status==200){ try{ const res = JSON.parse(xhr.responseText||"{}"); if (res.code==200){ obj.task.search.word.index = 1; for (let i=0;i obj.task.search.word.list.length - 1) obj.task.search.word.index = 0; resolve(obj.task.search.word.list[obj.task.search.word.index]); } } }); }; obj.taskPromo = async function(){ GM_log("=== 开始活动推广任务 ==="); if (obj.data.time.hoursNow < 12){ obj.task.promo.end++; return true; } if (obj.task.promo.times > 2){ obj.task.promo.end++; obj.pushMsg("活动推广🔴","未知原因,本次结束!"); return true; } const dashboard = await obj.getRewardsInfo(); if (dashboard==0) return false; let promotionsArr = []; const morePromotions = dashboard.morePromotions || []; const dailySetPromotions = dashboard.dailySetPromotions?.[obj.data.time.dateNow] || []; for (const p of [...dailySetPromotions, ...morePromotions]) { if (p.complete === false) promotionsArr.push({ offerId: p.offerId, hash: p.hash }); } obj.task.promo.token = await obj.getRewardsToken(); if (!obj.task.promo.token){ return false; } if (promotionsArr.length === 0){ obj.task.promo.end++; if (GM_getValue("task_promo",0) !== obj.data.time.dateNowNum) obj.pushMsg("活动推广🟢","完成!"); GM_setValue("task_promo", obj.data.time.dateNowNum); return true; } promotionsArr.forEach(item=>{ GM_xmlhttpRequest({ method: "POST", url: `https://rewards.bing.com/api/reportactivity`, headers: { "Content-Type":"application/x-www-form-urlencoded", "Referer": `https://rewards.bing.com/` }, data: `id=${encodeURIComponent(item.offerId||"")}&hash=${encodeURIComponent(item.hash)}&__RequestVerificationToken=${encodeURIComponent(obj.task.promo.token)}`, onload(xhr){ GM_log(`reportactivity 状态码:${xhr.status}`); }, }); }); obj.task.promo.times++; return false; }; obj.taskSearch = async function () { if (obj.task.search.end > 0) return true; const dashboard = await obj.getRewardsInfo(); if (dashboard == 0) return false; if (dashboard.userStatus.counters.pcSearch) { obj.task.search.pc.progress = dashboard.userStatus.counters.pcSearch[0].pointProgress; obj.task.search.pc.max = dashboard.userStatus.counters.pcSearch[0].pointProgressMax; } if (dashboard.userStatus.counters.mobileSearch) { obj.task.search.m.progress = dashboard.userStatus.counters.mobileSearch[0].pointProgress; obj.task.search.m.max = dashboard.userStatus.counters.mobileSearch[0].pointProgressMax; } else obj.task.search.m.max = 0; GM_log(`[search] pc=${obj.task.search.pc.progress}/${obj.task.search.pc.max} m=${obj.task.search.m.progress}/${obj.task.search.m.max} index=${obj.task.search.index}/${obj.task.search.limit.index}`); if (GM_getValue("Config.limit", true) == true) { if (obj.task.search.index > obj.task.search.limit.index) { obj.task.search.end++; GM_log(`必应搜索🔵限制搜索,本次共 ${obj.task.search.index} 次`); return true; } } else { if (obj.task.search.times > 2) { obj.task.search.end++; GM_log(`必应搜索🔵积分收入限制,本次共 ${obj.task.search.index} 次`); return true; } if (dashboard.userStatus.counters.dailyPoint[0].pointProgress == obj.task.search.progressNow) { obj.task.search.times++; } else { obj.task.search.times = 0; obj.task.search.progressNow = dashboard.userStatus.counters.dailyPoint[0].pointProgress; } } if (obj.task.search.pc.progress >= obj.task.search.pc.max && obj.task.search.m.progress >= obj.task.search.m.max) { obj.task.search.end++; if (GM_getValue("task_search", 0) != obj.data.time.dateNowNum) obj.pushMsg("必应搜索🟢","完成!"); GM_setValue("task_search", obj.data.time.dateNowNum); return true; } else { GM_setValue("task_search", 0); const keyword = await obj.getTopKeyword(); const pcUrl = `https://www.bing.com/search?q=${encodeURIComponent(keyword)}&qs=ds&FORM=QBLH`; const mobileMode = Math.random() < 0.5 ? "bingApp" : "edgeApp"; let mUrl; if (mobileMode === "bingApp") mUrl = `https://cn.bing.com/search?q=${encodeURIComponent(keyword)}&cc=CN&PC=SANSAAND&form=LWS001&ssp=1&darkschemeovr=1&safesearch=moderate&setlang=zh-hans`; else mUrl = `https://cn.bing.com/search?q=${encodeURIComponent(keyword)}&setmkt=en-US&PC=EMMX01&form=LBT003&scope=web`; const sendBing = (uaTag, uaVal, url, lang, referer) => { GM_xmlhttpRequest({ method: "GET", url, headers: { "User-Agent": uaVal, "Accept-Language": lang, "Referer": referer }, onload() { obj.task.search.index++; GM_log(`[search] ${uaTag} onload index=${obj.task.search.index}`); }, onerror() { obj.task.search.index++; GM_log(`[search] ${uaTag} onerror index=${obj.task.search.index}`); }, ontimeout(){ obj.task.search.index++; GM_log(`[search] ${uaTag} ontimeout index=${obj.task.search.index}`); }, }); }; if (obj.task.search.pc.progress < obj.task.search.pc.max) { const ua = obj.data.ua.pc[obj.getRandomNum(obj.data.ua.pc.length)]; sendBing("PC", ua, pcUrl, "zh-CN,zh;q=0.9", "https://www.bing.com/"); return false; } if (obj.task.search.m.progress < obj.task.search.m.max) { const mList = (obj.data.ua.m || []).filter(u => /EdgA|EdgiOS/.test(u)); const pool = mList.length ? mList : obj.data.ua.m; const ua = pool[obj.getRandomNum(pool.length)]; sendBing("M", ua, mUrl, "zh-CN,zh;q=0.9", "https://cn.bing.com/"); return false; } } }; // ========= 启动器 ========= return new Promise((resolve, reject)=>{ obj.beforeStart(); // 任务完成判定 obj.taskEnd = function(){ const needSign = GM_getValue("Config.app", false); if ( (!needSign || obj.task.sign.end>0) && obj.task.search.end>0 ){ const pushUrl = (GM_getValue("Config.push","")||"").trim(); if (pushUrl){ GM_notification({ title:"签到脚本", text:"开始推送到:"+pushUrl }); GM_xmlhttpRequest({ method: "GET", url: pushUrl, onload(xhr){ GM_notification({ title:"Push 成功", text:"状态码:"+xhr.status }); resolve(); }, onerror(){ GM_notification({ title:"Push 失败", text:"检查 URL/网络" }); resolve(); } }); } else resolve(); } }; // 菜单:安装前台助手 / 测试打开授权页 GM_registerMenuCommand("安装“前台授权助手”脚本", ()=> { GM_openInTab("https://scriptcat.org/scripts/code/3956/%E5%BE%AE%E8%BD%AF%20Rewards%20%E5%89%8D%E5%8F%B0%E6%8E%88%E6%9D%83%E5%8A%A9%E6%89%8B.user.js", { active:true }); }); GM_registerMenuCommand("测试:打开授权页(验证前后台传参)", async ()=>{ const id = String(Date.now()); GM_setValue(BUS.PENDING, { id, ts: Date.now() }); GM_openInTab(buildAuthorizeUrl(), { active:true, insert:true, setParent:true }); GM_notification({ title:"授权测试", text:"已打开授权页,登录/同意后回调页会把 code 传回后台" }); // 不等待结果,纯测试 }); // 一键测试 push(保留) async function testPushUrl(){ const pushUrl = (GM_getValue("Config.push","")||"").trim(); if (pushUrl){ GM_xmlhttpRequest({ method:"GET", url: pushUrl, onload:x=>{ GM_notification({title:"Push URL测试成功", text:`状态码: ${x.status}`}); }, onerror:()=>{ GM_notification({title:"Push URL测试失败", text:"请检查URL或网络!"}); } }); } else { GM_notification({ title:"Push URL为空", text:"请先填写后再测试!" }); } GM_setValue("Config.testPush", false); } if (GM_getValue("Config.testPush", false)) testPushUrl(); // 启动各任务 obj.signStart = async function(){ try{ const r = await obj.taskSign(); r ? obj.taskEnd() : setTimeout(()=>{ obj.signStart(); }, obj.data.time.task); }catch(e){ reject(e); } }; obj.readStart = async function(){ try{ const r = await obj.taskRead(); r ? obj.taskEnd() : setTimeout(()=>{ obj.readStart(); }, obj.data.time.task); }catch(e){ reject(e); } }; obj.promoStart= async function(){ try{ const r = await obj.taskPromo(); r ? obj.taskEnd() : setTimeout(()=>{ obj.promoStart(); }, obj.data.time.task); }catch(e){ reject(e); } }; obj.searchStart= async function(){ try{ const r = await obj.taskSearch(); const timespan = GM_getValue("Config.span",15)*1000; r ? obj.taskEnd() : setTimeout(()=>{ obj.searchStart(); }, obj.getScopeRandomNum(timespan-5000, timespan+5000)); }catch(e){ reject(e); } }; // 仅在需要 App 任务时,才会在对应任务里 ensureAppToken(不在这里主动授权) obj.taskStart = function(){ if (!GM_getValue("Config.app", false)){ obj.task.sign.end++; // 未开启 App,直接视为完成 obj.task.read.end++; } obj.promoStart(); obj.signStart(); obj.readStart(); obj.searchStart(); }(); });