Microsoft Bing Rewards每日任务脚本

自动完成微软积分每日搜索任务

  1. // ==UserScript==
  2. // @name Microsoft Bing Rewards每日任务脚本
  3. // @namespace https://gf.qytechs.cn/users/1362311
  4. // @version 4.0.11
  5. // @description 自动完成微软积分每日搜索任务
  6. // @author honguangli
  7. // @license MIT
  8. // @match https://www.bing.com/*
  9. // @match https://cn.bing.com/*
  10. // @match https://rewards.bing.com/*
  11. // @icon https://www.bing.com/favicon.ico
  12. // @run-at document-end
  13. // @grant GM_registerMenuCommand
  14. // @grant GM_addStyle
  15. // @grant GM_setValue
  16. // @grant GM_getValue
  17. // @grant GM_xmlhttpRequest
  18. // @connect api.gumengya.com
  19. // ==/UserScript==
  20.  
  21. (function() {
  22. 'use strict';
  23. // 仅在顶层框架运行
  24. if (window.top !== window.self) {
  25. return;
  26. }
  27.  
  28. const keywordApi = 'https://api.gumengya.com/Api/'; // 词条接口
  29.  
  30. const rewardHost = 'https://rewards.bing.com'; // 积分页面
  31. const searchHost = 'https://www.bing.com'; // 搜索页面
  32. const pathnames = ['/', '/search']; // 触发搜索页面
  33. const autoRunSearch = '?runMode=auto&runKey=1362311'; // 自动触发任务参数
  34.  
  35. const searchTimes = 40; // 自动搜索次数
  36.  
  37. const searchDelaySecondsMin = 15; // 每次搜索最小延迟时间,单位秒
  38. const searchDelaySecondsMax = 30; // 每次搜索最大延迟时间,单位秒
  39. const searchDelaySecondsFirst = 3; // 首次搜索延迟时间,单位秒,设置为0时立即触发
  40. const closeTaskCardDelaySeconds = 0; // 搜索完成后弹窗自动关闭延迟时间,单位秒,设置为0时需手动关闭
  41.  
  42. const searchSafeDelayTimes = 4; // 每搜索n次后触发长暂停
  43. const searchSafeDelaySeconds = 0 * 60; // 每次触发长暂停时长,单位秒
  44.  
  45. const clickDelaySecondsFirst = 3; // 首次搜索延迟时间,单位秒
  46.  
  47. const startBtn = true; // 是否在搜索框附近插入任务启动按钮
  48. const startBtnText = '获取积分'; // 任务启动按钮文本
  49.  
  50. const dailyBtn = true; // 是否在搜索框附近插入获取每日活动积分按钮
  51. const dailyBtnText = '获取积分2'; // 获取每日活动积分按钮文本
  52.  
  53. // 搜索词条库
  54. const searchKeySource = [
  55. { name: '百度', action: 'BaiduHot' },
  56. { name: '抖音', action: 'DouYinHot' },
  57. { name: '搜狗', action: 'SoGouHot' },
  58. { name: '360', action: 'SoHot' },
  59. { name: '微博', action: 'WeiBoHot' },
  60. { name: '知乎', action: 'ZhiHuHot' },
  61. { name: '今日头条', action: 'TouTiaoHot' },
  62. { name: '快手', action: 'KuaiShouHot' }
  63. ];
  64.  
  65. // 默认词条
  66. const defaultKeywords = [
  67. '众芳摇落独暄妍', '占尽风情向小园',
  68. '疏影横斜水清浅', '暗香浮动月黄昏',
  69. '霜禽欲下先偷眼', '粉蝶如知合断魂',
  70. '幸有微吟可相狎', '不须檀板共金樽',
  71. '剪绡零碎点酥乾', '向背稀稠画亦难',
  72. '日薄从甘春至晚', '霜深应怯夜来寒',
  73. '澄鲜只共邻僧惜', '冷落犹嫌俗客看',
  74. '忆着江南旧行路', '酒旗斜拂堕吟鞍',
  75.  
  76. '东风夜放花千树', '更吹落', '星如雨',
  77. '宝马雕车香满路',
  78. '凤箫声动', '玉壶光转', '一夜鱼龙舞',
  79. '蛾儿雪柳黄金缕', '笑语盈盈暗香去',
  80. '众里寻他千百度',
  81. '蓦然回首', '那人却在', '灯火阑珊处',
  82.  
  83. '君不见黄河之水天上来', '奔流到海不复回',
  84. '君不见高堂明镜悲白发', '朝如青丝暮成雪',
  85. '人生得意须尽欢', '莫使金樽空对月',
  86. '天生我材必有用', '千金散尽还复来',
  87. '烹羊宰牛且为乐', '会须一饮三百杯',
  88. '岑夫子', '丹丘生', '将进酒', '杯莫停',
  89. '与君歌一曲', '请君为我倾耳听',
  90. '钟鼓馔玉不足贵', '但愿长醉不愿醒',
  91. '古来圣贤皆寂寞', '惟有饮者留其名',
  92. '陈王昔时宴平乐', '斗酒十千恣欢谑',
  93. '主人何为言少钱', '径须沽取对君酌',
  94. '五花马', '千金裘', '呼儿将出换美酒', '与尔同销万古愁'
  95. ];
  96.  
  97. const timeKey = 'time'; // 时间戳
  98. const countKey = 'count'; // 计数器
  99. const pointsKey = 'points'; // 当日初始积分
  100. const searchPointsKey = 'searchPoints'; // 当日搜索任务初始积分
  101. const keywordsKey = 'search'; // 搜索词条
  102. const searchParamKey = 'param'; // 搜索参数
  103.  
  104. // web worker
  105. let delayWorker = null; // 搜索任务
  106. let scrollWorker = null; // 模拟浏览
  107.  
  108. GM_addStyle('#reward-task { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background-color: rgba(0, 0, 0, .2); z-index: 99999; }');
  109. GM_addStyle('#reward-task > .reward-task-content { max-width: 460px; margin: calc(50vh - 32px) auto 0; padding: 20px; background-color: #ffffff; border: 1px solid #e4e7ed; border-radius: 20px; color: #303133; overflow: hidden; transition: 0.3s; box-shadow: 0px 0px 12px rgba(0,0,0,0.12); }');
  110. GM_addStyle('#reward-task > .reward-task-content > div { display: flex; flex-flow: row wrap; align-items: center; column-gap: 12px; }');
  111. GM_addStyle('#reward-task > .reward-task-content > div > .item { width: 160px; padding: 7px 0; }');
  112. GM_addStyle('#reward-task > .reward-task-content > div > .tips { padding: 7px 0; color: #E6A23C; }');
  113. GM_addStyle('#reward-task > .reward-task-content > div > .btn-wrap { flex-grow: 1; text-align: right; }');
  114. GM_addStyle('.reward-task-btn { display: inline-block; line-height: 1; white-space: nowrap; cursor: pointer; background: #fff; border: 1px solid #dcdfe6; -webkit-appearance: none; text-align: center; -webkit-box-sizing: border-box; box-sizing: border-box; outline: 0; margin: 0; -webkit-transition: .1s; transition: .1s; font-weight: 500; padding: 8px 16px; font-size: 14px; border-radius: 4px; color: #606266; background-color: #ffffff; border: 1px solid #dcdfe6; border-color: #dcdfe6; }');
  115. GM_addStyle('.reward-task-btn.warning { color: #fff; background-color: #ebb563; border-color: #ebb563; }');
  116. GM_addStyle('#ScopeRow { margin-top: 48px; }');
  117.  
  118. // 注册(不可用)菜单
  119. const registerMenuCommand = () => {
  120. // 启动搜索任务
  121. GM_registerMenuCommand('获取积分', () => {
  122. start();
  123. });
  124.  
  125. // 启动搜索任务,含每日活动任务
  126. GM_registerMenuCommand('获取积分2(含每日活动)', () => {
  127. navigateToRewardPage();
  128. });
  129.  
  130. // 停止任务
  131. GM_registerMenuCommand('停止', () => {
  132. stop();
  133. removeTaskCard();
  134. });
  135.  
  136. // 更新词条
  137. GM_registerMenuCommand('更新词条', () => {
  138. new Promise((resolve, reject) => {
  139. let source = Math.floor(Math.random() * searchKeySource.length);
  140. queryKeywordList(source, resolve, reject);
  141. }).then( () => {
  142. alert('获取词条成功');
  143. }).catch( err => {
  144. alert('获取词条失败:' + err.message);
  145. });
  146. });
  147.  
  148. // 生成随机词条
  149. GM_registerMenuCommand('生成随机词条', () => {
  150. const keywords = generateKeywordList(100);
  151. const today = new Date();
  152. today.setHours(0);
  153. today.setMinutes(0);
  154. today.setSeconds(0);
  155. today.setMilliseconds(0);
  156. GM_setValue(keywordsKey, {
  157. time: today.getTime(),
  158. source: 0,
  159. keywords: keywords,
  160. });
  161. });
  162. };
  163.  
  164. // 启动搜索任务
  165. const start = () => {
  166. GM_setValue(countKey, 1);
  167. GM_setValue(searchPointsKey, -1);
  168. search();
  169. };
  170.  
  171. // 结束搜索任务
  172. const stop = () => {
  173. GM_setValue(countKey, 0);
  174. GM_setValue(searchPointsKey, -1);
  175. if (scrollWorker) {
  176. scrollWorker.postMessage({ type: "end" });
  177. }
  178. };
  179.  
  180. // 跳转积分页面并运行每日活动任务
  181. const navigateToRewardPage = () => {
  182. location.href = rewardHost + autoRunSearch;
  183. };
  184.  
  185. // 跳转搜索页面并运行搜索任务
  186. const navigateToSearchPage = () => {
  187. location.href = searchHost + autoRunSearch;
  188. };
  189.  
  190. // 自动点击完成每日活动积分
  191. const autoClickRewardActivity = () => {
  192. insertActivityTaskCard( () => {
  193. navigateToSearchPage();
  194. });
  195. };
  196.  
  197. // 搜索
  198. const search = () => {
  199. const count = GM_getValue(countKey);
  200. if (!count || count <= 0 || count > searchTimes + 1) {
  201. stop();
  202. return;
  203. }
  204.  
  205. // 延迟时间
  206. const delay = count === 1 ? searchDelaySecondsFirst : Math.floor(Math.random() * (searchDelaySecondsMax - searchDelaySecondsMin + 1)) + searchDelaySecondsMin + (count % searchSafeDelayTimes !== 1 ? 0 : searchSafeDelaySeconds);
  207.  
  208. // 添加任务进度卡片
  209. insertTaskCard(count - 1, delay, () => {
  210. // 获取词条
  211. getSearchInfo().then( keyword => {
  212. const queryInput = document.getElementById('sb_form_q');
  213. const param = `?q=${ encodeURIComponent(keyword) }&form=${ Math.random() > 0.4 ? 'QBLH' : 'QBRE' }&sp=-1&lq=0&pq=${ queryInput ? encodeURIComponent(queryInput.value) || '' : '' }&sc=0-0&qs=n&sk=&cvid=${ generateRandomString(32) }&ghsh=0&ghacc=0&ghpl=`;
  214.  
  215. // 更新计数器
  216. GM_setValue(countKey, count+1);
  217. // 更新搜索参数
  218. GM_setValue(searchParamKey, param);
  219.  
  220. // 触发搜索
  221. location.href = searchHost + '/search' + param;
  222. }).catch( err => {
  223. stop();
  224. removeTaskCard();
  225. alert('获取词条失败:' + err.message);
  226. });
  227. });
  228.  
  229. if (count > searchTimes) {
  230. return;
  231. }
  232.  
  233. // 模拟浏览网页
  234. pretendHuman();
  235. };
  236.  
  237. // 获取词条
  238. const getSearchInfo = () => {
  239. return new Promise((resolve, reject) => {
  240. const today = new Date();
  241. today.setHours(0);
  242. today.setMinutes(0);
  243. today.setSeconds(0);
  244. today.setMilliseconds(0);
  245.  
  246. let source = Math.floor(Math.random() * searchKeySource.length);
  247. const saveConfig = GM_getValue(keywordsKey);
  248. // 当日缓存的搜索词还未用完
  249. if (saveConfig && saveConfig.time === today.getTime()) {
  250. if (saveConfig.keywords.length > 0) {
  251. const keyword = saveConfig.keywords[0];
  252. saveConfig.keywords.splice(0, 1);
  253. GM_setValue(keywordsKey, saveConfig);
  254. resolve(keyword);
  255. return;
  256. } else {
  257. source = (saveConfig.source + 1) % searchKeySource.length;
  258. }
  259. }
  260.  
  261. // 更新词条
  262. queryKeywordList(source, resolve, reject);
  263. });
  264. };
  265.  
  266. // 更新词条
  267. const queryKeywordList = (source, resolve, reject) => {
  268. const today = new Date();
  269. today.setHours(0);
  270. today.setMinutes(0);
  271. today.setSeconds(0);
  272. today.setMilliseconds(0);
  273.  
  274. // 获取新词条
  275. const queryKeyword = (config, retry) => {
  276. GM_xmlhttpRequest({
  277. method: 'GET',
  278. url: keywordApi + searchKeySource[config.source].action,
  279. headers: {
  280. "Content-Type": "application/json"
  281. },
  282. responseType: 'json',
  283. onload: response => {
  284. if (response.status !== 200 || !response.response) {
  285. handleErr(new Error('调用Api失败'), retry, config);
  286. return;
  287. }
  288. if (response.response.code !== 200) {
  289. handleErr(new Error(response.response.msg), retry, config);
  290. return;
  291. }
  292.  
  293. config.keywords = Array.from(new Set(response.response.data.map(item => searchKeySource[config.source].name + ' ' + item.title + ' ' + generateRandomString(3))));
  294. config.keywords.splice(50);
  295. const keyword = config.keywords[0];
  296. config.keywords.splice(0, 1);
  297. GM_setValue(keywordsKey, config);
  298. resolve(keyword);
  299. },
  300. onerror: err => {
  301. handleErr(err, retry, config);
  302. },
  303. });
  304. };
  305.  
  306. // 失败处理,支持重试
  307. const handleErr = (err, retry, config) => {
  308. if (retry <= 0) {
  309. console.error(`获取词条[${ searchKeySource[config.source].name }]:失败终止`, err);
  310. //reject(err);
  311. config.keywords = generateKeywordList(100);
  312. const keyword = config.keywords[0];
  313. config.keywords.splice(0, 1);
  314. GM_setValue(keywordsKey, config);
  315. resolve(keyword);
  316. return;
  317. }
  318.  
  319. console.warn(`获取词条[${ searchKeySource[config.source].name }]:失败重试[第${ searchKeySource.length - retry }次]`, err);
  320. setTimeout(() => {
  321. queryKeyword({
  322. time: config.time,
  323. source: (config.source + 1) % searchKeySource.length,
  324. keywords: [],
  325. }, retry-1);
  326. }, 500);
  327. };
  328.  
  329. // 获取新词条
  330. queryKeyword({
  331. time: today.getTime(),
  332. source: source,
  333. keywords: [],
  334. }, 0, searchKeySource.length - 1);
  335. };
  336.  
  337. // 生成随机词条
  338. const generateKeywordList = (size) => {
  339. if (size <= 0) {
  340. return;
  341. }
  342.  
  343. const keywords = [];
  344. for (let i = 0; i < defaultKeywords.length && keywords.length < size * 1.2; i++) {
  345. if (defaultKeywords[i].length < 3) {
  346. keywords.push(defaultKeywords[i]);
  347. continue;
  348. }
  349.  
  350. let j = 0;
  351. while (j < defaultKeywords[i].length) {
  352. let z = Math.min(defaultKeywords[i].length, Math.floor(Math.random() * 3) + 2 + j);
  353. keywords.push(defaultKeywords[i].slice(j, z));
  354. j = z;
  355. }
  356.  
  357. if (Math.random() >= 0.5) {
  358. keywords.push(generateRandomStringZh(5));
  359. }
  360. }
  361.  
  362. return Array.from(new Set(keywords)).slice(0, size);
  363. };
  364.  
  365. // 插入启动按钮
  366. const insertStartBtn = () => {
  367. if (document.getElementById('reward-task-start')) {
  368. document.getElementById('reward-task-start').remove();
  369. }
  370. if (!startBtn) {
  371. return;
  372. }
  373. // 获取搜索表单
  374. const queryForm = document.getElementById('sb_form');
  375.  
  376. // 添加启动按钮
  377. const btn = document.createElement('button');
  378. btn.appendChild(document.createTextNode(startBtnText));
  379. btn.setAttribute('id', 'reward-task-start');
  380. btn.setAttribute('type', 'button');
  381. btn.classList.add('reward-task-btn');
  382. btn.style.setProperty('margin', '8px');
  383. btn.style.setProperty('padding', '8px 24px');
  384. btn.style.setProperty('border-radius', '24px');
  385. btn.onclick = () => {
  386. start();
  387. };
  388.  
  389. // 首页表单会重置组件内容,需要等待几秒后再插入
  390. setTimeout( () => {
  391. queryForm.appendChild(btn);
  392. }, location.pathname !== '/' ? 0 : 5000);
  393. };
  394.  
  395. // 插入启动按钮2
  396. const insertDailyBtn = () => {
  397. if (document.getElementById('reward-task-daily')) {
  398. document.getElementById('reward-task-daily').remove();
  399. }
  400. if (!dailyBtn) {
  401. return;
  402. }
  403. // 获取搜索表单
  404. const queryForm = document.getElementById('sb_form');
  405.  
  406. // 添加启动按钮2
  407. const btn = document.createElement('button');
  408. btn.appendChild(document.createTextNode(dailyBtnText));
  409. btn.setAttribute('id', 'reward-task-daily');
  410. btn.setAttribute('type', 'button');
  411. btn.classList.add('reward-task-btn');
  412. btn.style.setProperty('margin', '8px');
  413. btn.style.setProperty('padding', '8px 24px');
  414. btn.style.setProperty('border-radius', '24px');
  415. btn.onclick = () => {
  416. navigateToRewardPage();
  417. };
  418.  
  419. // 首页表单会重置组件内容,需要等待几秒后再插入
  420. setTimeout( () => {
  421. queryForm.appendChild(btn);
  422. }, location.pathname !== '/' ? 0 : 5000);
  423. };
  424.  
  425. // 插入搜索任务卡片
  426. const insertTaskCard = (times, delay, finish) => {
  427. removeTaskCard();
  428.  
  429. // 添加搜索任务卡片
  430. const h = `<div id="reward-task">
  431. <div class="reward-task-content">
  432. <div>
  433. <p id="reward-points" class="item">当前积分:${ getCurrPoints() }</p>
  434. </div>
  435. <div>
  436. <p id="task-points" class="item">本次获得积分:${ getTaskPoints() }</p>
  437. <p id="task-today-points" class="item">当日获得积分:${ getTodayPoints() }</p>
  438. </div>
  439. <div>
  440. <p class="item">进度:${ times } / ${ searchTimes }</p>
  441. <p id="reward-task-delay" class="item">${ times >= searchTimes ? `已完成${ closeTaskCardDelaySeconds > 0 ? ',' + closeTaskCardDelaySeconds + '秒后自动关闭' : '' }` : `等待时间:${ delay } ` }</p>
  442. <div class="btn-wrap">${ times >= searchTimes ? '<button id="reward-task-cancel" type="button" class="reward-task-btn warning">关闭</button>' : '<button id="reward-task-stop" type="button" class="reward-task-btn warning">停止</button>' }</div>
  443. </div>
  444. </div>
  445. </div>
  446. `;
  447. document.body.insertAdjacentHTML('beforeEnd', h);
  448.  
  449. // 停止按钮
  450. const btnStop = document.querySelector('#reward-task-stop');
  451. if (btnStop) {
  452. btnStop.onclick = () => {
  453. stop();
  454. removeTaskCard();
  455. };
  456. }
  457.  
  458. // 关闭按钮
  459. const btnCancel = document.querySelector('#reward-task-cancel');
  460. if (btnCancel) {
  461. btnCancel.onclick = () => {
  462. stop();
  463. removeTaskCard();
  464. };
  465. }
  466.  
  467. // 任务完成,自动延迟关闭任务窗口
  468. if (times >= searchTimes && closeTaskCardDelaySeconds > 0) {
  469. setTimeout( () => {
  470. removeTaskCard();
  471. }, closeTaskCardDelaySeconds * 1000);
  472. }
  473.  
  474. // 倒计时
  475. // 任务完成后再执行10次以便获取积分信息
  476. let remainDelay = times >= searchTimes ? 10 : delay;
  477. delayWorker = getWorker(getCountDown, { times: remainDelay });
  478. delayWorker.onmessage = e => {
  479. // 更新积分
  480. const domCurrPoints = document.getElementById('reward-points');
  481. const domTaskPoints = document.getElementById('task-points');
  482. const domTaskTodayPoints = document.getElementById('task-today-points');
  483. domCurrPoints.innerText = `当前积分:${ getCurrPoints() }`;
  484. domTaskPoints.innerText = `本次获得积分:${ getTaskPoints() }`;
  485. domTaskTodayPoints.innerText = `当日获得积分:${ getTodayPoints() }`;
  486.  
  487. // 任务已完成
  488. if (times >= searchTimes) {
  489. return;
  490. }
  491.  
  492. // 倒计时完成
  493. if (e.data.times === 0) {
  494. finish();
  495. return;
  496. }
  497.  
  498. // 更新倒计时
  499. const domDelay = document.getElementById('reward-task-delay');
  500. if (!domDelay) {
  501. return;
  502. }
  503. domDelay.innerText = `等待时间:${ e.data.times } 秒`;
  504. };
  505. delayWorker.postMessage({ type: "start", interval: 1000 });
  506. };
  507.  
  508. // 插入积分活动任务卡片
  509. const insertActivityTaskCard = (finish) => {
  510. removeTaskCard();
  511.  
  512. // 每日活动
  513. const daily = {
  514. todo: [],
  515. total: 0,
  516. finish: 0,
  517. };
  518. const dailyActivitys = document.querySelectorAll('mee-rewards-daily-set-section mee-card.ng-scope.c-card:not([disabled=disabled]) a.ds-card-sec');
  519. for (let i =0; i < dailyActivitys.length; i++) {
  520. daily.total++;
  521. if (dailyActivitys[i].querySelectorAll('.mee-icon.mee-icon-AddMedium').length > 0) {
  522. daily.todo.push(dailyActivitys[i]);
  523. } else {
  524. daily.finish++;
  525. }
  526. }
  527.  
  528. // 更多活动
  529. const more = {
  530. todo: [],
  531. total: 0,
  532. finish: 0,
  533. };
  534. const moreActivitys = document.querySelectorAll('mee-rewards-more-activities-card mee-card.ng-scope.c-card:not([disabled=disabled]) a.ds-card-sec');
  535. for (let i =0; i < moreActivitys.length; i++) {
  536. more.total++;
  537. if (moreActivitys[i].querySelectorAll('.mee-icon.mee-icon-AddMedium').length > 0) {
  538. more.todo.push(moreActivitys[i]);
  539. } else {
  540. more.finish++;
  541. }
  542. }
  543.  
  544. // 没有待完成的任务
  545. if (daily.todo.length === 0 && more.todo.length === 0) {
  546. //finish();
  547. //return;
  548. }
  549.  
  550. // 倒计时
  551. let delay = more.todo.length + clickDelaySecondsFirst;
  552.  
  553. // 每日活动只在12点后触发才可获得积分
  554. const today = new Date();
  555. if (today.getHours() >= 12) {
  556. delay += daily.todo.length;
  557. }
  558.  
  559. // 添加搜索任务卡片
  560. const h = `<div id="reward-task">
  561. <div class="reward-task-content">
  562. <div>
  563. <p id="reward-points" class="item">当前积分:${ getCurrPoints() }</p>
  564. </div>
  565. <div>
  566. <p id="task-points" class="item">本次获得积分:${ getTaskPoints() }</p>
  567. <p id="task-today-points" class="item">当日获得积分:${ getTodayPoints() }</p>
  568. </div>
  569. <div>
  570. <p id="daily-progress" class="item">每日任务:${ daily.finish } / ${ daily.total }</p>
  571. <p id="more-progress" class="item">更多任务:${ more.finish } / ${ more.total }</p>
  572. </div>
  573. <div>
  574. <p id="reward-task-delay" class="item">等待时间:${ delay } 秒</p>
  575. <div class="btn-wrap"><button id="reward-task-stop" type="button" class="reward-task-btn warning">停止</button></div>
  576. </div>
  577. <div>
  578. <p class="tips">注意:每日任务必须在12:00后执行才能获得积分!</p>
  579. </div>
  580. </div>
  581. </div>
  582. `;
  583. document.body.insertAdjacentHTML('beforeEnd', h);
  584.  
  585. // 停止按钮
  586. const btnStop = document.querySelector('#reward-task-stop');
  587. if (btnStop) {
  588. btnStop.onclick = () => {
  589. stop();
  590. removeTaskCard();
  591. };
  592. }
  593.  
  594. // 倒计时
  595. delayWorker = getWorker(getCountDown, { times: delay });
  596. delayWorker.onmessage = e => {
  597. // 更新积分
  598. const domCurrPoints = document.getElementById('reward-points');
  599. const domTaskPoints = document.getElementById('task-points');
  600. const domTaskTodayPoints = document.getElementById('task-today-points');
  601. domCurrPoints.innerText = `当前积分:${ getCurrPoints() }`;
  602. domTaskPoints.innerText = `本次获得积分:${ getTaskPoints() }`;
  603. domTaskTodayPoints.innerText = `当日获得积分:${ getTodayPoints() }`;
  604.  
  605. // 倒计时完成
  606. if (e.data.times === 0) {
  607. finish();
  608. return;
  609. }
  610.  
  611. // 更新倒计时
  612. const domDelay = document.getElementById('reward-task-delay');
  613. if (!domDelay) {
  614. return;
  615. }
  616. domDelay.innerText = `等待时间:${ e.data.times } 秒`;
  617.  
  618. // 首次延迟时间
  619. if (e.data.times > delay - clickDelaySecondsFirst) {
  620. return;
  621. }
  622.  
  623. // 更新任务进度
  624. const domDailyProgress = document.getElementById('daily-progress');
  625. const domMoreProgress = document.getElementById('more-progress');
  626. const index = delay - clickDelaySecondsFirst - e.data.times;
  627. if (today.getHours() >= 12) {
  628. if (index < daily.todo.length) {
  629. daily.todo[index].click();
  630. daily.finish++;
  631. domDailyProgress.innerText = `每日任务:${ daily.finish } / ${ daily.total }`;
  632. } else if (index - daily.todo.length < more.todo.length) {
  633. more.todo[index - daily.todo.length].click();
  634. more.finish++;
  635. domMoreProgress.innerText = `更多任务:${ more.finish } / ${ more.total }`;
  636. }
  637. } else {
  638. more.tody[index].click();
  639. more.finish++;
  640. domMoreProgress.innerText = `更多任务:${ more.finish } / ${ more.total }`;
  641. }
  642. };
  643. delayWorker.postMessage({ type: "start", interval: 1000 });
  644. };
  645.  
  646. // 移除搜索任务卡片
  647. const removeTaskCard = () => {
  648. if (delayWorker) {
  649. delayWorker.postMessage({ type: 'end' });
  650. }
  651. if (!document.getElementById('reward-task')) {
  652. return;
  653. }
  654. document.getElementById('reward-task').remove();
  655. };
  656.  
  657. // 模拟浏览网页
  658. const pretendHuman = () => {
  659. if (scrollWorker) {
  660. scrollWorker.postMessage({ type: "end" });
  661. }
  662.  
  663. window.scrollTo({
  664. top: 0,
  665. behavior: 'smooth'
  666. });
  667.  
  668. // 启用定时器缓慢滑动到底部,期间随机触发停留和向上滑动,完成滑动到底部后再次滑动到顶部后停止滑动
  669. scrollWorker = getWorker(getCountDown, { times: 120 });
  670. scrollWorker.onmessage = e => {
  671. if (e.data.times === 0 || document.documentElement.scrollTop >= document.documentElement.scrollHeight - document.documentElement.clientHeight) {
  672. scrollWorker.postMessage({ type: "end" });
  673. window.scrollTo({
  674. top: 0,
  675. behavior: 'smooth'
  676. });
  677. return;
  678. }
  679. const number = Math.floor(Math.random() * 10) + 1;
  680. if (number < 3) {
  681. window.scrollTo({
  682. top: document.documentElement.scrollTop - 200,
  683. behavior: 'smooth'
  684. });
  685. } else if (number > 5) {
  686. window.scrollTo({
  687. top: document.documentElement.scrollTop + 100,
  688. behavior: 'smooth'
  689. });
  690. }
  691. };
  692. scrollWorker.postMessage({ type: "start", interval: 500 });
  693. };
  694.  
  695. // 获取当前积分
  696. // 兼容PC端搜索页、积分页、移动端搜索页
  697. const getCurrPoints = () => {
  698. let pointsStr = '';
  699.  
  700. const searchPagePointsWrap = document.querySelector('#rh_rwm .points-container');
  701. const rewardPagePointsWrap = document.querySelector('#balanceToolTipDiv.textAndIcon mee-rewards-counter-animation.ng-isolate-scope');
  702. const mobilePagePointsWrap = document.querySelector('#fly_id_rc');
  703. if (searchPagePointsWrap) {
  704. // PC端搜索页
  705. // 需要判断当前显示积分是否已完成计数器动画效果,根据完成情况从不同渠道获取积分值
  706. if (!searchPagePointsWrap.classList.contains('balance-animation')) {
  707. pointsStr = searchPagePointsWrap.innerText.trim();
  708. } else {
  709. pointsStr = document.documentElement.style.getPropertyValue('--rw-gp-balance-to');
  710. }
  711. } else if (rewardPagePointsWrap) {
  712. // 积分页
  713. // 需要判断当前显示积分是否已完成计数器动画效果,需完成后才可获取积分值
  714. const span = document.querySelector('#balanceToolTipDiv.textAndIcon mee-rewards-counter-animation.ng-isolate-scope span');
  715. if (span) {
  716. const v1 = rewardPagePointsWrap.innerText.trim().replace(/\D/g, '');
  717. const v2 = span.getAttribute('aria-label').trim().replace(/\D/g, '');
  718. pointsStr = v1 === v2 ? v1 : '';
  719. }
  720. } else if (mobilePagePointsWrap) {
  721. // 移动端搜索页
  722. pointsStr = mobilePagePointsWrap.innerText.trim();
  723.  
  724. // 关闭菜单
  725. const menuClose = document.querySelector('#HBFlyoutClose');
  726. if (menuClose) {
  727. menuClose.click();
  728. }
  729. } else {
  730. // 尝试 移动端搜索页 开启菜单
  731. const menuOpen = document.querySelector('#mHamburger');
  732. if (menuOpen) {
  733. menuOpen.click();
  734. }
  735. }
  736.  
  737. const points = parseInt(pointsStr);
  738. if (isNaN(points)) {
  739. return null;
  740. }
  741. return points;
  742. };
  743.  
  744. // 计算已取得积分
  745. const getTaskPoints = () => {
  746. const currPoints = getCurrPoints();
  747. if (currPoints === null) {
  748. return 0;
  749. }
  750.  
  751. let startPoints = GM_getValue(searchPointsKey);
  752. if (startPoints === -1) {
  753. GM_setValue(searchPointsKey, currPoints);
  754. return 0;
  755. }
  756. return currPoints - startPoints;
  757. };
  758.  
  759. // 计算当日已取得积分
  760. const getTodayPoints = () => {
  761. const currPoints = getCurrPoints();
  762. if (currPoints === null) {
  763. return 0;
  764. }
  765.  
  766. const today = new Date();
  767. today.setHours(0);
  768. today.setMinutes(0);
  769. today.setSeconds(0);
  770. today.setMilliseconds(0);
  771.  
  772. let startPoints = GM_getValue(pointsKey);
  773. if (GM_getValue(timeKey) !== today.getTime()) {
  774. GM_setValue(timeKey, today.getTime());
  775. GM_setValue(pointsKey, currPoints);
  776. return 0;
  777. }
  778. return currPoints - startPoints;
  779. };
  780.  
  781. // 获取web worker
  782. function getWorker(worker, param) {
  783. const code = worker.toString();
  784. const blob = new Blob([`(${code})(${JSON.stringify(param)})`]);
  785.  
  786. return new Worker(URL.createObjectURL(blob));
  787. }
  788.  
  789. // 倒计时函数
  790. function getCountDown(param) {
  791. let _timer = null;
  792. let times = param.times;
  793.  
  794. this.onmessage = e => {
  795. const data = e.data;
  796. if(data.type === 'start') {
  797. _timer = setInterval(() => {
  798. times--;
  799. this.postMessage({ times });
  800. if (times <= 0) {
  801. clearInterval(_timer);
  802. }
  803. } , data.interval);
  804. } else if (data.type === 'end') {
  805. clearInterval(_timer);
  806. }
  807. };
  808. }
  809.  
  810. // 生成指定长度随机字符串
  811. const generateRandomString = length => {
  812. const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  813. let result = '';
  814. const charactersLength = characters.length;
  815. for (let i = 0; i < length; i++) {
  816. result += characters.charAt(Math.floor(Math.random() * charactersLength));
  817. }
  818. return result;
  819. };
  820.  
  821. // 生成指定长度随机中文字符串
  822. const generateRandomStringZh = length => {
  823. let result = '';
  824. for (let i = 0; i < length; i++) {
  825. result += String.fromCodePoint(Math.floor(Math.random() * (40869 - 19968)) + 19968);
  826. }
  827. return result;
  828. };
  829.  
  830. // 注册(不可用)菜单
  831. registerMenuCommand();
  832.  
  833. // 积分页支持自动完成每日活动
  834. if ('https://' + location.host === rewardHost) {
  835. if (location.search === autoRunSearch) {
  836. autoClickRewardActivity();
  837. }
  838. return;
  839. }
  840.  
  841. // 搜索页支持自动完成每日搜索
  842. if (pathnames.includes(location.pathname)) {
  843. insertStartBtn();
  844. insertDailyBtn();
  845.  
  846. // 判断是否正在运行搜索任务
  847. const searchParam = GM_getValue(searchParamKey);
  848. if (location.search === searchParam) {
  849. search();
  850. return;
  851. }
  852.  
  853. // 判断是否触发搜索任务
  854. if (location.search === autoRunSearch) {
  855. start();
  856. }
  857. return;
  858. }
  859. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址