// ==UserScript== // @name Microsoft Bing Rewards 每日任务脚本 // @version 1.0.0 // @description 自动完成微软Rewards每日搜索任务,使用热门词库,模拟真实用户行为,支持后台运行,标题栏实时显示进度。 // @author AL // @match *://*.bing.com/* // @exclude https://rewards.bing.com/* // @icon https://www.bing.com/favicon.ico // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_notification // @grant GM_xmlhttpRequest // @connect gumengya.com // @run-at document-end // @license MIT // ==/UserScript== (function() { 'use strict'; // ==================== 用户可配置参数 ==================== const CONFIG = { maxSearches: 40, // 总搜索次数(PC端每日上限约30-50,请根据实际情况调整) delayMin: 15000, // 每次搜索后最小延迟(毫秒)15秒 delayMax: 45000, // 每次搜索后最大延迟(毫秒)45秒 pauseAfter: 5, // 每执行几次搜索后暂停一次 pauseDuration: 300000, // 暂停时长(毫秒)5分钟 scrollDuration: 1500, // 滚动持续时间(毫秒,页面后台时自动跳过) clickRandomResult: true, // 是否随机点击搜索结果(后台时自动禁用) useAntiDetection: true, // 是否启用滚动/点击等反检测行为(后台时自动禁用) debug: false // 控制台输出调试日志 }; // 热门词接口配置(故梦API) const HOT_WORDS_API = "https://api.gmya.net/Api/"; let appkey = ""; // 可选:从 https://www.gmya.net/api 申请,留空则使用无key接口 const KEYWORDS_SOURCES = ['WeiBoHot', 'TouTiaoHot', 'DouYinHot', 'BaiduHot']; // 默认搜索词(接口失败时使用) const DEFAULT_WORDS = [ "盛年不重来,一日难再晨", "千里之行,始于足下", "少年易学老难成,一寸光阴不可轻", "敏而好学,不耻下问", "海内存知已,天涯若比邻", "三人行,必有我师焉", "莫愁前路无知已,天下谁人不识君", "人生贵相知,何用金与钱", "天生我材必有用", "海纳百川有容乃大;壁立千仞无欲则刚", "穷则独善其身,达则兼济天下", "读书破万卷,下笔如有神", "学而不思则罔,思而不学则殆", "一年之计在于春,一日之计在于晨", "莫等闲,白了少年头,空悲切", "少壮不努力,老大徒伤悲", "一寸光阴一寸金,寸金难买寸光阴", "近朱者赤,近墨者黑", "吾生也有涯,而知也无涯", "纸上得来终觉浅,绝知此事要躬行", "学无止境", "己所不欲,勿施于人", "天将降大任于斯人也", "鞠躬尽瘁,死而后已", "书到用时方恨少", "天下兴亡,匹夫有责", "人无远虑,必有近忧", "为中华之崛起而读书", "一日无书,百事荒废", "岂能尽如人意,但求无愧我心", "人生自古谁无死,留取丹心照汗青", "生于忧患,死于安乐", "言必信,行必果", "夫君子之行,静以修身,俭以养德", "老骥伏枥,志在千里", "一日不读书,胸臆无佳想", "王侯将相宁有种乎", "淡泊以明志。宁静而致远", "卧龙跃马终黄土" ]; // 状态存储键名(跨页面持久化) const STORAGE_KEYS = { TASK_ACTIVE: 'ms_rewards_task_active', TOTAL: 'ms_rewards_total', DONE: 'ms_rewards_done', SEARCH_WORDS: 'ms_rewards_search_words', LAST_PAUSE_COUNT: 'ms_rewards_last_pause' }; let searchWords = []; // 存储当前使用的搜索词列表 // ==================== 基础工具函数 ==================== function log(msg) { if (CONFIG.debug) console.log(`[Rewards] ${new Date().toLocaleTimeString()} - ${msg}`); } function randomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // 生成随机字符串(用于搜索链接的form和cvid参数) function generateRandomString(length) { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; let result = ''; for (let i = 0; i < length; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return result; } // 搜索词混淆(插入随机空白字符,模拟打字误差) function autoStrTrans(str) { let yStr = str; let rStr = ""; // 可自定义插入的字符(例如空格或特殊符号) let zStr = ""; let prePo = 0; for (let i = 0; i < yStr.length;) { let step = randomInt(1, 5); if (i > 0) { zStr = zStr + yStr.substr(prePo, i - prePo) + rStr; prePo = i; } i = i + step; } if (prePo < yStr.length) { zStr = zStr + yStr.substr(prePo, yStr.length - prePo); } return zStr; } // 更新浏览器标签页标题,显示当前搜索进度 function updateTitle(current, total) { const titleElem = document.getElementsByTagName("title")[0]; if (titleElem) { let baseTitle = titleElem.innerText.replace(/\[\d+\s*\/\s*\d+\]\s*/, ''); titleElem.innerText = `[${current} / ${total}] ${baseTitle}`; } } // ==================== 模拟真实用户行为(反检测) ==================== let isPageVisible = true; document.addEventListener('visibilitychange', () => { isPageVisible = !document.hidden; log(`页面可见性: ${isPageVisible ? '前台' : '后台'}`); }); // 模拟鼠标移动轨迹 async function simulateMouseMovement(startX, startY, endX, endY) { if (!isPageVisible) return; // 后台不模拟,节省资源 const steps = randomInt(10, 30); const stepX = (endX - startX) / steps; const stepY = (endY - startY) / steps; for (let i = 0; i <= steps; i++) { const x = startX + stepX * i + (Math.random() - 0.5) * 5; const y = startY + stepY * i + (Math.random() - 0.5) * 5; const event = new MouseEvent('mousemove', { bubbles: true, cancelable: true, clientX: x, clientY: y }); document.dispatchEvent(event); await sleep(randomInt(5, 15)); } } // 模拟页面滚动(缓动曲线,随机目标位置) async function simulateScroll() { if (!isPageVisible || !CONFIG.useAntiDetection) return; const duration = CONFIG.scrollDuration; const startY = window.scrollY; const targetY = randomInt(document.body.scrollHeight * 0.3, document.body.scrollHeight * 0.8); const startTime = Date.now(); while (Date.now() - startTime < duration) { const progress = (Date.now() - startTime) / duration; const easeProgress = 1 - Math.pow(1 - progress, 1.5); const currentY = startY + (targetY - startY) * easeProgress; window.scrollTo(0, currentY); await sleep(randomInt(30, 80)); } await sleep(randomInt(200, 600)); // 约40%概率再慢慢滚回顶部(模拟阅读完毕) if (Math.random() > 0.6) { const backDuration = randomInt(800, 2000); const backStart = Date.now(); const backStartY = window.scrollY; while (Date.now() - backStart < backDuration) { const progress = (Date.now() - backStart) / backDuration; const currentY = backStartY * (1 - progress); window.scrollTo(0, currentY); await sleep(randomInt(20, 50)); } } } // 随机点击搜索结果中的某个链接 async function clickRandomResult() { if (!CONFIG.clickRandomResult || !isPageVisible) return; const selectors = [ 'a[href*="/search?q="]', '#b_results h2 a', '.b_algo h2 a', 'a[class*="tilk"]' ]; let links = []; for (const selector of selectors) { const found = document.querySelectorAll(selector); if (found.length > 0) { links = Array.from(found); break; } } if (links.length === 0) { links = Array.from(document.querySelectorAll('a[href]')).filter(link => { const href = link.getAttribute('href'); return href && !href.startsWith('javascript:') && !href.startsWith('#'); }); } if (links.length > 0) { // 随机选择一个链接(跳过前几个,更自然) const index = Math.min(randomInt(2, 8), links.length - 1); const target = links[index > 0 ? index : randomInt(0, links.length - 1)]; const rect = target.getBoundingClientRect(); if (rect && rect.top < window.innerHeight && rect.bottom > 0) { await simulateMouseMovement( window.innerWidth / 2, window.innerHeight / 2, rect.left + rect.width / 2, rect.top + rect.height / 2 ); if (Math.random() < 0.4) { target.click(); log("点击了搜索结果链接"); await sleep(randomInt(3000, 8000)); if (Math.random() < 0.7) { window.history.back(); await sleep(randomInt(1500, 4000)); } } } } } // ==================== 获取热门搜索词 ==================== async function fetchHotWords() { const source = KEYWORDS_SOURCES[Math.floor(Math.random() * KEYWORDS_SOURCES.length)]; let url; if (appkey) { url = HOT_WORDS_API + source + "?format=json&appkey=" + appkey; } else { url = HOT_WORDS_API + source; } log(`尝试从 ${source} 获取热门词...`); try { const response = await fetch(url); if (!response.ok) throw new Error(`HTTP ${response.status}`); const data = await response.json(); if (data.data && data.data.length > 0) { const words = data.data.map(item => item.title).filter(w => w && w.length > 0); if (words.length > 0) { log(`成功获取 ${words.length} 个热门词`); return words; } } } catch (err) { log(`获取热门词失败:${err.message}`); } log("使用默认搜索词库"); return [...DEFAULT_WORDS]; } async function initSearchWords() { let cached = GM_getValue(STORAGE_KEYS.SEARCH_WORDS, null); if (cached && cached.length > 0) { log("使用缓存的搜索词"); searchWords = cached; return; } searchWords = await fetchHotWords(); GM_setValue(STORAGE_KEYS.SEARCH_WORDS, searchWords); } // ==================== 核心搜索流程(跨页面自动继续) ==================== // 执行一次搜索跳转 async function performSearch(query, currentIndex, total) { const randomString = generateRandomString(4); const randomCvid = generateRandomString(32); const searchUrl = `${location.origin}/search?q=${encodeURIComponent(query)}&form=${randomString}&cvid=${randomCvid}`; log(`[${currentIndex}/${total}] 搜索词: "${query}"`); if (isPageVisible) { await simulateMouseMovement(randomInt(100,500), randomInt(100,300), randomInt(200,800), randomInt(200,600)); } window.location.href = searchUrl; // 跳转后脚本会重新加载,后续由 autoContinue 处理 } // 完成当前搜索结果页的行为(滚动、点击),更新计数,调度下一次搜索 async function finishCurrentAndContinue() { let done = GM_getValue(STORAGE_KEYS.DONE, 0); let total = GM_getValue(STORAGE_KEYS.TOTAL, CONFIG.maxSearches); done = Number(done); total = Number(total); let newDone = done + 1; GM_setValue(STORAGE_KEYS.DONE, newDone); updateTitle(newDone, total); log(`已完成 ${newDone}/${total}`); // 模拟真实浏览行为(仅在可见时) await simulateScroll(); await clickRandomResult(); // 检查是否需要执行长暂停 let lastPause = GM_getValue(STORAGE_KEYS.LAST_PAUSE_COUNT, 0); lastPause = Number(lastPause); if (newDone - lastPause >= CONFIG.pauseAfter && newDone < total) { log(`执行暂停 ${CONFIG.pauseDuration / 1000} 秒...`); GM_setValue(STORAGE_KEYS.LAST_PAUSE_COUNT, newDone); await sleep(CONFIG.pauseDuration); } // 判断任务是否完成 if (newDone >= total) { log("所有搜索任务完成!"); GM_deleteValue(STORAGE_KEYS.TASK_ACTIVE); GM_deleteValue(STORAGE_KEYS.TOTAL); GM_deleteValue(STORAGE_KEYS.DONE); GM_deleteValue(STORAGE_KEYS.LAST_PAUSE_COUNT); if (typeof GM_notification !== 'undefined') { GM_notification({ title: "Rewards 任务完成", text: `已完成 ${total} 次搜索!` }); } return; } // 准备下一次搜索词(循环使用词库) const nextWord = searchWords[newDone % searchWords.length]; const confusedWord = autoStrTrans(nextWord); const waitTime = randomInt(CONFIG.delayMin, CONFIG.delayMax); log(`等待 ${(waitTime/1000).toFixed(1)} 秒后开始第 ${newDone+1} 次搜索...`); await sleep(waitTime); await performSearch(confusedWord, newDone+1, total); } // 自动继续未完成的任务(每次页面加载时调用) async function autoContinue() { const isActive = GM_getValue(STORAGE_KEYS.TASK_ACTIVE, false); if (!isActive) { log("当前无活动任务,请通过菜单“开始”启动。"); return; } let done = GM_getValue(STORAGE_KEYS.DONE, 0); let total = GM_getValue(STORAGE_KEYS.TOTAL, CONFIG.maxSearches); done = Number(done); total = Number(total); log(`检测到活动任务: 已完成 ${done}/${total}`); updateTitle(done, total); if (done >= total) { log("任务已完成,清除状态。"); GM_deleteValue(STORAGE_KEYS.TASK_ACTIVE); GM_deleteValue(STORAGE_KEYS.TOTAL); GM_deleteValue(STORAGE_KEYS.DONE); GM_deleteValue(STORAGE_KEYS.LAST_PAUSE_COUNT); return; } const isSearchPage = window.location.pathname.includes('/search') && window.location.search.includes('q='); if (isSearchPage && done > 0) { // 在搜索结果页且已有计数,继续处理 await finishCurrentAndContinue(); } else if (!isSearchPage && done === 0) { // 在首页且未开始,执行第一次搜索 log("当前在首页,开始第一次搜索..."); const firstWord = searchWords[0]; const confusedWord = autoStrTrans(firstWord); await performSearch(confusedWord, 1, total); } else if (isSearchPage && done === 0) { // 意外情况:搜索结果页但计数器为0(如手动进入),仍然继续 log("在搜索结果页但计数器为0,继续执行..."); await finishCurrentAndContinue(); } else if (!isSearchPage && done > 0) { log("警告:任务进行中但当前不在搜索结果页,请确保停留在 Bing 页面。"); } } // ==================== Tampermonkey 菜单命令 ==================== async function startTask() { if (GM_getValue(STORAGE_KEYS.TASK_ACTIVE, false)) { log("任务已在运行中,请勿重复启动。"); return; } if (!searchWords.length) { await initSearchWords(); } GM_setValue(STORAGE_KEYS.TASK_ACTIVE, true); GM_setValue(STORAGE_KEYS.TOTAL, CONFIG.maxSearches); GM_setValue(STORAGE_KEYS.DONE, 0); GM_setValue(STORAGE_KEYS.LAST_PAUSE_COUNT, 0); log(`任务已启动,目标次数: ${CONFIG.maxSearches}`); await autoContinue(); } function stopTask() { if (!GM_getValue(STORAGE_KEYS.TASK_ACTIVE, false)) { log("当前没有运行中的任务。"); return; } GM_deleteValue(STORAGE_KEYS.TASK_ACTIVE); GM_deleteValue(STORAGE_KEYS.TOTAL); GM_deleteValue(STORAGE_KEYS.DONE); GM_deleteValue(STORAGE_KEYS.LAST_PAUSE_COUNT); log("任务已手动停止,进度已清除。"); // 恢复页面标题,移除进度显示 const titleElem = document.getElementsByTagName("title")[0]; if (titleElem) { titleElem.innerText = titleElem.innerText.replace(/\[\d+\s*\/\s*\d+\]\s*/, ''); } } // 注册菜单项(热键:O 开始,S 停止) GM_registerMenuCommand('▶ 开始 (O)', startTask, 'O'); GM_registerMenuCommand('⏹️ 停止 (S)', stopTask, 'S'); // ==================== 脚本初始化 ==================== (async () => { await initSearchWords(); // 页面加载后稍等片刻,让 DOM 稳定再自动继续任务 setTimeout(() => autoContinue().catch(err => console.error(err)), 1500); })(); })();