您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
通过劫持Date.now()和计时器实现暂停/继续倒计时。同时拦截学习时间上报和题目提交请求,修改上报的数据(如startTime, time)。
// ==UserScript== // @name 粉笔网计时器暂停脚本 // @namespace http://tampermonkey.net/ // @version 2.4 // @description 通过劫持Date.now()和计时器实现暂停/继续倒计时。同时拦截学习时间上报和题目提交请求,修改上报的数据(如startTime, time)。 // @author BlingCc & Gemini // @match https://*.fenbi.com/* // @license MIT // @grant none // @run-at document-start // @icon https://www.fenbi.com/favicon.ico // ==/UserScript== (function() { 'use strict'; console.log('粉笔网计时器增强脚本 v2.3 已加载。'); // ------------------ 核心功能:劫持并修改网络请求 ------------------ const originalFetch = window.fetch; const originalXhrSend = XMLHttpRequest.prototype.send; const originalXhrOpen = XMLHttpRequest.prototype.open; /** * 根据请求的URL,修改请求体内容 * @param {string} bodyString - 原始请求体字符串 * @param {string} url - 请求的URL * @returns {string|null} - 返回修改后的请求体字符串,如果无需修改或失败则返回null */ function modifyRequestBody(bodyString, url) { try { const data = JSON.parse(bodyString); let modified = false; // 规则1: 学习时间上报 (studyTime) if (url.includes('/activity/report/studyTime')) { console.log('粉笔脚本:拦截到【学习时间上报】请求', JSON.parse(JSON.stringify(data))); if (Array.isArray(data)) { data.forEach(item => { if (item && typeof item.startTime === 'number') { const originalStartTime = item.startTime; const baseTime = originalStartTime - (originalStartTime % 1000000); const lastSixDigits = originalStartTime % 1000000; const newLastSixDigits = Math.round(lastSixDigits * 0.8); item.startTime = baseTime + newLastSixDigits; modified = true; console.log(`粉笔脚本:startTime已修改。原始值: ${originalStartTime}, 新值: ${item.startTime}`); } }); } } // 规则2: 题目提交 (async/exercises) else if (url.includes('/api/xingce/async/exercises')) { console.log('粉笔脚本:拦截到【题目提交】请求', JSON.parse(JSON.stringify(data))); if (Array.isArray(data)) { data.forEach(item => { // 检查item是否存在,并且有time属性 if (item && typeof item.time !== 'undefined') { const originalTime = item.time; // --- 这里是修改的部分 --- // 将 time 修改为 35 到 47 之间的一个随机整数 item.time = Math.floor(Math.random() * (47 - 35 + 1)) + 35; // --- 修改结束 --- modified = true; console.log(`粉笔脚本:time已修改。原始值: ${originalTime}, 新值: ${item.time}`); } }); } } // 如果数据被修改过,则返回新的字符串,否则返回原样 return modified ? JSON.stringify(data) : bodyString; } catch (e) { console.error('粉笔脚本:解析或修改请求体失败,将按原样发送。', e); return null; // 返回null表示失败,将使用原始body } } // 1. 劫持 window.fetch window.fetch = function(input, init) { const url = (typeof input === 'string') ? input : input.url; const method = (init && init.method) ? init.method.toUpperCase() : (input && input.method) ? input.method.toUpperCase() : 'GET'; if (method === 'POST' && (url.includes('/activity/report/studyTime') || url.includes('/api/xingce/async/exercises'))) { return new Promise(resolve => { if (!init.body) { resolve(originalFetch(input, init)); return; } // 读取请求体,它可能是一个Blob, a Stream, etc. (new Response(init.body)).text().then(bodyText => { const newBody = modifyRequestBody(bodyText, url); const finalBody = newBody !== null ? newBody : bodyText; console.log('粉笔脚本:发送修改后的请求体:', finalBody); resolve(originalFetch(input, newInit)); }); }); } return originalFetch(input, init); }; // 2. 劫持 XMLHttpRequest XMLHttpRequest.prototype.open = function(method, url) { this._hooked_method = method; this._hooked_url = url; return originalXhrOpen.apply(this, arguments); }; XMLHttpRequest.prototype.send = function(body) { const url = this._hooked_url; const method = this._hooked_method; if (method && method.toUpperCase() === 'POST' && url && (url.includes('/activity/report/studyTime') || url.includes('/api/xingce/async/exercises'))) { const newBody = modifyRequestBody(body, url); const finalBody = newBody !== null ? newBody : body; console.log('粉笔脚本 (XHR):发送修改后的请求体:', finalBody); return originalXhrSend.call(this, finalBody); } return originalXhrSend.apply(this, arguments); }; // ------------------ 核心逻辑:劫持时间和计时器函数 (原脚本功能) ------------------ // (这部分代码保持不变) let isPaused = false; let timeOffset = 0; let pauseStartTime = 0; const originalDateNow = Date.now; const OriginalDate = Date; const originalSetInterval = window.setInterval; const originalRequestAnimationFrame = window.requestAnimationFrame; Date.now = function() { if (isPaused) { return pauseStartTime; } return originalDateNow() - timeOffset; }; window.Date = class extends OriginalDate { constructor(...args) { if (args.length === 0) { super(Date.now()); } else { super(...args); } } }; Object.assign(window.Date, OriginalDate); window.Date.now = Date.now; window.setInterval = function(callback, delay, ...args) { const newCallback = () => { if (!isPaused) { callback(...args); } }; return originalSetInterval(newCallback, delay); }; window.requestAnimationFrame = function(callback) { const newCallback = (timestamp) => { if (!isPaused) { callback(timestamp); } else { originalRequestAnimationFrame(newCallback); } }; return originalRequestAnimationFrame(newCallback); }; // ------------------ UI部分:创建控制按钮 ------------------ // (这部分代码保持不变) function createToggleButton() { const button = document.createElement('button'); button.id = 'timer-pause-toggle-button'; button.textContent = '⏸️ 暂停计时'; Object.assign(button.style, { position: 'fixed', bottom: '20px', left: '20px', zIndex: '99999', padding: '10px 15px', backgroundColor: '#007bff', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer', fontSize: '14px', boxShadow: '0 2px 5px rgba(0,0,0,0.2)' }); button.addEventListener('click', () => { isPaused = !isPaused; if (isPaused) { pauseStartTime = originalDateNow(); button.textContent = '▶️ 继续计时'; button.style.backgroundColor = '#28a745'; console.log(`计时器已暂停于: ${new OriginalDate(pauseStartTime).toLocaleString()}`); } else { const pauseDuration = originalDateNow() - pauseStartTime; timeOffset += pauseDuration; button.textContent = '⏸️ 暂停计时'; button.style.backgroundColor = '#007bff'; console.log(`计时器已恢复,本次暂停 ${pauseDuration / 1000} 秒。总暂停时长: ${timeOffset / 1000} 秒。`); } }); document.body.appendChild(button); } if (document.readyState === 'loading') { window.addEventListener('DOMContentLoaded', createToggleButton); } else { createToggleButton(); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址