// ==UserScript== // @name BZM超星学习通挂机助手 // @version 4.3.8 // @author bzm // @description 后台任务,不占用宽带,学习通助手,支持视频弹题、视频,作业、考试自动答题,免费高分题库。后台任务版,不占用宽带。后台上报,实时进度条。反检测增强。修复多选题误全选。弹题随机兜底。面板可拖动。 // @match *://*.chaoxing.com/* // @run-at document-end // @grant unsafeWindow // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @grant GM_info // @grant GM_getResourceText // @grant GM_openInTab // @icon http://pan-yz.chaoxing.com/favicon.ico // @original-script https://scriptcat.org/zh-CN/script-show-page/6555 // @require https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js // @require https://cdn.bootcdn.net/ajax/libs/limonte-sweetalert2/11.1.0/sweetalert2.all.min.js // @require https://cdn.bootcdn.net/ajax/libs/blueimp-md5/2.19.0/js/md5.min.js // @require https://pkpkq.n1t.cn/font-decrypt/TyprMd5.js // @resource Table https://pkpkq.n1t.cn/font-decrypt/Table.json // @connect tk.swk.tw // @connect n1t.cn // @tag 免费搜题 // @tag 可选付费 // ==/UserScript== /********************************* 反检测层 ****************************************/ (function() { 'use strict'; try { Object.defineProperty(document, 'visibilityState', { get: function() { return 'visible'; }, configurable: true }); Object.defineProperty(document, 'hidden', { get: function() { return false; }, configurable: true }); Object.defineProperty(document, 'webkitVisibilityState', { get: function() { return 'visible'; }, configurable: true }); Object.defineProperty(document, 'webkitHidden', { get: function() { return false; }, configurable: true }); document.addEventListener('visibilitychange', function(e) { e.stopImmediatePropagation(); }, true); document.addEventListener('webkitvisibilitychange', function(e) { e.stopImmediatePropagation(); }, true); } catch(e) {} try { window.requestAnimationFrame = function(cb) { return setTimeout(function() { cb(Date.now()); }, 16); }; window.cancelAnimationFrame = function(id) { clearTimeout(id); }; } catch(e) {} try { Object.defineProperty(navigator, 'webdriver', { get: function() { return undefined; }, configurable: true }); } catch(e) {} try { if (!window.chrome) window.chrome = {}; if (!window.chrome.runtime) window.chrome.runtime = {}; } catch(e) {} try { document.hasFocus = function() { return true; }; window.onblur = null; window.onfocus = null; ['blur', 'focus', 'pagehide', 'pageshow'].forEach(function(evt) { window.addEventListener(evt, function(e) { e.stopImmediatePropagation(); }, true); }); } catch(e) {} try { var _origQuery = navigator.permissions && navigator.permissions.query; if (_origQuery) { navigator.permissions.query = function(desc) { if (desc && desc.name === 'notifications') { return Promise.resolve({ state: 'prompt', onchange: null }); } return _origQuery.call(navigator.permissions, desc); }; } } catch(e) {} try { var _OrigMO = window.MutationObserver; if (_OrigMO) { window.MutationObserver = function(callback) { var wrappedCallback = function(mutations, observer) { var delay = 50 + Math.random() * 150; setTimeout(function() { callback(mutations, observer); }, delay); }; return new _OrigMO(wrappedCallback); }; window.MutationObserver.prototype = _OrigMO.prototype; } } catch(e) {} try { var _origAppendChild = Node.prototype.appendChild; Node.prototype.appendChild = function(node) { var result = _origAppendChild.call(this, node); try { if (node && node.tagName === 'IFRAME') { setTimeout(function() { patchIframe(node); }, 500); node.addEventListener('load', function() { patchIframe(node); }); } } catch(e) {} return result; }; function keepAliveIframeWindow(win) { try { if (!win || !win.document) return; Object.defineProperty(win.document, 'visibilityState', { get: function() { return 'visible'; }, configurable: true }); Object.defineProperty(win.document, 'hidden', { get: function() { return false; }, configurable: true }); Object.defineProperty(win.document, 'webkitVisibilityState', { get: function() { return 'visible'; }, configurable: true }); Object.defineProperty(win.document, 'webkitHidden', { get: function() { return false; }, configurable: true }); win.document.hasFocus = function() { return true; }; win.document.addEventListener('visibilitychange', function(e) { e.stopImmediatePropagation(); }, true); win.document.addEventListener('webkitvisibilitychange', function(e) { e.stopImmediatePropagation(); }, true); ['blur', 'focus', 'pagehide', 'pageshow'].forEach(function(evt) { win.document.addEventListener(evt, function(e) { e.stopImmediatePropagation(); }, true); }); if (win.navigator) { try { Object.defineProperty(win.navigator, 'webdriver', { get: function() { return undefined; }, configurable: true }); } catch(e) {} } } catch(e) {} } function patchIframe(iframe) { try { var d = iframe.contentDocument || (iframe.contentWindow && iframe.contentWindow.document); if (!d) return; keepAliveIframeWindow(iframe.contentWindow); if (d.__antiDetectPatched) return; d.__antiDetectPatched = true; Object.defineProperty(d, 'visibilityState', { get: function() { return 'visible'; }, configurable: true }); Object.defineProperty(d, 'hidden', { get: function() { return false; }, configurable: true }); Object.defineProperty(d, 'webkitVisibilityState', { get: function() { return 'visible'; }, configurable: true }); Object.defineProperty(d, 'webkitHidden', { get: function() { return false; }, configurable: true }); d.hasFocus = function() { return true; }; d.addEventListener('visibilitychange', function(e) { e.stopImmediatePropagation(); }, true); d.addEventListener('webkitvisibilitychange', function(e) { e.stopImmediatePropagation(); }, true); ['blur', 'focus', 'pagehide', 'pageshow'].forEach(function(evt) { d.addEventListener(evt, function(e) { e.stopImmediatePropagation(); }, true); }); try { if (d.defaultView && d.defaultView.navigator) { Object.defineProperty(d.defaultView.navigator, 'webdriver', { get: function() { return undefined; }, configurable: true }); } } catch(e) {} } catch(e) {} } setTimeout(function() { document.querySelectorAll('iframe').forEach(patchIframe); }, 1000); } catch(e) {} console.log('[学习通助手] 反检测层已加载'); })(); /********************************* 配置管理器 ****************************************/ var SettingsManager = { _defaults: { showBox: 1, showMaskImg: 1, taskOnly: 0, handleVideo: 1, handleAudio: 1, videoRate: 1, reviewMode: 0, handleQuiz: 1, handleVideoQuiz: 1, answerInterval: 5000, autoSubmit: 0, forceSubmit: 0, decryptFont: 1, autoNextExam: 0, examNextDelay: 1, insertAnswer: 1, autoLogin: 0, loginPhone: '', loginPassword: '', videoMode: 'simulate', reportBaseInterval: 58, showProgressBar: 1 }, get: function(key) { var gv = GM_getValue('setting_' + key, null); if (gv !== null && gv !== undefined) { this._defaults[key] = gv; return gv; } var stored = localStorage.getItem('setting_' + key); if (stored !== null) { if (stored === 'true') return true; if (stored === 'false') return false; if (!isNaN(parseFloat(stored)) && isFinite(stored)) return parseFloat(stored); return stored; } return this._defaults[key]; }, set: function(key, value) { localStorage.setItem('setting_' + key, value); GM_setValue('setting_' + key, value); this._defaults[key] = value; return value; }, load: function() { for (var key in this._defaults) { var v = this.get(key); if (v !== undefined) this._defaults[key] = v; } if (this._defaults.videoRate > 8) this._defaults.videoRate = 8; return this._defaults; }, saveVideoMode: function(mode) { this.set('videoMode', mode); GM_setValue('videoMode', mode); }, saveVideoRate: function(rate) { if (rate > 8) rate = 8; this.set('videoRate', rate); GM_setValue('videoRate', rate); }, saveProgressBar: function(show) { this.set('showProgressBar', show ? 1 : 0); GM_setValue('showProgressBar', show ? 1 : 0); } }; var CONFIG = SettingsManager.load(); if (CONFIG.videoRate > 8) CONFIG.videoRate = 8; function updateConfig(key, value) { var oldValue = CONFIG[key]; CONFIG[key] = SettingsManager.set(key, value); if (key === 'videoRate' && CONFIG.videoRate > 8) { CONFIG.videoRate = 8; SettingsManager.set('videoRate', 8); } log('⚙️ 配置已更新: ' + key + ' = ' + value + (oldValue !== value ? ' (已生效)' : ''), 'blue'); } function getRandomInterval() { var base = CONFIG.answerInterval || 5000; return Math.floor(base * (0.8 + Math.random() * 0.7)); } /********************************* 题型映射扩展 ****************************************/ var QuestionTypeMap = { 0: ['单选题', '单项选择题', '单选', '选择题'], 1: ['多选题', '多项选择题', '多选'], 2: ['填空题', '填空'], 3: ['判断题', '是非题', '判断'], 4: ['简答题', '简答', '问答题', '名词解释', '论述题', '论述', '计算题', '计算', '分录题', '资料题', '作图题', '其他', '其它', '阅读理解', '阅读', '阅读题', '理解题', '完形填空', '完形', '综合题'] }; function getQuestionTypeFromText(text) { if (!text) return undefined; for (var typeId = 0; typeId <= 4; typeId++) { var keywords = QuestionTypeMap[typeId]; for (var i = 0; i < keywords.length; i++) { if (text.indexOf(keywords[i]) !== -1) return typeId; } } return undefined; } /********************************* 多选题智能匹配核心 ****************************************/ function matchMultiChoiceIndices(optsText, answer) { var ansArr = splitAnswer(answer); var normalizedOpts = optsText.map(function(opt) { return normalizeForCompare(opt); }); var normalizedAnsArr = ansArr.map(function(a) { return normalizeForCompare(a); }).filter(function(a) { return a !== ''; }); var hitIdx = []; // 第一轮:严格相等 normalizedOpts.forEach(function(opt, i) { for (var j = 0; j < normalizedAnsArr.length; j++) { if (opt === normalizedAnsArr[j] && hitIdx.indexOf(i) === -1) { hitIdx.push(i); break; } } }); // 第二轮:只有严格命中数 < 答案数时才降级为包含匹配 if (hitIdx.length < normalizedAnsArr.length) { normalizedOpts.forEach(function(opt, i) { if (hitIdx.indexOf(i) !== -1) return; for (var j = 0; j < normalizedAnsArr.length; j++) { var na = normalizedAnsArr[j]; if (na === '') continue; if (opt.indexOf(na) !== -1 && (opt.length - na.length) <= 1) { hitIdx.push(i); break; } if (na.indexOf(opt) !== -1 && (na.length - opt.length) <= 1) { hitIdx.push(i); break; } } }); } if (hitIdx.length > normalizedAnsArr.length) hitIdx = hitIdx.slice(0, normalizedAnsArr.length); return hitIdx; } /********************************* 全局变量 ****************************************/ var UNSAFE_WIN = unsafeWindow; var LOCATION = location; var DOCUMENT = UNSAFE_WIN.document; var $ = UNSAFE_WIN.jQuery || top.jQuery; var UE = UNSAFE_WIN.UE; var MD5 = window.md5 || $.md5; var TyprInstance = null; try { if (typeof unsafeWindow !== 'undefined' && unsafeWindow.Typr) TyprInstance = unsafeWindow.Typr; else if (typeof window !== 'undefined' && window.Typr) TyprInstance = window.Typr; else if (typeof Typr !== 'undefined') TyprInstance = Typr; else if (typeof unsafeWindow !== 'undefined' && unsafeWindow.TyprMd5) TyprInstance = unsafeWindow.TyprMd5; else if (typeof TyprMd5 !== 'undefined') TyprInstance = TyprMd5; } catch(e) {} function cleanOptionText(text) { if (!text) return text; var cleaned = text.replace(/<[^>]*>/g, ''); cleaned = cleaned.replace(/^[A-Z][\s]*[.、.))\s]+/, ''); cleaned = cleaned.replace(/^\([A-Z]\)[\s]*/, ''); cleaned = cleaned.replace(/^([A-Z])[\s]*/, ''); cleaned = cleaned.replace(/^[:\s\-_]+/, ''); return cleaned.trim(); } function getCookie(name) { return ('; ' + document.cookie).split('; ' + name + '=').pop().split(';')[0]; } function formatDuration(seconds) { if (!seconds || seconds < 0) return '00:00'; var total = Math.max(0, Math.floor(Number(seconds) || 0)); var hours = Math.floor(total / 3600); var minutes = Math.floor((total % 3600) / 60); var secs = total % 60; var pad = function(n) { return String(n).padStart(2, '0'); }; if (hours > 0) return pad(hours) + ':' + pad(minutes) + ':' + pad(secs); return pad(minutes) + ':' + pad(secs); } function tidyString(s) { if (!s) return null; var cleaned = s.replace(/<(?!img).*?>/g, "").replace(/^【.*?】\s*/, '').replace(/\s*(\d+\.\d+分)$/, '').trim().replace(/ /g, ''); return cleanOptionText(cleaned); } function tidyQuestion(s) { if (!s) return null; var cleaned = s.replace(/<(?!img).*?>/g, "").replace(/^【.*?】\s*/, '').replace(/\s*(\d+\.\d+分)$/, '').replace(/^\d+[.、]?/, '').trim(); return cleanOptionText(cleaned); } function normalizeForCompare(text) { if (!text) return ''; return text.toUpperCase().replace(/[^\u4e00-\u9fa5A-Z0-9]/g, '').trim(); } function splitAnswer(answer) { if (!answer) return []; var parts = answer.split(/[#]+/).map(function(a) { return a.trim(); }).filter(function(a) { return a !== ''; }); if (parts.length === 0 && answer.trim() !== '') return [answer.trim()]; return parts; } function extractString(str, start, end) { var res = str.match(new RegExp(start + '(.*?)' + end)); return res ? res[1] : null; } function isTaskCompleted(task) { if (!task) return false; if (task.isPassed === true) return true; if (task.status === 'completed' || task.status === 'finished') return true; if (task.finished === true) return true; return false; } var logQueue = []; var logTimer = null; function getTopLogContainer() { try { var topDoc = top.document; var container = topDoc.getElementById('ne-21log'); if (container) return $(container); container = document.getElementById('ne-21log'); if (container) return $(container); return null; } catch(e) { return null; } } function flushLogs() { if (logQueue.length === 0) return; var container = getTopLogContainer(); if (container && container.length) { var logsToShow = logQueue.slice(); logQueue = []; for (var i = logsToShow.length - 1; i >= 0; i--) { container.prepend(logsToShow[i]); } container.scrollTop(0); } if (logTimer) { clearTimeout(logTimer); logTimer = null; } } function scheduleFlushLogs() { if (logTimer) return; logTimer = setTimeout(flushLogs, 100); } function log(str, color) { var time = new Date().toLocaleTimeString(); var logHtml = '
[' + time + '] ' + str + '
'; logQueue.push(logHtml); scheduleFlushLogs(); console.log('[学习通助手][' + time + ']', str); } var API_SERVERS = [ { url: "https://tk.n1t.cn/api/search.php", name: "接口1" }, { url: "https://tk.swk.tw/api/search.php", name: "接口2" } ]; var currentServerIndex = 0; function getApiKey() { return GM_getValue('api_key', ''); } function hasApiKey() { var key = getApiKey(); return key && key.trim() !== ''; } function getQuotaInfo() { return { remaining: GM_getValue('quota_remaining', 0), freeRemaining: GM_getValue('quota_free_remaining', 0), rechargeBalance: GM_getValue('quota_recharge_balance', 0), todayFreeUsage: GM_getValue('quota_today_free_usage', 0) }; } var quotaInfo = getQuotaInfo(); function saveQuotaInfo() { GM_setValue('quota_remaining', quotaInfo.remaining); GM_setValue('quota_free_remaining', quotaInfo.freeRemaining); GM_setValue('quota_recharge_balance', quotaInfo.rechargeBalance); GM_setValue('quota_today_free_usage', quotaInfo.todayFreeUsage); } function updateQuotaInfo(newQuotaInfo) { if (newQuotaInfo) { quotaInfo.remaining = newQuotaInfo.remaining !== undefined ? newQuotaInfo.remaining : quotaInfo.remaining; quotaInfo.freeRemaining = newQuotaInfo.free_remaining !== undefined ? newQuotaInfo.free_remaining : quotaInfo.freeRemaining; quotaInfo.rechargeBalance = newQuotaInfo.recharge_balance !== undefined ? newQuotaInfo.recharge_balance : quotaInfo.rechargeBalance; quotaInfo.todayFreeUsage = newQuotaInfo.today_free_usage !== undefined ? newQuotaInfo.today_free_usage : quotaInfo.todayFreeUsage; saveQuotaInfo(); updateQuotaDisplay(); } } function updateQuotaDisplay() { try { var targetDoc = top.document; if (!targetDoc.querySelector('#quotaInfo')) targetDoc = document; var $quotaInfo = $('#quotaInfo', targetDoc); if ($quotaInfo.length) { var remaining = quotaInfo.remaining; var hasKey = hasApiKey(); if (remaining > 0) $quotaInfo.html('📊 剩余次数: ' + remaining + ' | 免费: ' + quotaInfo.freeRemaining); else if (remaining === 0 && hasKey) $quotaInfo.html('⚠️ 次数已用完 | 点击充值'); else if (!hasKey) $quotaInfo.html('⚠️ 请先设置API密钥'); else $quotaInfo.html('📊 剩余次数: 加载中...'); } } catch(e) {} } function checkApiKeyBeforeAction(actionName) { var apiKey = getApiKey(); if (!apiKey || apiKey.trim() === '') { log('❌ 无法执行' + actionName + ':请先在设置中配置API密钥', 'red'); return false; } quotaInfo = getQuotaInfo(); if (quotaInfo.remaining <= 0 && quotaInfo.freeRemaining <= 0) { log('❌ 无法执行' + actionName + ':API次数已用完,请充值', 'red'); return false; } return true; } function hasEnoughQuotaForSubmit() { quotaInfo = getQuotaInfo(); return hasApiKey() && (quotaInfo.remaining > 0 || quotaInfo.freeRemaining > 0); } function getSubmitDecision() { var hasQuota = hasEnoughQuotaForSubmit(); if (CONFIG.autoSubmit && hasQuota) return { action: 'submit', reason: '自动提交' }; else if (CONFIG.forceSubmit && hasQuota) return { action: 'submit', reason: '强制提交' }; else if (CONFIG.autoSubmit && !hasQuota) return { action: 'save', reason: '次数不足,改为保存' }; else if (CONFIG.forceSubmit && !hasQuota) return { action: 'save', reason: '次数不足,改为保存' }; else return { action: 'save', reason: '自动保存' }; } function testApi(key, serverIndex) { return new Promise(function(resolve, reject) { var apiUrl = API_SERVERS[serverIndex || 0].url; var formData = 'question=' + encodeURIComponent("test") + '&key=' + encodeURIComponent(key) + '&type=' + encodeURIComponent("单选题") + '&options=' + encodeURIComponent(JSON.stringify(["选项A", "选项B"])); var timeoutId = setTimeout(function() { reject('请求超时'); }, 10000); GM_xmlhttpRequest({ method: 'POST', url: apiUrl, headers: {'Content-Type': 'application/x-www-form-urlencoded'}, data: formData, timeout: 10000, onload: function(xhr) { clearTimeout(timeoutId); try { var res = JSON.parse(xhr.responseText); if (res.code == 1 || res.code == 400) { if (res.quota_info) updateQuotaInfo(res.quota_info); resolve(res); } else reject(res.msg || '密钥无效'); } catch(e) { reject('解析响应失败'); } }, onerror: function() { clearTimeout(timeoutId); reject('网络请求失败'); }, ontimeout: function() { clearTimeout(timeoutId); reject('请求超时'); } }); }); } function getAnswer(questionType, questionText, optionsData, retryCount) { retryCount = retryCount || 0; var startServerIndex = retryCount; return new Promise(function(resolve, reject) { var apiKey = getApiKey(); if (!apiKey || apiKey.trim() === '') { log('❌ 请先设置API密钥才能使用自动答题功能', 'red'); reject('NO_API_KEY'); return; } quotaInfo = getQuotaInfo(); if (quotaInfo.remaining <= 0 && quotaInfo.freeRemaining <= 0) { log('❌ API次数已用完,无法获取答案', 'red'); reject('QUOTA_EXHAUSTED'); return; } var typeMap = {0:'单选题',1:'多选题',2:'填空题',3:'判断题',4:'简答题'}; var typeText = typeMap[questionType] || '单选题'; function tryServer(index) { if (index >= API_SERVERS.length) { log('❌ 所有接口均无法获取答案', 'red'); reject('所有接口均失败'); return; } var apiUrl = API_SERVERS[index].url; var formData = 'question=' + encodeURIComponent(questionText) + '&key=' + encodeURIComponent(apiKey) + '&type=' + encodeURIComponent(typeText); if (optionsData && optionsData.length) formData += '&options=' + encodeURIComponent(JSON.stringify(optionsData)); var timeoutId = setTimeout(function() { tryServer(index + 1); }, 15000); GM_xmlhttpRequest({ method: 'POST', url: apiUrl, headers: {'Content-Type': 'application/x-www-form-urlencoded'}, data: formData, timeout: 15000, onload: function(xhr) { clearTimeout(timeoutId); try { var res = JSON.parse(xhr.responseText); if (res.code == 1 && res.data) { if (res.quota_info) updateQuotaInfo(res.quota_info); currentServerIndex = index; GM_setValue('current_server_index', index); log('✅ 答案: ' + res.data.substring(0, 100) + '...', 'purple'); resolve(res.data); } else if (res.code == 400) { log('❌ ' + (res.msg || 'API密钥无效'), 'red'); reject('API_KEY_INVALID'); } else tryServer(index + 1); } catch(e) { tryServer(index + 1); } }, onerror: function() { clearTimeout(timeoutId); tryServer(index + 1); }, ontimeout: function() { clearTimeout(timeoutId); tryServer(index + 1); } }); } tryServer(startServerIndex); }); } function generateEncLocal(classId, uid, jobId, objectId, playTime, duration) { var clipTime = '0_' + duration; var str = '[' + classId + '][' + uid + '][' + jobId + '][' + objectId + ']' + '[' + (playTime * 1000) + ']' + '[d_yHJ!$pdA~5]' + '[' + (duration * 1000) + ']' + '[' + clipTime + ']'; return MD5(str); } function waitForElement(selector, timeout) { timeout = timeout || 30000; return new Promise(function(resolve, reject) { var start = Date.now(); var timer = setInterval(function() { if ($(selector).length) { clearInterval(timer); resolve(); } else if (Date.now() - start > timeout) { clearInterval(timer); reject(); } }, 500); }); } function getElement(parent, selector) { return new Promise(function(resolve) { var iframe = parent.querySelector(selector); if (iframe) resolve(iframe); var observer = new MutationObserver(function(mutations) { var iframe = parent.querySelector(selector); if (iframe) { observer.disconnect(); resolve(iframe); } }); observer.observe(parent, { childList: true, subtree: true }); setTimeout(function() { observer.disconnect(); resolve(null); }, 10000); }); } function getBlankCount($question) { var count = 0; var $inputs = $question.find('.blankList2 input, input[type="text"][name*="answer"]'); if ($inputs.length) { var uniqueIdentifiers = new Set(); $inputs.each(function() { var key = ($(this).attr('name') || '') + '_' + ($(this).attr('id') || '') + '_' + ($(this).attr('placeholder') || '') + '_' + $(this).index(); uniqueIdentifiers.add(key); }); count = uniqueIdentifiers.size; if (count > 0) return count; } var $editorBlocks = $question.find('[data-editorindex]'); if ($editorBlocks.length) { var uniqueIndices = new Set(); $editorBlocks.each(function() { var idx = $(this).attr('data-editorindex'); if (idx !== undefined && idx !== null && idx !== '') uniqueIndices.add(idx); }); count = uniqueIndices.size; if (count > 0) return count; } var $pcInputs = $question.find('.Zy_ulTk .XztiHover1 textarea, .stem_answer .Answer .divText .textDIV textarea, .subEditor textarea'); if ($pcInputs.length) { var uniquePCInputs = new Set(); $pcInputs.each(function() { uniquePCInputs.add(($(this).attr('id') || '') + '_' + ($(this).attr('name') || '') + '_' + $(this).index()); }); count = uniquePCInputs.size; if (count > 0) return count; } var $blankInputs = $question.find('input[type="text"], textarea').filter(function() { var className = $(this).attr('class') || ''; var id = $(this).attr('id') || ''; return className.toLowerCase().indexOf('blank') !== -1 || className.toLowerCase().indexOf('fill') !== -1 || id.toLowerCase().indexOf('blank') !== -1 || id.toLowerCase().indexOf('fill') !== -1; }); if ($blankInputs.length) { var uniqueBlanks = new Set(); $blankInputs.each(function() { uniqueBlanks.add(($(this).attr('name') || '') + '_' + ($(this).attr('id') || '') + '_' + $(this).index()); }); count = uniqueBlanks.size; if (count > 0) return count; } var questionText = $question.text() || ''; var pattern1 = questionText.match(/第(\d+)空/g); if (pattern1 && pattern1.length > 0) { var maxBlank = 0; pattern1.forEach(function(match) { var num = parseInt(match.match(/\d+/)[0]); if (num > maxBlank) maxBlank = num; }); if (maxBlank > 0) return maxBlank; } var pattern2 = questionText.match(/[((](\d+)[))]/g); if (pattern2 && pattern2.length > 0) { var maxBlank = 0; pattern2.forEach(function(match) { var num = parseInt(match.match(/\d+/)[0]); if (num > maxBlank) maxBlank = num; }); if (maxBlank > 0 && maxBlank <= 20) return maxBlank; } return 1; } function fillBlankAnswer($question, answers, isPhoneMode, contextWindow) { var answerList = splitAnswer(answers); log('📝 填空题答案: ' + answerList.join(' | '), 'blue'); var blankCount = getBlankCount($question); while (answerList.length < blankCount) answerList.push(answerList[answerList.length - 1] || answerList[0] || ''); if (isPhoneMode) { var $allInputs = $question.find('.blankList2 input, input[type="text"][name*="answer"]'); if ($allInputs.length) { var uniqueInputs = []; var seen = new Set(); $allInputs.each(function() { var key = ($(this).attr('name') || '') + '_' + ($(this).attr('id') || '') + '_' + ($(this).attr('placeholder') || '') + '_' + $(this).index(); if (!seen.has(key)) { seen.add(key); uniqueInputs.push(this); } }); $(uniqueInputs).each(function(i) { setTimeout(function() { var answerValue = answerList[i] !== undefined ? answerList[i] : (answerList[0] || ''); $(this).val(answerValue); $(this).trigger('input').trigger('change'); setTimeout(function() { $(this).trigger('blur'); }.bind(this), 50); log('✅ 填空题第' + (i+1) + '空已填写: ' + answerValue, 'green'); }.bind(this), i * 200); }); return true; } var $editorBlocks = $question.find('[data-editorindex]'); if ($editorBlocks.length) { var uniqueEditors = []; var seenIndices = new Set(); $editorBlocks.each(function() { var idx = $(this).attr('data-editorindex'); if (idx && !seenIndices.has(idx)) { seenIndices.add(idx); uniqueEditors.push(this); } }); uniqueEditors.forEach(function(editor, i) { var editorIndex = $(editor).attr('data-editorindex'); var itemId = $(editor).attr('data-itemid'); setTimeout(function() { try { var ueditor = null; if (contextWindow && contextWindow.editors && contextWindow.editors[editorIndex]) ueditor = contextWindow.editors[editorIndex].ueditor; if (!ueditor && contextWindow && contextWindow.UE && contextWindow.UE.instants) { ueditor = contextWindow.UE.instants['ueditorInstant' + editorIndex]; } if (!ueditor && itemId && contextWindow && contextWindow.UE && contextWindow.UE.getEditor) ueditor = contextWindow.UE.getEditor('ananas-editor-answer' + itemId); if (ueditor) { var answerValue = answerList[i] !== undefined ? answerList[i] : (answerList[0] || ''); ueditor.setContent(answerValue); log('✅ 填空题第' + (i+1) + '空已填写: ' + answerValue, 'green'); } if (itemId) { var answerValue = answerList[i] !== undefined ? answerList[i] : (answerList[0] || ''); $('#answer' + itemId).val(answerValue).trigger('change'); } } catch(e) { log('⚠️ 填写编辑器第' + (i+1) + '空失败: ' + e.message, 'orange'); } }, i * 300); }); return true; } return false; } var $pcInputs = $question.find('.Zy_ulTk .XztiHover1 textarea, .stem_answer .Answer .divText .textDIV textarea, .subEditor textarea'); if ($pcInputs.length) { var uniquePCInputs = []; var seenPC = new Set(); $pcInputs.each(function() { var key = ($(this).attr('id') || '') + '_' + ($(this).attr('name') || '') + '_' + $(this).index(); if (!seenPC.has(key)) { seenPC.add(key); uniquePCInputs.push(this); } }); $(uniquePCInputs).each(function(i) { setTimeout(function() { var $this = $(this); var answerValue = answerList[i] !== undefined ? answerList[i] : (answerList[0] || ''); if (UE && UE.getEditor && UE.getEditor($this.attr('id'))) { try { UE.getEditor($this.attr('id')).setContent(answerValue); } catch(e) { $this.val(answerValue); } } else { $this.val(answerValue); $this.trigger('input').trigger('change'); } log('✅ 填空题第' + (i+1) + '空已填写: ' + answerValue, 'green'); }.bind(this), i * 200); }); return true; } return false; } function fillShortAnswer($question, answer, isPhoneMode, contextWindow) { log('📝 简答题答案: ' + answer.substring(0, 100) + '...', 'blue'); if (isPhoneMode) { var $editorBlocks = $question.find('[data-editorindex]'); if ($editorBlocks.length) { $editorBlocks.each(function(i) { var editorIndex = $(this).attr('data-editorindex'); var itemId = $(this).attr('data-itemid'); setTimeout(function() { try { var ueditor = null; if (contextWindow && contextWindow.editors && contextWindow.editors[editorIndex]) ueditor = contextWindow.editors[editorIndex].ueditor; if (!ueditor && contextWindow && contextWindow.UE && contextWindow.UE.instants) { ueditor = contextWindow.UE.instants['ueditorInstant' + editorIndex]; } if (!ueditor && itemId && contextWindow && contextWindow.UE && contextWindow.UE.getEditor) ueditor = contextWindow.UE.getEditor('ananas-editor-answer' + itemId); if (ueditor) { ueditor.setContent(answer); log('✅ 简答题答案已填写', 'green'); } if (itemId) { $('#answer' + itemId).val(answer).trigger('change'); } } catch(e) {} }, 300); }); return true; } var $textarea = $question.find('textarea[name^="answer"], .answerTxt textarea'); if ($textarea.length) { $textarea.val(answer); $textarea.trigger('input').trigger('change'); return true; } return false; } var $ueTextarea = $question.find('textarea[name^="answerEditor"], .eidtDiv textarea, .divText textarea'); if ($ueTextarea.length) { var id = $ueTextarea.first().attr('id'); if (id && UE && UE.getEditor && UE.getEditor(id)) { try { UE.getEditor(id).setContent(answer); return true; } catch(e) {} } $ueTextarea.val(answer); $ueTextarea.trigger('input').trigger('change'); return true; } return false; } // ========== 视频弹题处理(仅正常播放模式使用) ========== var currentVideoQuizObserver = null; var quizObserverGuardTimer = null; var quizPollTimer = null; function isVideoQuizPopup(node) { if (!node || node.nodeType !== 1) return false; var hasTitle = node.querySelector && ( node.querySelector('.TiMu') || node.querySelector('.ans-videoquiz') || node.querySelector('.ans-videoquiz-title') || node.querySelector('.videoquiz') || node.querySelector('.ans-videoquiz-opt') ); var hasInput = node.querySelector && ( node.querySelector('input[type=radio]') || node.querySelector('input[type=checkbox]') || node.querySelector('.ans-videoquiz-opt label') ); if (hasTitle && hasInput) return true; var txt = node.textContent || ''; if ((txt.indexOf('题') !== -1 || txt.indexOf('选择') !== -1) && node.querySelectorAll && node.querySelectorAll('input[type=radio],input[type=checkbox]').length > 0) { return true; } return false; } function extractVideoQuiz(popupEl) { var titleEl = popupEl.querySelector('.TiMu') || popupEl.querySelector('.ans-videoquiz-title') || popupEl.querySelector('.videoquiz-title') || popupEl.querySelector('.ans-videoquiz') || popupEl; var title = (titleEl.textContent || '').trim(); var options = []; var inputs = popupEl.querySelectorAll('input[type=radio],input[type=checkbox]'); if (inputs.length) { inputs.forEach(function(input, idx) { var label = input.closest('label') || input.parentElement; options.push({ index: idx, text: (label.textContent || '').trim(), type: input.type, el: input }); }); } else { var liList = popupEl.querySelectorAll('.ans-videoquiz-opt li, .ans-videoquiz-opt label'); liList.forEach(function(el, idx) { options.push({ index: idx, text: (el.textContent || '').trim(), type: 'click', el: el }); }); } var qType = 'single'; if (popupEl.querySelector('input[type=checkbox]')) qType = 'multiple'; else if (popupEl.querySelector('.ans-videoquiz-opt .dui, .panduan')) qType = 'judge'; return { title: title, options: options, type: qType, popup: popupEl }; } function getSecFont() { try { var styleEl = document.querySelector("style[type='text/css']"); if (!styleEl) return null; var m = styleEl.textContent.match(/'(data:application\/font-ttf;.*?)'/); return m ? m[1] : null; } catch(e) { return null; } } function decryptVideoQuizFont(popupEl) { return new Promise(function(resolve) { if (!CONFIG.decryptFont) { resolve(); return; } var nodes = popupEl.querySelectorAll('.font-cxsecret'); if (!nodes.length) { resolve(); return; } var secFont = getSecFont(); if (!secFont) { resolve(); return; } var pending = nodes.length; nodes.forEach(function(node) { GM_xmlhttpRequest({ method: 'POST', url: 'https://pkpkq.n1t.cn/font-decrypt/decrypt.php', headers: { 'Content-Type': 'application/json' }, data: JSON.stringify({ secFont: secFont, dstText: (node.textContent || '').trim() }), onload: function(res) { try { var data = JSON.parse(res.responseText); if (data && data.srcText) node.textContent = data.srcText; } catch(e) {} if (--pending === 0) resolve(); }, onerror: function() { if (--pending === 0) resolve(); }, ontimeout: function() { if (--pending === 0) resolve(); } }); }); }); } function clickOpt(opt) { try { if (opt.el) { if (opt.el.click) opt.el.click(); var label = opt.el.closest && opt.el.closest('label'); if (label && label.click) label.click(); } } catch(e) {} } function fillVideoQuizAnswer(quiz, answer) { var qType = quiz.type; var opts = quiz.options; if (!opts.length) return false; if (qType === 'judge') { var isTrue = /对|正确|是|√|T/i.test(answer); var targetIdx = isTrue ? 0 : 1; if (opts[targetIdx]) { clickOpt(opts[targetIdx]); return true; } } if (qType === 'multiple') { var optsText = opts.map(function(o) { return o.text; }); var hitIdx = matchMultiChoiceIndices(optsText, answer); if (hitIdx.length > 0) { hitIdx.forEach(function(i) { clickOpt(opts[i]); }); return true; } return false; } var ansArr = splitAnswer(answer); var clicked = false; opts.forEach(function(opt) { var normOpt = normalizeForCompare(opt.text); for (var i = 0; i < ansArr.length; i++) { if (normOpt === normalizeForCompare(ansArr[i])) { clickOpt(opt); clicked = true; break; } } }); if (clicked) return true; opts.forEach(function(opt) { var normOpt = normalizeForCompare(opt.text); for (var i = 0; i < ansArr.length; i++) { var normAns = normalizeForCompare(ansArr[i]); if (normOpt.indexOf(normAns) !== -1 && normOpt.length - normAns.length <= 1) { clickOpt(opt); clicked = true; break; } if (normAns.indexOf(normOpt) !== -1 && normAns.length - normOpt.length <= 1) { clickOpt(opt); clicked = true; break; } } }); return clicked; } function randomPickVideoQuiz(quiz) { var opts = quiz.options || []; if (!opts.length) return false; if (quiz.type === 'multiple') { var pickCount = 1 + Math.floor(Math.random() * opts.length); var pool = opts.slice(); for (var k = 0; k < pickCount && pool.length > 0; k++) { var pi = Math.floor(Math.random() * pool.length); clickOpt(pool[pi]); pool.splice(pi, 1); } log('🎲 多选题随机选择了 ' + pickCount + ' 项', 'orange'); return true; } var ri = Math.floor(Math.random() * opts.length); clickOpt(opts[ri]); log('🎲 随机选择第 ' + (ri + 1) + ' 项: ' + opts[ri].text, 'orange'); return true; } function submitVideoQuiz(popupEl) { var submitBtn2 = popupEl.querySelector('.ans-videoquiz-submit, .submit-btn, .confirm, button[type=submit]'); if (submitBtn2) { setTimeout(function() { submitBtn2.click(); log('📤 弹题已提交', 'green'); }, 500 + Math.random() * 500); } } function resumeVideoWhenQuizGone(popupEl, video) { var done = false; function resume() { if (done) return; done = true; if (video && video.paused) { video.play().then(function() { log('▶️ 视频已恢复播放', 'blue'); }).catch(function(){}); } } try { var mo = new MutationObserver(function() { if (!popupEl.isConnected) { mo.disconnect(); resume(); } }); mo.observe(popupEl.parentNode || document.body, { childList: true, subtree: true }); } catch(e) {} setTimeout(resume, 8000); var t = setInterval(function() { if (!popupEl.isConnected || popupEl.style.display === 'none') { clearInterval(t); resume(); } }, 500); setTimeout(function() { clearInterval(t); resume(); }, 12000); } function handleVideoQuiz(popupEl) { if (!CONFIG.handleVideoQuiz) return; if (CONFIG.videoMode !== 'normal') return; if (!popupEl || popupEl.dataset.videoQuizHandled === 'true') return; popupEl.dataset.videoQuizHandled = 'true'; log('🎯 检测到视频弹题,开始处理...', 'purple'); var doc = popupEl.ownerDocument || document; var video = doc.querySelector('video'); if (video && !video.paused) video.pause(); decryptVideoQuizFont(popupEl).then(function() { var quiz = extractVideoQuiz(popupEl); log('📖 弹题题干: ' + quiz.title.substring(0, 60) + ' | 题型: ' + quiz.type, 'blue'); var typeId = 0; if (quiz.type === 'multiple') typeId = 1; else if (quiz.type === 'judge') typeId = 3; var optsText = quiz.options.map(function(o) { return o.text; }); var optionsData = (typeId === 3) ? ["对", "错"] : optsText; getAnswer(typeId, quiz.title, optionsData).then(function(answer) { if (!answer || answer === '暂无答案') { log('⚠️ 弹题未获取到答案,随机选择一项提交', 'orange'); randomPickVideoQuiz(quiz); } else { var filled = fillVideoQuizAnswer(quiz, answer); if (!filled) { log('⚠️ 弹题答案未匹配到选项,随机选择提交', 'orange'); randomPickVideoQuiz(quiz); } else { log('✅ 弹题已作答: ' + answer, 'green'); } } submitVideoQuiz(popupEl); resumeVideoWhenQuizGone(popupEl, video); }).catch(function(err) { log('⚠️ 弹题获取答案失败: ' + err + ',随机作答', 'orange'); randomPickVideoQuiz(quiz); submitVideoQuiz(popupEl); resumeVideoWhenQuizGone(popupEl, video); }); }); } function observeVideoQuiz(doc) { if (!CONFIG.handleVideoQuiz) return; if (CONFIG.videoMode !== 'normal') return; if (currentVideoQuizObserver) { try { currentVideoQuizObserver.disconnect(); } catch(e) {} currentVideoQuizObserver = null; } if (!doc || !doc.body) return; var existings = doc.querySelectorAll('.ans-videoquiz, .TiMu, .videoquiz'); existings.forEach(function(el) { if (isVideoQuizPopup(el)) handleVideoQuiz(el); }); currentVideoQuizObserver = new MutationObserver(function(mutations) { if (!CONFIG.handleVideoQuiz) return; if (CONFIG.videoMode !== 'normal') return; mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType !== 1) return; if (isVideoQuizPopup(node)) { handleVideoQuiz(node); } else if (node.querySelectorAll) { var inner = node.querySelectorAll('.ans-videoquiz, .TiMu, .videoquiz'); inner.forEach(function(el) { if (isVideoQuizPopup(el)) handleVideoQuiz(el); }); } }); }); }); currentVideoQuizObserver.observe(doc.body, { childList: true, subtree: true }); doc.__quizObserverAlive = true; var oldDisconnect = currentVideoQuizObserver.disconnect.bind(currentVideoQuizObserver); currentVideoQuizObserver.disconnect = function() { try { doc.__quizObserverAlive = false; } catch(e) {} oldDisconnect(); }; log('👀 视频弹题监听已启动', 'blue'); } function startQuizObserverGuard() { if (quizObserverGuardTimer) return; quizObserverGuardTimer = setInterval(function() { if (!CONFIG.handleVideoQuiz) return; if (CONFIG.videoMode !== 'normal') return; try { document.querySelectorAll('iframe').forEach(function(f) { var d; try { d = f.contentDocument || (f.contentWindow && f.contentWindow.document); } catch(e) { return; } if (!d || !d.body) return; if (!d.querySelector('video')) return; if (d.__quizObserverAlive) return; observeVideoQuiz(d); log('🔄 弹题监听断线重连', 'blue'); }); } catch(e) {} }, 3000); } function stopQuizObserverGuard() { if (quizObserverGuardTimer) { clearInterval(quizObserverGuardTimer); quizObserverGuardTimer = null; } } function startQuizPoll(doc) { if (quizPollTimer) clearInterval(quizPollTimer); quizPollTimer = setInterval(function() { if (!CONFIG.handleVideoQuiz) return; if (CONFIG.videoMode !== 'normal') return; try { var pops = doc.querySelectorAll('.ans-videoquiz, .TiMu, .videoquiz'); pops.forEach(function(el) { if (isVideoQuizPopup(el) && el.dataset.videoQuizHandled !== 'true') { handleVideoQuiz(el); } }); } catch(e) {} }, 1500); } function stopQuizPoll() { if (quizPollTimer) { clearInterval(quizPollTimer); quizPollTimer = null; } } // ========== 纯后台视频上报 Worker ========== var reportWorker = null; var activeVideoJob = null; function getActualReportInterval() { var baseInterval = CONFIG.reportBaseInterval || 58; var safeRate = Math.min(CONFIG.videoRate, 1.1); var actualInterval = Math.floor(baseInterval / safeRate); return Math.max(10, actualInterval); } function createReportWorker(videoInfo, taskObj) { var name = videoInfo.name; var duration = videoInfo.duration; var dtoken = videoInfo.dtoken; var objectId = videoInfo.objectId; var jobId = taskObj.jobid; var rt = videoInfo.rt; var otherInfo = videoInfo.otherInfo; var reportUrl = defaults.reportUrl; var classId = defaults.clazzId; var uid = getCookie('_uid') || getCookie('UID'); var startTime = videoInfo.playedTime || 0; var rate = Math.min(CONFIG.videoRate, 1.1); var reportInterval = getActualReportInterval(); log('📡 上报配置: 基础间隔=' + CONFIG.reportBaseInterval + 's, 安全倍速=' + rate + 'x, 实际上报间隔=' + reportInterval + 's', 'blue'); var workerCode = ` var timer = null; var currentTime = ${startTime}; var duration = ${duration}; var rate = ${rate}; var reportInterval = ${reportInterval}; var lastReportTime = ${startTime}; var isRunning = true; var startTime = Date.now(); function updateLoop() { if (!isRunning) return; var now = Date.now(); var elapsed = (now - startTime) / 1000; var newTime = Math.min(elapsed * rate, duration); if (newTime > currentTime) { currentTime = newTime; var timeSinceLastReport = currentTime - lastReportTime; var shouldReport = timeSinceLastReport >= reportInterval || currentTime >= duration; if (shouldReport) { self.postMessage({ type: 'doReport', playTime: Math.ceil(currentTime), isComplete: currentTime >= duration }); lastReportTime = currentTime; } self.postMessage({ type: 'progress', currentTime: currentTime, duration: duration }); } if (currentTime >= duration) { isRunning = false; if (timer) clearTimeout(timer); setTimeout(function() { self.postMessage({ type: 'completed' }); }, 3000); } else { timer = setTimeout(updateLoop, 200); } } updateLoop(); self.onmessage = function(e) { var data = e.data; switch(data.type) { case 'updateRate': rate = Math.min(data.rate, 1.1); var baseInterval = ${CONFIG.reportBaseInterval}; reportInterval = Math.max(10, Math.floor(baseInterval / rate)); self.postMessage({ type: 'rateUpdated', rate: rate, interval: reportInterval }); break; case 'stop': isRunning = false; if (timer) clearTimeout(timer); self.postMessage({ type: 'stopped' }); break; } }; `; var blob = new Blob([workerCode], { type: 'application/javascript' }); var worker = new Worker(URL.createObjectURL(blob)); worker.videoContext = { name: name, duration: duration, dtoken: dtoken, objectId: objectId, jobId: jobId, rt: rt, otherInfo: otherInfo, reportUrl: reportUrl, classId: classId, uid: uid }; worker.onmessage = function(e) { var data = e.data; switch(data.type) { case 'progress': if (CONFIG.showProgressBar) updateProgressBar(data.currentTime, data.duration); break; case 'rateUpdated': log('📡 上报间隔已更新: 倍速=' + data.rate + 'x, 上报间隔=' + data.interval + 's', 'blue'); break; case 'doReport': var ctx = worker.videoContext; var enc = generateEncLocal(ctx.classId, ctx.uid, ctx.jobId, ctx.objectId, data.playTime, ctx.duration); var reportsUrl = ctx.reportUrl + '/' + ctx.dtoken + '?clazzId=' + ctx.classId + '&playingTime=' + data.playTime + '&duration=' + ctx.duration + '&clipTime=0_' + ctx.duration + '&objectId=' + ctx.objectId + '&otherInfo=' + ctx.otherInfo + '&jobid=' + ctx.jobId + '&userid=' + ctx.uid + '&isdrag=0' + '&view=pc' + '&enc=' + enc + '&rt=' + ctx.rt + '&dtype=Video' + '&_t=' + Date.now(); log('📡 上报: ' + formatDuration(data.playTime) + '/' + formatDuration(ctx.duration) + ' (间隔:' + getActualReportInterval() + 's)', 'blue'); GM_xmlhttpRequest({ method: "GET", url: reportsUrl, headers: { 'Host': LOCATION.host, 'Referer': LOCATION.protocol + '//' + LOCATION.host + '/ananas/modules/video/index.html' }, onload: function(res) { try { var result = JSON.parse(res.responseText); if (result.isPassed || result.status === true || result.msg === 'success') { if (!worker.isCompleted) { worker.isCompleted = true; log('✅ 视频任务已完成: ' + ctx.name, 'green'); if (worker.completeCallback) worker.completeCallback(); } } } catch(e) { if (res.responseText.indexOf('success') !== -1 && !worker.isCompleted) { worker.isCompleted = true; log('✅ 视频任务已完成: ' + ctx.name, 'green'); if (worker.completeCallback) worker.completeCallback(); } } }, onerror: function() { log('⚠️ 上报失败,将继续尝试', 'orange'); } }); break; case 'completed': log('⏹️ 播放完成,发送收尾确认心跳...', 'green'); var ctx = worker.videoContext; var finalEnc = generateEncLocal(ctx.classId, ctx.uid, ctx.jobId, ctx.objectId, ctx.duration, ctx.duration); var finalUrl = ctx.reportUrl + '/' + ctx.dtoken + '?clazzId=' + ctx.classId + '&playingTime=' + ctx.duration + '&duration=' + ctx.duration + '&clipTime=0_' + ctx.duration + '&objectId=' + ctx.objectId + '&otherInfo=' + ctx.otherInfo + '&jobid=' + ctx.jobId + '&userid=' + ctx.uid + '&isdrag=0' + '&view=pc' + '&enc=' + finalEnc + '&rt=' + ctx.rt + '&dtype=Video' + '&_t=' + Date.now(); GM_xmlhttpRequest({ method: "GET", url: finalUrl, headers: { 'Host': LOCATION.host, 'Referer': LOCATION.protocol + '//' + LOCATION.host + '/ananas/modules/video/index.html' }, onload: function() { log('✅ 收尾确认已发送', 'green'); setTimeout(function() { if (worker.completeCallback) worker.completeCallback(); }, 2000); }, onerror: function() { setTimeout(function() { if (worker.completeCallback) worker.completeCallback(); }, 2000); } }); break; case 'stopped': log('📡 Worker已停止', 'blue'); break; } }; return worker; } var currentVideoInterval = null; var currentProgressBar = null; var currentVideoWorker = null; var currentVideoTaskId = null; var videoTaskCounter = 0; var taskList = null; var defaults = null; var domList = null; var submitBtn = null; var saveBtn = null; var frameContent = null; var okBtn = null; var isProcessing = false; var isBoxHidden = false; var pikaqiuAdded = false; var pendingMissionCount = 0; var completedMissionCount = 0; var isJumping = false; var hasTriggeredNoTaskJump = false; var isVideoTaskActive = false; var currentVideoName = null; function forceCleanupAll() { if (currentVideoWorker) { try { currentVideoWorker.postMessage({ type: 'stop' }); setTimeout(function() { try { if (currentVideoWorker) currentVideoWorker.terminate(); } catch(e) {} }, 100); currentVideoWorker = null; } catch(e) {} } if (currentVideoInterval) { clearInterval(currentVideoInterval); currentVideoInterval = null; } if (reportWorker) { try { reportWorker.terminate(); } catch(e) {} reportWorker = null; } if (currentVideoQuizObserver) { try { currentVideoQuizObserver.disconnect(); } catch(e) {} currentVideoQuizObserver = null; } stopQuizObserverGuard(); stopQuizPoll(); removeProgressBar(); isVideoTaskActive = false; currentVideoName = null; activeVideoJob = null; currentVideoTaskId = null; } function removeProgressBar() { if (currentProgressBar) { try { $(currentProgressBar).remove(); currentProgressBar = null; } catch(e) {} } try { var targetDoc = top.document; var existingBar = targetDoc.querySelector('#video-progress-bar'); if (existingBar) $(existingBar).remove(); } catch(e) {} } function createProgressBar(videoName, duration) { removeProgressBar(); currentVideoName = videoName; var taskId = ++videoTaskCounter; currentVideoTaskId = taskId; var targetDoc = top.document; var actualInterval = getActualReportInterval(); var barHtml = `
🎬 ${videoName} 0%
00:00 剩余: --:-- ${formatDuration(duration)}
● 后台上报中 ⚡ ${CONFIG.videoRate.toFixed(2)}x (间隔:${actualInterval}s)
`; try { $(targetDoc.body).append(barHtml); currentProgressBar = targetDoc.querySelector('#video-progress-bar'); } catch(e) { $(document.body).append(barHtml); currentProgressBar = document.querySelector('#video-progress-bar'); } } function updateProgressBar(playTime, duration) { if (!currentProgressBar) return; var taskId = currentProgressBar.getAttribute('data-task-id'); if (taskId && currentVideoTaskId && parseInt(taskId) !== currentVideoTaskId) return; var percent = Math.min(100, Math.round((playTime / duration) * 100)); var remaining = Math.max(0, duration - playTime); var fillBar = currentProgressBar.querySelector('#progress-bar-fill'); var percentSpan = currentProgressBar.querySelector('#progress-percent'); var currentSpan = currentProgressBar.querySelector('#progress-current'); var remainingSpan = currentProgressBar.querySelector('#progress-remaining'); if (fillBar) fillBar.style.width = percent + '%'; if (percentSpan) percentSpan.textContent = percent + '%'; if (currentSpan) currentSpan.textContent = formatDuration(playTime); if (remainingSpan) remainingSpan.textContent = '剩余: ' + formatDuration(remaining); } function updateProgressBarRateDisplay() { if (!currentProgressBar) return; var actualInterval = getActualReportInterval(); var rateSpan = currentProgressBar.querySelector('#progress-rate'); if (rateSpan) rateSpan.innerHTML = '⚡ ' + CONFIG.videoRate.toFixed(2) + 'x (间隔:' + actualInterval + 's)'; } function completeCurrentTask() { forceCleanupAll(); if (taskList && taskList.length > 0) { var completedTask = taskList[0]; var taskName = completedTask.property ? (completedTask.property.name || completedTask.property.title || '未知') : '未知'; log('✅ 完成任务: ' + taskName, 'green'); taskList.splice(0, 1); completedMissionCount++; log('📋 任务进度: ' + completedMissionCount + '/' + pendingMissionCount + ' | 剩余: ' + taskList.length, 'blue'); } if (domList && domList.length > 0) domList.splice(0, 1); isProcessing = false; if (taskList && taskList.length > 0) { log('📋 还有 ' + taskList.length + ' 个任务点待处理,继续...', 'green'); setTimeout(function() { startMission(); }, 1000); } else { log('✅ 此页面所有任务处理完毕', 'green'); goToNext(); } } function goToNext() { if (isJumping) return; isJumping = true; var nextBtn = null; try { nextBtn = top.document.querySelector('#mainid > .prev_next.next:not(.disabled)') || top.document.querySelector('#prevNextFocusNext:not(.disabled)') || top.document.querySelector('.prev_next.next:not(.disabled)') || $('.prev_next.next:not(.disabled)')[0]; if (nextBtn && !nextBtn.disabled && !nextBtn.classList.contains('disabled')) { log('✅ 找到下一节按钮,3秒后跳转', 'green'); setTimeout(function() { nextBtn.click(); isJumping = false; }, 3000); return true; } else log('📚 课程已全部完成,无更多章节可跳转', 'green'); } catch(e) { log('❌ 跳转失败: ' + e.message, 'red'); } isJumping = false; return false; } function getVideoStatus(item, callback) { var objectId = item.property.objectid; var statusUrl = LOCATION.protocol + '//' + LOCATION.host + '/ananas/status/' + objectId + '?k=' + (getCookie('fid') || '') + '&flag=normal&_dc=' + Date.now(); GM_xmlhttpRequest({ method: "GET", url: statusUrl, headers: { 'Host': LOCATION.host, 'Referer': LOCATION.protocol + '//' + LOCATION.host + '/ananas/modules/video/index.html' }, onload: function(res) { try { var videoInfo = JSON.parse(res.responseText); callback({ success: true, duration: videoInfo.duration, dtoken: videoInfo.dtoken, objectId: objectId, rt: item.property.rt || '0.9', otherInfo: item.otherInfo || '', jobid: item.jobid, playedTime: videoInfo.playingTime || 0, isPassed: videoInfo.isPassed || false, name: item.property.name }); } catch(e) { callback({ success: false, error: e.message }); } }, onerror: function(err) { callback({ success: false, error: err.error }); } }); } function simulateVideoReport(videoInfo, taskObj) { var name = videoInfo.name || taskObj.property.name; var duration = videoInfo.duration; var isAlreadyPassed = videoInfo.isPassed || false; if (isAlreadyPassed && !CONFIG.reviewMode) { log('✅ 视频已完成: ' + name + ',跳过', 'green'); completeCurrentTask(); return; } var actualInterval = getActualReportInterval(); log('🎬 模拟上报模式: ' + name + ',总时长: ' + formatDuration(duration), 'purple'); log('📡 上报间隔: ' + actualInterval + '秒 (安全倍速 ' + Math.min(CONFIG.videoRate, 1.1).toFixed(2) + 'x)', 'blue'); if (CONFIG.showProgressBar) { createProgressBar(name, duration); updateProgressBar(videoInfo.playedTime || 0, duration); } isVideoTaskActive = true; currentVideoName = name; currentVideoWorker = createReportWorker(videoInfo, taskObj); reportWorker = currentVideoWorker; var workerTaskId = videoTaskCounter; currentVideoWorker.completeCallback = function() { if (workerTaskId === currentVideoTaskId && isVideoTaskActive) { forceCleanupAll(); completeCurrentTask(); } }; } function normalVideoPlay(dom, obj) { var name = obj.property.name; var target = dom.length > 0 ? dom[0] : null; if (!target) { log('⚠️ 未找到视频iframe,3秒后重试', 'orange'); isProcessing = false; setTimeout(function() { normalVideoPlay(dom, obj); }, 3000); return; } log('🎬 正常播放模式处理视频:' + name, 'purple'); isVideoTaskActive = true; currentVideoName = name; var executed = false; var doc = target.contentDocument || target.contentWindow.document; if (currentVideoInterval) clearInterval(currentVideoInterval); currentVideoInterval = setInterval(function() { var media = doc.querySelector('video') || doc.querySelector('audio'); if (media && !executed) { executed = true; log('✅ ' + name + ' 开始播放', 'green'); media.muted = true; var playRate = Math.min(CONFIG.videoRate, 8); media.playbackRate = playRate > 1 ? playRate : 1; media.play(); if (doc && doc.body && !currentVideoQuizObserver) { observeVideoQuiz(doc); startQuizObserverGuard(); startQuizPoll(doc); } var resumePlay = function() { if (media.paused && !media.ended) media.play(); }; media.addEventListener('pause', resumePlay); var onVideoEnd = function() { log('✅ ' + name + ' 播放完成', 'green'); media.removeEventListener('pause', resumePlay); media.removeEventListener('ended', onVideoEnd); clearInterval(currentVideoInterval); currentVideoInterval = null; forceCleanupAll(); completeCurrentTask(); }; media.addEventListener('ended', onVideoEnd); if (media.ended) onVideoEnd(); } }, 1500); } function processVideo(dom, obj) { if (!CONFIG.handleVideo) { log('ℹ️ 用户设置不处理视频任务', 'orange'); completeCurrentTask(); return; } var isPassed = obj.isPassed; var name = obj.property.name; if (!CONFIG.reviewMode && isPassed === true) { log('✅ 视频:' + name + ' 已完成,跳过', 'green'); completeCurrentTask(); return; } if (CONFIG.videoMode === 'simulate') { log('🎬 模拟上报模式处理视频: ' + name, 'purple'); getVideoStatus(obj, function(result) { if (result.success) simulateVideoReport(result, obj); else { log('❌ 获取视频信息失败: ' + result.error + ',降级到正常播放', 'red'); normalVideoPlay(dom, obj); } }); return; } normalVideoPlay(dom, obj); } function processAudio(dom, obj) { if (!CONFIG.handleAudio) { log('ℹ️ 用户设置不处理音频任务', 'orange'); completeCurrentTask(); return; } var isPassed = obj.isPassed; var name = obj.property.name; if (!CONFIG.reviewMode && isPassed === true) { log('✅ 音频:' + name + ' 已完成,跳过', 'green'); completeCurrentTask(); return; } log('🎵 处理音频:' + name + ',等待5秒后完成', 'purple'); setTimeout(function() { completeCurrentTask(); }, 5000); } function processBook(dom, obj) { var jobId = obj.property.jobid; var name = obj.property.bookname; var jtoken = obj.jtoken; var knowledgeId = defaults.knowledgeid; var courseId = defaults.courseid; var clazzId = defaults.clazzId; if (isTaskCompleted(obj)) { log('✅ 读书:' + name + ' 已完成,跳过', 'green'); completeCurrentTask(); return; } $.ajax({ url: LOCATION.protocol + "//" + LOCATION.host + '/ananas/job?jobid=' + jobId + '&knowledgeid=' + knowledgeId + '&courseid=' + courseId + '&clazzid=' + clazzId + '&jtoken=' + jtoken + '&_dc=' + Date.now(), method: 'GET', success: function(res) { log('📚 读书:' + name + (res.msg || '完成'), 'green'); completeCurrentTask(); }, error: function() { log('❌ 读书:' + name + ' 处理失败,3秒后重试', 'red'); isProcessing = false; setTimeout(function() { processBook(dom, obj); }, 3000); } }); } function processDocument(dom, obj) { var jobId = obj.property.jobid; var name = obj.property.name; var jtoken = obj.jtoken; var knowledgeId = defaults.knowledgeid; var courseId = defaults.courseid; var clazzId = defaults.clazzId; if (isTaskCompleted(obj)) { log('✅ 文档:' + name + ' 已完成,跳过', 'green'); completeCurrentTask(); return; } $.ajax({ url: LOCATION.protocol + "//" + LOCATION.host + '/ananas/job/document?jobid=' + jobId + '&knowledgeid=' + knowledgeId + '&courseid=' + courseId + '&clazzid=' + clazzId + '&jtoken=' + jtoken + '&_dc=' + Date.now(), method: 'GET', success: function(res) { log('📄 文档:' + name + (res.msg || '完成'), 'green'); completeCurrentTask(); }, error: function() { log('❌ 文档:' + name + ' 处理失败,3秒后重试', 'red'); isProcessing = false; setTimeout(function() { processDocument(dom, obj); }, 3000); } }); } function processRead(dom, obj) { var jobId = obj.property.jobid; var name = obj.property.title; var jtoken = obj.jtoken; var knowledgeId = defaults.knowledgeid; var courseId = defaults.courseid; var clazzId = defaults.clazzId; if (isTaskCompleted(obj)) { log('✅ 阅读:' + name + ' 已完成,跳过', 'green'); completeCurrentTask(); return; } $.ajax({ url: LOCATION.protocol + '//' + LOCATION.host + '/ananas/job/readv2?jobid=' + jobId + '&knowledgeid=' + knowledgeId + '&courseid=' + courseId + '&clazzid=' + clazzId + '&jtoken=' + jtoken + '&_dc=' + Date.now(), method: 'GET', success: function(res) { log('📖 阅读:' + name + (res.msg || '完成'), 'green'); completeCurrentTask(); }, error: function() { log('❌ 阅读:' + name + ' 处理失败,3秒后重试', 'red'); isProcessing = false; setTimeout(function() { processRead(dom, obj); }, 3000); } }); } function startMission() { if (isProcessing) { log('⚠️ 已有任务在处理中,跳过', 'orange'); return; } if (!taskList || taskList.length <= 0) { log('✅ 此页面任务处理完毕', 'green'); isProcessing = false; goToNext(); return; } forceCleanupAll(); isProcessing = true; var taskType = taskList[0].type; var dom = domList[0]; var task = taskList[0]; if (taskType == undefined) taskType = taskList[0].property.module; var taskName = task.property ? (task.property.name || task.property.title || '未知') : '未知'; log('🔄 开始处理任务: ' + taskName + ' (类型: ' + taskType + ')', 'blue'); var handlers = { 'video': function() { processVideo(dom, task); }, 'audio': function() { processAudio(dom, task); }, 'workid': function() { processQuiz(dom, task); }, 'document': function() { processDocument(dom, task); }, 'read': function() { processRead(dom, task); }, 'insertbook': function() { processBook(dom, task); } }; if (handlers[taskType]) handlers[taskType](); else if (['insertimage'].indexOf(taskType) !== -1) { log('ℹ️ 发现无需处理任务,跳过', 'orange'); completeCurrentTask(); } else { log('⚠️ 暂不支持处理此类型:' + taskType + ',跳过', 'red'); completeCurrentTask(); } } // ========== 答题核心 ========== function processQuiz(dom, obj) { if (!CONFIG.handleQuiz) { log('ℹ️ 用户设置不自动处理测验', 'orange'); completeCurrentTask(); return; } if (!checkApiKeyBeforeAction('自动答题')) { completeCurrentTask(); return; } var isDo = true; if (CONFIG.taskOnly && obj.jobid == undefined) isDo = false; if (isDo) { if (obj.jobid !== undefined) { var phoneWeb = LOCATION.protocol + '//' + LOCATION.host + '/work/phone/work?workId=' + obj.jobid.replace('work-', '') + '&courseId=' + defaults.courseid + '&clazzId=' + defaults.clazzId + '&knowledgeId=' + defaults.knowledgeid + '&jobId=' + obj.jobid + '&enc=' + obj.enc; log('📝 准备处理测验', 'purple'); setTimeout(function() { startPhoneQuiz(0, [dom], phoneWeb, obj); }, 3000); } else setTimeout(function() { startPCQuiz(0, [dom], obj); }, 3000); } else { log('ℹ️ 用户设置只处理属于任务点的任务', 'orange'); completeCurrentTask(); } } function startPhoneQuiz(index, doms, phoneWeb, taskObj) { if (index == doms.length) { log('✅ 此页面全部测验已处理完毕', 'green'); completeCurrentTask(); return; } getElement($(doms[index]).contents()[0], 'iframe').then(function(iframe) { if (!iframe) { setTimeout(function() { startPhoneQuiz(index, doms, phoneWeb, taskObj); }, 5000); return; } var workIframe = $(iframe); var workStatus = workIframe.contents().find('.newTestCon .newTestTitle .testTit_status').text().trim(); if (!workStatus) { domList.splice(0, 1); isProcessing = false; setTimeout(switchMission, 2000); return; } if (workStatus.indexOf("待做") != -1 || workStatus.indexOf("待完成") != -1 || workStatus.indexOf("未达到及格线") != -1) { workIframe.attr('src', phoneWeb); getElement($(doms[index]).contents()[0], 'iframe[src="' + phoneWeb + '"]').then(function() { setTimeout(function() { doPhoneQuiz(workIframe.contents(), taskObj); }, 3000); }); } else if (workStatus.indexOf('待批阅') != -1) { taskList.splice(0, 1); domList.splice(0, 1); log('⚠️ 测验待批阅,跳过', 'red'); completeCurrentTask(); } else if (workStatus.indexOf('已完成') != -1 || workStatus.indexOf('已交') != -1) { log('✅ 测验已完成,跳过', 'green'); completeCurrentTask(); } else { taskList.splice(0, 1); domList.splice(0, 1); log('⚠️ 未知状态,跳过', 'red'); completeCurrentTask(); } }); } function doPhoneQuiz($dom, taskObj) { var $cy = $dom.find('.Wrappadding form'); submitBtn = $cy.find('.zquestions .zsubmit .btn-ok-bottom'); okBtn = $dom.find('#okBtn'); saveBtn = $cy.find('.zquestions .zsubmit .btn-save'); var questionList = $cy.find('.zquestions .Py-mian1'); processPhoneQuestions(0, questionList, taskObj); } function processPhoneQuestions(index, questionList, taskObj) { if (index == questionList.length) { var decision = getSubmitDecision(); if (decision.action === 'submit') { log('✅ 测验处理完成,准备' + decision.reason + '。', 'green'); setTimeout(function() { if (submitBtn && submitBtn.length) submitBtn.click(); setTimeout(function() { if (okBtn && okBtn.length) okBtn.click(); log('✅ 提交成功', 'green'); completeCurrentTask(); }, 3000); }, 5000); } else { log('💾 ' + decision.reason + ',自动保存', 'green'); setTimeout(function() { if (saveBtn && saveBtn.length) saveBtn.click(); log('✅ 保存成功', 'green'); completeCurrentTask(); }, 5000); } return; } var contextWindow = questionList[index] ? (questionList[index].ownerDocument.defaultView || unsafeWindow) : unsafeWindow; var $question = $(questionList[index]); var questionHtml = $question.find('.Py-m1-title').html() || $question.html() || ''; var typeName = ''; var bracketMatch = questionHtml.match(/\[(.*?)\]/); var _hash = atob('YW13eGI='); if (bracketMatch && bracketMatch[1]) { typeName = bracketMatch[1]; } if (!typeName) { var chineseMatch = questionHtml.match(/(单选题|多选题|填空题|判断题|简答题)/); if (chineseMatch) { typeName = chineseMatch[1]; } } var typeMap = { '单选题': 0, '多选题': 1, '填空题': 2, '判断题': 3, '简答题': 4 }; var questionType = typeMap[typeName]; if (questionType === undefined) { questionType = getQuestionTypeFromText(questionHtml); } if (questionType === undefined) { if ($question.find('.answerList.singleChoice li').length) questionType = 0; else if ($question.find('.answerList.multiChoice li').length) questionType = 1; else if ($question.find('.blankList2 input').length) questionType = 2; else if ($question.find('.answerList.panduan li').length) questionType = 3; else if ($question.find('textarea').length) questionType = 4; } if (questionType === undefined) { log('⚠️ 第' + (index + 1) + '题无法识别题型,跳过', 'orange'); setTimeout(function() { processPhoneQuestions(index + 1, questionList, taskObj); }, getRandomInterval()); return; } var cleanedQuestion = tidyQuestion(questionHtml); cleanedQuestion = cleanedQuestion.replace(/\[.*?\]/, '').trim(); cleanedQuestion = cleanedQuestion.replace(/^\d+[.、]?\s*/, ''); var checkAnswered = function() { if (questionType == 0 || questionType == 1) { var $opts = questionType == 0 ? $question.find('.answerList.singleChoice li') : $question.find('.answerList.multiChoice li'); for (var i = 0; i < $opts.length; i++) { if ($($opts[i]).attr('aria-label')) return true; } } else if (questionType == 2) { var $inputs = $question.find('.blankList2 input'); if ($inputs.length && $inputs.first().val() && $inputs.first().val().trim() !== '') return true; var $editorBlocks = $question.find('[data-editorindex]'); if ($editorBlocks.length) { var hasContent = false; $editorBlocks.each(function() { var itemId = $(this).attr('data-itemid'); if (itemId && $('#answer' + itemId).val() && $('#answer' + itemId).val().trim() !== '') { hasContent = true; } }); if (hasContent) return true; } } else if (questionType == 3) { var $pd = $question.find('.answerList.panduan li'); for (var i = 0; i < $pd.length; i++) { if ($($pd[i]).attr('aria-label')) return true; } } else if (questionType == 4) { var $ta = $question.find('textarea[name^="answer"]'); if ($ta.length && $ta.first().val() && $ta.first().val().trim() !== '') return true; var $editorBlocks = $question.find('[data-editorindex]'); if ($editorBlocks.length) { var hasContent = false; $editorBlocks.each(function() { var itemId = $(this).attr('data-itemid'); if (itemId && $('#answer' + itemId).val() && $('#answer' + itemId).val().trim() !== '') { hasContent = true; } }); if (hasContent) return true; } } return false; }; if (checkAnswered()) { log('📌 第' + (index + 1) + '题已作答,跳过', 'green'); setTimeout(function() { processPhoneQuestions(index + 1, questionList, taskObj); }, 30); return; } var $opts = []; var optsText = []; var optionsData = []; var pureQuestion = cleanedQuestion; if (questionType == 0 || questionType == 1) { $opts = questionType == 0 ? $question.find('.answerList.singleChoice li') : $question.find('.answerList.multiChoice li'); $opts.each(function() { var rawText = tidyString($(this).html()); rawText = rawText.replace(/^[A-Z][\s]*[.、.))\s]+/, ''); rawText = rawText.replace(/^\([A-Z]\)[\s]*/, ''); rawText = rawText.replace(/^([A-Z])[\s]*/, ''); optsText.push(rawText); }); optionsData = optsText; } else if (questionType == 2) { var blankCount = getBlankCount($question); optionsData = [blankCount.toString()]; } else if (questionType == 3) optionsData = ["对", "错"]; else if (questionType == 4) optionsData = ["1"]; getAnswer(questionType, pureQuestion, optionsData).then(function(answer) { if (answer == '暂无答案' || !answer) { log('⚠️ 第' + (index + 1) + '题无法获取答案,跳过', 'red'); setTimeout(function() { processPhoneQuestions(index + 1, questionList, taskObj); }, getRandomInterval()); return; } if (CONFIG.insertAnswer) $question.find('.Py-m1-title').html($question.find('.Py-m1-title').html() + '

📖 ' + answer + '

'); if (questionType == 0) { var normalizedOpts = optsText.map(function(opt) { return normalizeForCompare(opt); }); var normalizedAnswer = normalizeForCompare(answer); var idx = normalizedOpts.findIndex(function(t) { return t === normalizedAnswer; }); if (idx === -1) idx = normalizedOpts.findIndex(function(t) { return (t.indexOf(normalizedAnswer) !== -1 && t.length - normalizedAnswer.length <= 1) || (normalizedAnswer.indexOf(t) !== -1 && normalizedAnswer.length - t.length <= 1); }); if (idx != -1) { $question.find('.answerList.singleChoice li').eq(idx).click(); log('✅ 单选题已选: ' + optsText[idx], 'green'); } else log('⚠️ 未找到匹配选项: ' + answer, 'orange'); } else if (questionType == 1) { var hitIdx = matchMultiChoiceIndices(optsText, answer); if (hitIdx.length > 0) { hitIdx.forEach(function(i, k) { setTimeout(function() { $($opts[i]).click(); }, 300 * (k + 1)); }); log('✅ 多选题已选 ' + hitIdx.length + ' 项: ' + hitIdx.map(function(i){return optsText[i];}).join(' | '), 'green'); } else { log('⚠️ 多选题未匹配到任何选项,答案: ' + answer, 'orange'); } } else if (questionType == 2) fillBlankAnswer($question, answer, true, contextWindow); else if (questionType == 3) { var trueKeywords = '正确|是|对|√|T|ri'; var $pd = $question.find('.answerList.panduan li'); if (answer && answer.trim() !== '' && (trueKeywords.indexOf(answer) != -1 || trueKeywords.toLowerCase().indexOf(answer.toLowerCase()) != -1)) { $pd.each(function(i, t) { if ($(t).attr('val-param') == 'true') { $(t).click(); log('✅ 判断题自动选择: 正确', 'green'); } }); } else { $pd.each(function(i, t) { if ($(t).attr('val-param') == 'false') { $(t).click(); log('✅ 判断题自动选择: 错误', 'green'); } }); } } else if (questionType == 4) fillShortAnswer($question, answer, true, contextWindow); log('✅ 第' + (index + 1) + '题自动答题成功', 'green'); setTimeout(function() { processPhoneQuestions(index + 1, questionList, taskObj); }, getRandomInterval()); }).catch(function() { log('⚠️ 第' + (index + 1) + '题获取答案失败,跳过', 'orange'); setTimeout(function() { processPhoneQuestions(index + 1, questionList, taskObj); }, getRandomInterval()); }); } function startPCQuiz(index, doms, taskObj) { if (index == doms.length) { log('✅ 此页面全部测验已处理完毕', 'green'); completeCurrentTask(); return; } getElement($(doms[index]).contents()[0], 'iframe').then(function(iframe) { if (!iframe) { setTimeout(function() { startPCQuiz(index, doms, taskObj); }, 5000); return; } var workIframe = $(iframe); var workStatus = workIframe.contents().find(".newTestCon .newTestTitle .testTit_status").text().trim(); if (!workStatus) { domList.splice(0, 1); isProcessing = false; setTimeout(switchMission, 2000); return; } if (workStatus.indexOf("待做") != -1 || workStatus.indexOf("待完成") != -1) { log('📝 准备处理测验', 'purple'); setTimeout(function() { doPCQuiz(index, doms, iframe, taskObj); }, 5000); } else if (workStatus.indexOf('待批阅') != -1) { taskList.splice(0, 1); domList.splice(0, 1); log('⚠️ 测验待批阅,跳过', 'red'); completeCurrentTask(); } else if (workStatus.indexOf('已完成') != -1 || workStatus.indexOf('已交') != -1) { log('✅ 测验已完成,跳过', 'green'); completeCurrentTask(); } else { taskList.splice(0, 1); domList.splice(0, 1); log('⚠️ 未知状态,跳过', 'red'); completeCurrentTask(); } }); } function doPCQuiz(index, doms, dom, taskObj) { frameContent = $(dom).contents(); var $cyHtml = frameContent.find('.CeYan'); var questionList = $cyHtml.find('.TiMu'); submitBtn = frameContent.find(".ZY_sub").find(".btnSubmit"); saveBtn = frameContent.find(".ZY_sub").find(".btnSave"); processPCQuestions(0, questionList); } function processPCQuestions(index, questionList) { if (index == questionList.length) { var decision = getSubmitDecision(); if (decision.action === 'submit') { log('✅ 测验处理完成,准备' + decision.reason + '。', 'green'); setTimeout(function() { if (submitBtn && submitBtn.length) submitBtn.click(); setTimeout(function() { if (frameContent && frameContent.find('#confirmSubWin > div > div > a.bluebtn').length) frameContent.find('#confirmSubWin > div > div > a.bluebtn').click(); log('✅ 提交成功', 'green'); completeCurrentTask(); }, 3000); }, 5000); } else { log('💾 ' + decision.reason + ',自动保存', 'green'); setTimeout(function() { if (saveBtn && saveBtn.length) saveBtn.click(); log('✅ 保存成功', 'green'); completeCurrentTask(); }, 5000); } return; } var $question = $(questionList[index]); var questionHtml = $question.find('.Zy_TItle.clearfix > div').html() || $question.html() || ''; var typeName = ''; var bracketMatch = questionHtml.match(/【(.*?)】/); if (bracketMatch && bracketMatch[1]) { typeName = bracketMatch[1]; } if (!typeName) { var chineseMatch = questionHtml.match(/(单选题|多选题|填空题|判断题|简答题)/); if (chineseMatch) { typeName = chineseMatch[1]; } } var typeMap = { '单选题': 0, '多选题': 1, '填空题': 2, '判断题': 3, '简答题': 4 }; var questionType = typeMap[typeName]; if (questionType === undefined) { questionType = getQuestionTypeFromText(questionHtml); } if (questionType === undefined) { if ($question.find('.Zy_ulTop li').length) { questionType = $question.find('input[type="checkbox"]').length ? 1 : 0; } else if ($question.find('.Zy_ulTk .XztiHover1').length) { questionType = 2; } else { questionType = 4; } } var cleanedQuestion = tidyQuestion(questionHtml); cleanedQuestion = cleanedQuestion.replace(/【.*?】/, '').trim(); cleanedQuestion = cleanedQuestion.replace(/^\d+[.、]?\s*/, ''); var $opts = []; var optsText = []; var optionsData = []; var pureQuestion = cleanedQuestion; if (questionType == 0 || questionType == 1) { $opts = $question.find('.Zy_ulTop li a'); $opts.each(function() { var rawText = tidyString($(this).html()); rawText = rawText.replace(/^[A-Z][\s]*[.、.))\s]+/, ''); rawText = rawText.replace(/^\([A-Z]\)[\s]*/, ''); rawText = rawText.replace(/^([A-Z])[\s]*/, ''); optsText.push(rawText); }); optionsData = optsText; } else if (questionType == 2) { var blankCount = getBlankCount($question); optionsData = [blankCount.toString()]; } else if (questionType == 3) optionsData = ["对", "错"]; else if (questionType == 4) optionsData = ["1"]; getAnswer(questionType, pureQuestion, optionsData).then(function(answer) { if (answer == '暂无答案' || !answer) { log('⚠️ PC第' + (index + 1) + '题无法获取答案,跳过', 'orange'); setTimeout(function() { processPCQuestions(index + 1, questionList); }, getRandomInterval()); return; } if (CONFIG.insertAnswer) $question.find('.Zy_TItle.clearfix > div').html($question.find('.Zy_TItle.clearfix > div').html() + '

📖 ' + answer + '

'); if (questionType == 0) { var normalizedOpts = optsText.map(function(opt) { return normalizeForCompare(opt); }); var normalizedAnswer = normalizeForCompare(answer); var idx = normalizedOpts.findIndex(function(t) { return t === normalizedAnswer; }); if (idx === -1) idx = normalizedOpts.findIndex(function(t) { return (t.indexOf(normalizedAnswer) !== -1 && t.length - normalizedAnswer.length <= 1) || (normalizedAnswer.indexOf(t) !== -1 && normalizedAnswer.length - t.length <= 1); }); if (idx != -1) { $($opts[idx]).parent().click(); log('✅ PC单选题已选: ' + optsText[idx], 'green'); } else log('⚠️ 未找到匹配选项: ' + answer, 'orange'); } else if (questionType == 1) { var hitIdx = matchMultiChoiceIndices(optsText, answer); if (hitIdx.length > 0) { hitIdx.forEach(function(i, k) { setTimeout(function() { $($opts[i]).parent().click(); }, 300 * (k + 1)); }); log('✅ PC多选题已选 ' + hitIdx.length + ' 项: ' + hitIdx.map(function(i){return optsText[i];}).join(' | '), 'green'); } else { log('⚠️ PC多选题未匹配到任何选项,答案: ' + answer, 'orange'); } } else if (questionType == 2) fillBlankAnswer($question, answer, false, null); else if (questionType == 3) { var trueKeywords = '正确|是|对|√|T|ri'; var $pdContainer = $question.find('.Zy_ulTop li'); if ($pdContainer.length === 0) { $pdContainer = $question.find('.stem_answer .answer_p').parent(); } if (answer && answer.trim() !== '' && (trueKeywords.indexOf(answer) != -1 || trueKeywords.toLowerCase().indexOf(answer.toLowerCase()) != -1)) { var clicked = false; $pdContainer.each(function(i, t) { var valAttr = $(t).find('span').attr('val-param') || $(t).attr('val-param'); if (valAttr == 'true') { $(t).click(); clicked = true; log('✅ PC判断题自动选择: 正确', 'green'); } }); if (!clicked && $pdContainer.length > 0) { $($pdContainer[0]).click(); log('✅ PC判断题自动选择: 正确(默认)', 'green'); } } else { var clicked = false; $pdContainer.each(function(i, t) { var valAttr = $(t).find('span').attr('val-param') || $(t).attr('val-param'); if (valAttr == 'false') { $(t).click(); clicked = true; log('✅ PC判断题自动选择: 错误', 'green'); } }); if (!clicked && $pdContainer.length > 1) { $($pdContainer[1]).click(); log('✅ PC判断题自动选择: 错误(默认)', 'green'); } else if (!clicked && $pdContainer.length === 1) { $($pdContainer[0]).click(); log('✅ PC判断题自动选择: 错误(唯一)', 'green'); } } } else if (questionType == 4) fillShortAnswer($question, answer, false, null); log('✅ PC第' + (index + 1) + '题自动答题成功', 'green'); setTimeout(function() { processPCQuestions(index + 1, questionList); }, getRandomInterval()); }).catch(function() { log('⚠️ PC第' + (index + 1) + '题获取答案失败,跳过', 'orange'); setTimeout(function() { processPCQuestions(index + 1, questionList); }, getRandomInterval()); }); } function switchMission() { isProcessing = false; startMission(); } function processHomework() { log('📝 开始处理作业', 'green'); if (!checkApiKeyBeforeAction('作业自动答题')) return; var $homeworkTable = $('.mark_table').find('form'); var questionList = $homeworkTable.find('.questionLi'); doHomework(0, questionList); } function doHomework(index, questionList) { if (index == questionList.length) { log('✅ 作业题目已全部完成', 'green'); return; } var $question = $(questionList[index]); var typeName = $question.attr('typename'); var typeMap = { '单选题': 0, '多选题': 1, '填空题': 2, '判断题': 3, '简答题': 4 }; var questionType = typeMap[typeName]; var questionFull = $question.find('.mark_name').html() || ''; var cleanedQuestion = tidyQuestion(questionFull); cleanedQuestion = cleanedQuestion.replace(/^[(].*?[)]/, '').trim(); cleanedQuestion = cleanedQuestion.replace(/^\d+[.、]?\s*/, ''); if (questionType === undefined) { questionType = getQuestionTypeFromText(questionFull); } if (questionType === undefined) { var answerTmpArr = $question.find('.stem_answer').find('.answer_p'); if (answerTmpArr && answerTmpArr.length > 0) { questionType = $question.find('input[type="checkbox"]').length ? 1 : 0; } else if ($question.find('.stem_answer').find('.divText textarea').length) { questionType = 4; } } if (questionType === undefined) { log('⚠️ 作业第' + (index + 1) + '题无法识别题型,跳过', 'orange'); setTimeout(function() { doHomework(index + 1, questionList); }, getRandomInterval()); return; } var checkAnswered = function() { if (questionType == 0 || questionType == 1) { var $opts = $question.find('.stem_answer .answer_p'); for (var i = 0; i < $opts.length; i++) { if ($($opts[i]).parent().find('span').attr('class') && $($opts[i]).parent().find('span').attr('class').indexOf('check_answer') != -1) { return true; } } } else if (questionType == 2) { var $inputs = $question.find('.stem_answer .Answer .divText .textDIV textarea'); if ($inputs.length && $inputs.val() && $inputs.val().trim() !== '') return true; } else if (questionType == 3) { var $opts = $question.find('.stem_answer .answer_p'); for (var i = 0; i < $opts.length; i++) { if ($($opts[i]).parent().find('span').attr('class') && $($opts[i]).parent().find('span').attr('class').indexOf('check_answer') != -1) { return true; } } } else if (questionType == 4) { var $ta = $question.find('.stem_answer .eidtDiv textarea'); if ($ta.length && $ta.val() && $ta.val().trim() !== '') return true; } return false; }; if (checkAnswered()) { log('📌 作业第' + (index + 1) + '题已作答,跳过', 'green'); setTimeout(function() { doHomework(index + 1, questionList); }, 30); return; } var $opts = []; var optsText = []; var optionsData = []; var pureQuestion = cleanedQuestion; if (questionType == 0 || questionType == 1) { $opts = $question.find('.stem_answer .answer_p'); $opts.each(function() { var rawText = tidyString($(this).html()); rawText = rawText.replace(/^[A-Z][\s]*[.、.))\s]+/, ''); rawText = rawText.replace(/^\([A-Z]\)[\s]*/, ''); rawText = rawText.replace(/^([A-Z])[\s]*/, ''); optsText.push(rawText); }); optionsData = optsText; } else if (questionType == 2) { var blankCount = getBlankCount($question); optionsData = [blankCount.toString()]; } else if (questionType == 3) optionsData = ["对", "错"]; else if (questionType == 4) optionsData = ["1"]; getAnswer(questionType, pureQuestion, optionsData).then(function(answer) { if (answer == '暂无答案' || !answer) { log('⚠️ 作业第' + (index + 1) + '题无法获取答案,跳过', 'orange'); setTimeout(function() { doHomework(index + 1, questionList); }, getRandomInterval()); return; } if (CONFIG.insertAnswer) $question.find('.mark_name').html($question.find('.mark_name').html() + '

📖 ' + answer + '

'); if (questionType == 0) { var normalizedOpts = optsText.map(function(opt) { return normalizeForCompare(opt); }); var normalizedAnswer = normalizeForCompare(answer); var idx = normalizedOpts.findIndex(function(t) { return t === normalizedAnswer; }); if (idx === -1) idx = normalizedOpts.findIndex(function(t) { return (t.indexOf(normalizedAnswer) !== -1 && t.length - normalizedAnswer.length <= 1) || (normalizedAnswer.indexOf(t) !== -1 && normalizedAnswer.length - t.length <= 1); }); if (idx != -1) { $($opts[idx]).parent().click(); log('✅ 作业单选题已选: ' + optsText[idx], 'green'); } } else if (questionType == 1) { var hitIdx = matchMultiChoiceIndices(optsText, answer); if (hitIdx.length > 0) { hitIdx.forEach(function(i, k) { setTimeout(function() { $($opts[i]).parent().click(); }, 300 * (k + 1)); }); log('✅ 作业多选题已选 ' + hitIdx.length + ' 项: ' + hitIdx.map(function(i){return optsText[i];}).join(' | '), 'green'); } else { log('⚠️ 作业多选题未匹配到任何选项,答案: ' + answer, 'orange'); } } else if (questionType == 2) fillBlankAnswer($question, answer, false, null); else if (questionType == 3) { var trueKeywords = '正确|是|对|√|T|ri'; var $pdContainer = $question.find('.stem_answer .answer_p').parent(); if (answer && answer.trim() !== '' && (trueKeywords.indexOf(answer) != -1 || trueKeywords.toLowerCase().indexOf(answer.toLowerCase()) != -1)) { if ($pdContainer.length > 0) { $($pdContainer[0]).click(); log('✅ 作业判断题自动选择: 正确', 'green'); } } else { if ($pdContainer.length > 1) { $($pdContainer[1]).click(); log('✅ 作业判断题自动选择: 错误', 'green'); } else if ($pdContainer.length === 1) { $($pdContainer[0]).click(); log('✅ 作业判断题自动选择: 错误(唯一)', 'green'); } } } else if (questionType == 4) fillShortAnswer($question, answer, false, null); log('✅ 作业第' + (index + 1) + '题自动答题成功', 'green'); setTimeout(function() { doHomework(index + 1, questionList); }, getRandomInterval()); }).catch(function() { log('⚠️ 作业第' + (index + 1) + '题获取答案失败,跳过', 'orange'); setTimeout(function() { doHomework(index + 1, questionList); }, getRandomInterval()); }); } function processExam() { if (!checkApiKeyBeforeAction('考试自动答题')) return; var $examTable = $('.mark_table').find('.whiteDiv'); var questionFull = tidyString($examTable.find('h3.mark_name').html().trim()); var typeName = questionFull.match(/[(](.*?),.*?分[)]|$/)[1]; var typeMap = { '单选题': 0, '多选题': 1, '填空题': 2, '判断题': 3, '简答题': 4 }; var questionType = typeMap[typeName]; var cleanedQuestion = tidyQuestion(questionFull.replace(/[(].*?分[)]/, '').replace(/^\s*/, '')); cleanedQuestion = cleanedQuestion.replace(/^\d+[.、]?\s*/, ''); var $answerDom = $examTable.find('#submitTest').find('.stem_answer'); if (questionType === undefined) { questionType = getQuestionTypeFromText(questionFull); } if (questionType === undefined) { var $opts = $answerDom.find('.clearfix.answerBg .fl.answer_p'); if ($opts.length) { questionType = $answerDom.find('input[type="checkbox"]').length ? 1 : 0; } else if ($answerDom.find('.Answer .divText .subEditor textarea').length) { questionType = 4; } } if (questionType === undefined) { log('⚠️ 考试题目无法识别题型,跳过', 'orange'); goToNextExam(); return; } var checkAnswered = function() { if (questionType == 0 || questionType == 1) { var $opts = $answerDom.find('.clearfix.answerBg .fl.answer_p'); for (var i = 0; i < $opts.length; i++) { if ($($opts[i]).parent().find('span').attr('class') && $($opts[i]).parent().find('span').attr('class').indexOf('check_answer') != -1) { return true; } } } else if (questionType == 2) { var $inputs = $answerDom.find('.Answer .divText .subEditor textarea'); if ($inputs.length && $inputs.val() && $inputs.val().trim() !== '') return true; } else if (questionType == 4) { var $ta = $answerDom.find('.subEditor textarea'); if ($ta.length && $ta.val() && $ta.val().trim() !== '') return true; } return false; }; if (checkAnswered()) { log('📌 考试此题已作答,跳过', 'green'); goToNextExam(); return; } var $opts = []; var optsText = []; var optionsData = []; var pureQuestion = cleanedQuestion; if (questionType == 0 || questionType == 1) { $opts = $answerDom.find('.clearfix.answerBg .fl.answer_p'); $opts.each(function() { var rawText = tidyString($(this).html()); rawText = rawText.replace(/^[A-Z][\s]*[.、.))\s]+/, ''); rawText = rawText.replace(/^\([A-Z]\)[\s]*/, ''); rawText = rawText.replace(/^([A-Z])[\s]*/, ''); optsText.push(rawText); }); optionsData = optsText; } else if (questionType == 2) { var blankCount = getBlankCount($answerDom); optionsData = [blankCount.toString()]; } else if (questionType == 4) optionsData = ["1"]; else if (questionType == 3) optionsData = ["对", "错"]; getAnswer(questionType, pureQuestion, optionsData).then(function(answer) { if (answer == '暂无答案' || !answer) { log('⚠️ 考试此题无法获取答案,跳过', 'orange'); setTimeout(function() { goToNextExam(); }, getRandomInterval()); return; } if (CONFIG.insertAnswer) $examTable.find('h3.mark_name').html($examTable.find('h3.mark_name').html() + '📖 ' + answer + ''); if (questionType == 0) { var normalizedOpts = optsText.map(function(opt) { return normalizeForCompare(opt); }); var normalizedAnswer = normalizeForCompare(answer); var idx = normalizedOpts.findIndex(function(t) { return t === normalizedAnswer; }); if (idx === -1) idx = normalizedOpts.findIndex(function(t) { return (t.indexOf(normalizedAnswer) !== -1 && t.length - normalizedAnswer.length <= 1) || (normalizedAnswer.indexOf(t) !== -1 && normalizedAnswer.length - t.length <= 1); }); if (idx != -1) { $($opts[idx]).parent().click(); log('✅ 考试单选题已选: ' + optsText[idx], 'green'); } } else if (questionType == 1) { var hitIdx = matchMultiChoiceIndices(optsText, answer); if (hitIdx.length > 0) { hitIdx.forEach(function(i, k) { setTimeout(function() { $($opts[i]).parent().click(); }, 300 * (k + 1)); }); log('✅ 考试多选题已选 ' + hitIdx.length + ' 项: ' + hitIdx.map(function(i){return optsText[i];}).join(' | '), 'green'); } else { log('⚠️ 考试多选题未匹配到任何选项,答案: ' + answer, 'orange'); } } else if (questionType == 2) fillBlankAnswer($answerDom, answer, false, null); else if (questionType == 3) { var trueKeywords = '正确|是|对|√|T|ri'; var $pdContainer = $answerDom.find('.clearfix.answerBg .fl.answer_p').parent(); if (answer && answer.trim() !== '' && (trueKeywords.indexOf(answer) != -1 || trueKeywords.toLowerCase().indexOf(answer.toLowerCase()) != -1)) { if ($pdContainer.length > 0) { $($pdContainer[0]).click(); log('✅ 考试判断题选择: 正确', 'green'); } } else { if ($pdContainer.length > 1) { $($pdContainer[1]).click(); log('✅ 考试判断题选择: 错误', 'green'); } else if ($pdContainer.length === 1) { $($pdContainer[0]).click(); log('✅ 考试判断题选择: 错误(唯一)', 'green'); } } } else if (questionType == 4) fillShortAnswer($answerDom, answer, false, null); log('✅ 考试自动答题成功', 'green'); setTimeout(function() { goToNextExam(); }, getRandomInterval()); }).catch(function() { log('⚠️ 考试此题获取答案失败,跳过', 'orange'); setTimeout(function() { goToNextExam(); }, getRandomInterval()); }); } function goToNextExam() { if (CONFIG.autoNextExam) { var $nextBtn = $('.mark_table .whiteDiv .nextDiv a.jb_btn'); var delay = CONFIG.examNextDelay ? 2000 + (Math.floor(Math.random() * 5 + 1) * 1000) : 2000; setTimeout(function() { $nextBtn.click(); }, delay); log('⏭️ ' + (delay/1000) + '秒后自动跳转下一题', 'blue'); } } // ========== 字体解密 ========== function base64ToUint8Array(base64) { var data = window.atob(base64); var buf = new Uint8Array(data.length); for (var i = 0; i < data.length; i++) buf[i] = data.charCodeAt(i); return buf; } function decryptFont() { if (CONFIG.decryptFont !== 1) return; try { if (!TyprInstance || !TyprInstance.U || !TyprInstance.parse) { try { if (typeof unsafeWindow !== 'undefined' && unsafeWindow.Typr) TyprInstance = unsafeWindow.Typr; else if (typeof window !== 'undefined' && window.Typr) TyprInstance = window.Typr; else if (typeof Typr !== 'undefined') TyprInstance = Typr; else if (typeof unsafeWindow !== 'undefined' && unsafeWindow.TyprMd5) TyprInstance = unsafeWindow.TyprMd5; else if (typeof TyprMd5 !== 'undefined') TyprInstance = TyprMd5; } catch(e) {} if (!TyprInstance || !TyprInstance.U) { setTimeout(decryptFont, 1000); return; } } var $tip = $('style:contains(font-cxsecret)'); if (!$tip.length) return; var styleText = $tip.text(); var fontBase64 = null; var match1 = styleText.match(/base64,([\w\W]+?)'/); if (match1 && match1[1]) fontBase64 = match1[1]; if (!fontBase64) { var match2 = styleText.match(/url\("data:application\/font-woff;charset=utf-8;base64,([\w\W]+?)"\)/); if (match2 && match2[1]) fontBase64 = match2[1]; } if (!fontBase64) { var match3 = styleText.match(/url\('data:application\/font-woff;charset=utf-8;base64,([\w\W]+?)'\)/); if (match3 && match3[1]) fontBase64 = match3[1]; } if (!fontBase64) return; var fontUint8Array = base64ToUint8Array(fontBase64); var parsedFont = TyprInstance.parse(fontUint8Array); if (!parsedFont || parsedFont.length === 0) return; var font = parsedFont[0]; var tableJson = GM_getResourceText('Table'); if (!tableJson) return; var table; try { table = JSON.parse(tableJson); } catch(e) { return; } var $secretElements = $('.font-cxsecret'); if ($secretElements.length === 0) return; var existingChars = new Set(); $secretElements.each(function() { var html = $(this).html(); if (html) { for (var j = 0; j < html.length; j++) { var charCode = html.charCodeAt(j); if (charCode >= 19968 && charCode <= 40869) existingChars.add(charCode); } } }); if (existingChars.size === 0) return; var match = {}; var charCodes = Array.from(existingChars); for (var idx = 0; idx < charCodes.length; idx++) { var i = charCodes[idx]; var glyph = TyprInstance.U.codeToGlyph(font, i); if (!glyph) continue; var path = TyprInstance.U.glyphToPath(font, glyph); if (!path) continue; var pathStr = JSON.stringify(path); var hash = MD5(pathStr); if (!hash) continue; var hashKey = hash.slice(24); var realChar = table[hashKey]; if (realChar !== undefined && realChar !== null && realChar !== 0) match[i] = realChar; } var matchCount = Object.keys(match).length; if (matchCount === 0) return; $secretElements.each(function() { var $el = $(this); var html = $el.html(); if (!html) return; for (var code in match) { var encryptedChar = String.fromCharCode(parseInt(code)); var realChar = String.fromCharCode(match[code]); var escapedChar = encryptedChar.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); var regex = new RegExp(escapedChar, 'g'); html = html.replace(regex, realChar); } if ($el.html() !== html) { $el.html(html); $el.removeClass('font-cxsecret'); } }); log('✅ 字体解密完成!已还原 ' + matchCount + ' 个字符', 'green'); } catch(e) { log('❌ 字体解密失败: ' + e.message, 'red'); } } // ========== UI ========== function showPikaqiu() { if (!CONFIG.showMaskImg) return; if (pikaqiuAdded) return; try { var targetDoc = top.document; if (targetDoc.querySelector('#pikaqiu-img')) return; var imgHtml = ''; $(targetDoc.body).append(imgHtml); pikaqiuAdded = true; $('#pikaqiu-img', targetDoc).click(function() { var $box = $('#ne-21box', targetDoc); if ($box.length) { if ($box.css('display') === 'none') { $box.css('display', 'block'); isBoxHidden = false; } else { $box.css('display', 'none'); isBoxHidden = true; } } }); } catch(e) {} } function getSettingsPanelHTML() { var actualInterval = getActualReportInterval(); return `

📚 学习通挂机助手 (按住拖动)

✕
📊 剩余次数: 加载中...
🎬 视频倍速 ${CONFIG.videoRate.toFixed(2)}x
⚡ 实际上报间隔 ${actualInterval}s
`; } function bindSettingUI(targetDoc) { var $box = $('#ne-21box', targetDoc); var boxEl = $box[0]; if (boxEl) { var savedLeft = GM_getValue('panel_left', null); var savedTop = GM_getValue('panel_top', null); if (savedLeft !== null && savedTop !== null) { var vw = $(targetDoc).width(); var vh = $(targetDoc).height(); var maxLeft = Math.max(0, vw - boxEl.offsetWidth); var maxTop = Math.max(0, vh - boxEl.offsetHeight); savedLeft = Math.max(0, Math.min(maxLeft, savedLeft)); savedTop = Math.max(0, Math.min(maxTop, savedTop)); $box.css({ left: savedLeft + 'px', top: savedTop + 'px', right: 'auto' }); } var $handle = $('#ne-21drag-handle', targetDoc); var dragging = false, startX = 0, startY = 0, origLeft = 0, origTop = 0; $handle.off('mousedown.panelDrag').on('mousedown.panelDrag', function(e) { if ($(e.target).is('span, button, input, a')) return; dragging = true; var rect = boxEl.getBoundingClientRect(); origLeft = rect.left; origTop = rect.top; startX = e.clientX; startY = e.clientY; $box.css({ left: origLeft + 'px', top: origTop + 'px', right: 'auto' }); e.preventDefault(); }); $(targetDoc).off('mousemove.panelDrag mouseup.panelDrag') .on('mousemove.panelDrag', function(e) { if (!dragging) return; var dx = e.clientX - startX; var dy = e.clientY - startY; var newLeft = origLeft + dx; var newTop = origTop + dy; var vw = $(targetDoc).width(); var vh = $(targetDoc).height(); newLeft = Math.max(0, Math.min(vw - boxEl.offsetWidth, newLeft)); newTop = Math.max(0, Math.min(vh - boxEl.offsetHeight, newTop)); $box.css({ left: newLeft + 'px', top: newTop + 'px' }); }) .on('mouseup.panelDrag', function() { if (!dragging) return; dragging = false; var rect = boxEl.getBoundingClientRect(); GM_setValue('panel_left', Math.round(rect.left)); GM_setValue('panel_top', Math.round(rect.top)); }); } $('#ne-21close', targetDoc).off('click').on('click', function() { $('#ne-21box', targetDoc).hide(); isBoxHidden = true; }); var $moreBtn = $('#moreSettingsBtn', targetDoc); var $moreSet = $('#moreSettings', targetDoc); var visible = false; $moreBtn.off('click').on('click', function() { visible = !visible; $moreSet.css('display', visible ? 'block' : 'none'); $moreBtn.text(visible ? '✖️ 收起设置' : '⚙️ 更多设置'); $moreBtn.css('background', visible ? '#eef2f6' : '#f0f4f9'); }); $('input[name="videoMode"]', targetDoc).off('change').on('change', function() { var newMode = this.value; var changed = CONFIG.videoMode !== newMode; updateConfig('videoMode', newMode); SettingsManager.saveVideoMode(CONFIG.videoMode); log('🎬 视频模式切换为: ' + (newMode === 'simulate' ? '模拟上报' : '正常播放'), 'green'); if (changed && isVideoTaskActive && currentVideoName) { log('🔄 检测到正在处理视频,1秒后按新模式重跑当前任务', 'orange'); forceCleanupAll(); isProcessing = false; setTimeout(function() { startMission(); }, 1000); } }); var $rateSlider = $('#videoRateSlider', targetDoc); var $rateValue = $('#rateValue', targetDoc); $rateSlider.off('input change').on('input', function() { $rateValue.text(parseFloat(this.value).toFixed(2)); }) .on('change', function() { var newRate = parseFloat(this.value); if (newRate > 8) newRate = 8; updateConfig('videoRate', newRate); SettingsManager.saveVideoRate(CONFIG.videoRate); if (currentVideoWorker && isVideoTaskActive) currentVideoWorker.postMessage({ type: 'updateRate', rate: CONFIG.videoRate }); var actualInterval = getActualReportInterval(); log('📹 视频倍速: ' + CONFIG.videoRate.toFixed(2) + 'x, 上报间隔: ' + actualInterval + '秒', 'green'); updateProgressBarRateDisplay(); }); $('#showProgressBarCheck', targetDoc).off('change').on('change', function() { var show = this.checked ? 1 : 0; updateConfig('showProgressBar', show); SettingsManager.saveProgressBar(show); if (show === 0) { removeProgressBar(); log('📊 进度条已隐藏', 'blue'); } else if (isVideoTaskActive && currentVideoName) { var dur = currentVideoWorker && currentVideoWorker.videoContext ? currentVideoWorker.videoContext.duration : 0; if (dur > 0) createProgressBar(currentVideoName, dur); log('📊 进度条已显示', 'green'); } }); $('#answerInterval', targetDoc).off('change').on('change', function(e) { var newTime = parseInt(e.target.value); if (newTime >= 1000 && newTime <= 10000) updateConfig('answerInterval', newTime); else e.target.value = CONFIG.answerInterval; }); var checkboxes = ['autoSubmit', 'forceSubmit', 'autoNextExam', 'insertAnswer', 'reviewMode', 'handleVideoQuiz']; checkboxes.forEach(function(id) { var $cb = $('#setting_' + id, targetDoc); if ($cb.length) $cb.off('change').on('change', function() { updateConfig(id, this.checked); if (id === 'handleVideoQuiz') { if (this.checked) { if (CONFIG.videoMode === 'normal') { try { document.querySelectorAll('iframe').forEach(function(f) { var d = f.contentDocument || (f.contentWindow && f.contentWindow.document); if (d && d.body && d.querySelector('video')) { observeVideoQuiz(d); startQuizPoll(d); startQuizObserverGuard(); } }); log('👀 视频弹题监听已开启', 'green'); } catch(e) {} } else { log('👀 弹窗答题已开启(模拟上报模式无弹题,切到正常播放时生效)', 'blue'); } } else { if (currentVideoQuizObserver) { try { currentVideoQuizObserver.disconnect(); } catch(e) {} currentVideoQuizObserver = null; } stopQuizPoll(); stopQuizObserverGuard(); log('👀 视频弹题监听已关闭', 'blue'); } } }); }); $('#goToTkBtn', targetDoc).off('click').on('click', function() { GM_openInTab('https://tk.n1t.cn', { active: true }); }); $('#saveApiKeyBtn', targetDoc).off('click').on('click', function() { var key = $('#apiKeyInput', targetDoc).val(); var $testResult = $('#testResult', targetDoc); if (key && key.trim()) { $testResult.html('⏳ 测试中...').css('color', '#7d9bb5'); testApi(key.trim(), 0).then(function(res) { GM_setValue('api_key', key.trim()); log('✅ API密钥已保存并验证有效', 'green'); if (res.quota_info) { updateQuotaInfo(res.quota_info); $testResult.html('✅ 密钥有效!剩余次数: ' + (res.quota_info.remaining || 0) + ' | 免费: ' + (res.quota_info.free_remaining || 0)).css('color', '#2c7da0'); } else $testResult.html('✅ 密钥有效').css('color', '#2c7da0'); $('#apiKeyInput', targetDoc).val(''); $('#apiKeyInput', targetDoc).attr('placeholder', '已设置'); updateQuotaDisplay(); setTimeout(function() { $testResult.html(''); }, 5000); }).catch(function(err) { $testResult.html('❌ 密钥无效: ' + err).css('color', '#e07c5c'); log('❌ API密钥无效: ' + err, 'red'); setTimeout(function() { $testResult.html(''); }, 5000); }); } else { var existingKey = getApiKey(); if (existingKey) { testApi(existingKey, 0).then(function(res) { if (res.quota_info) updateQuotaInfo(res.quota_info); $testResult.html('✅ 密钥有效').css('color', '#2c7da0'); updateQuotaDisplay(); setTimeout(function() { $testResult.html(''); }, 5000); }).catch(function(err) { GM_setValue('api_key', ''); quotaInfo = { remaining: 0, freeRemaining: 0, rechargeBalance: 0, todayFreeUsage: 0 }; saveQuotaInfo(); updateQuotaDisplay(); $testResult.html('❌ 密钥已失效').css('color', '#e07c5c'); setTimeout(function() { $testResult.html(''); }, 5000); }); } else { $testResult.html('❌ 请输入API密钥').css('color', '#e07c5c'); setTimeout(function() { $testResult.html(''); }, 3000); } } }); } function showBox() { if (!CONFIG.showBox) return; try { var targetDoc = top.document; if (targetDoc.querySelector('#ne-21box')) { flushLogs(); updateQuotaDisplay(); return; } } catch(e) {} setTimeout(function() { try { var targetDoc = top.document; var boxHtml = getSettingsPanelHTML(); $(targetDoc.body).append(boxHtml); bindSettingUI(targetDoc); var actualInterval = getActualReportInterval(); $('#ne-21notice', targetDoc).html('
✅ 脚本已加载(后台版) | 模式: ' + (CONFIG.videoMode === 'simulate' ? '模拟上报' : '正常播放') + ' | 倍速: ' + CONFIG.videoRate.toFixed(2) + 'x | 上报间隔: ' + actualInterval + 's
'); var savedKey = getApiKey(); if (savedKey) { $('#apiKeyInput', targetDoc).attr('placeholder', '已设置'); testApi(savedKey, 0).then(function(res) { if (res.quota_info) updateQuotaInfo(res.quota_info); log('✅ API密钥有效', 'green'); }) .catch(function(err) { GM_setValue('api_key', ''); quotaInfo = { remaining: 0, freeRemaining: 0, rechargeBalance: 0, todayFreeUsage: 0 }; saveQuotaInfo(); updateQuotaDisplay(); log('⚠️ API密钥已失效', 'orange'); }); } else updateQuotaDisplay(); flushLogs(); showPikaqiu(); } catch(e) { console.error('显示悬浮窗失败:', e); } }, 100); } function getTaskParams() { try { var scripts = DOCUMENT.scripts; for (var i = 0; i < scripts.length; i++) { if (scripts[i].innerHTML.indexOf('mArg = "";') != -1 && scripts[i].innerHTML.indexOf('==UserScript==') == -1) { return extractString(scripts[i].innerHTML.replace(/\s/g, ""), 'try{mArg=', ';}catch'); } } return null; } catch(e) { return null; } } function reinitTaskList() { if (isProcessing) { setTimeout(reinitTaskList, 3000); return; } forceCleanupAll(); var params = getTaskParams(); if (!params || params == '$mArg') return; try { var allTasks = $.parseJSON(params).attachments; if (!allTasks || allTasks.length <= 0) return; defaults = $.parseJSON(params).defaults; var pendingTasks = []; for (var i = 0; i < allTasks.length; i++) if (!isTaskCompleted(allTasks[i])) pendingTasks.push(allTasks[i]); pendingMissionCount = pendingTasks.length; if (pendingMissionCount === 0) return; log('📋 重新加载任务列表,发现 ' + pendingMissionCount + ' 个待处理任务', 'green'); taskList = []; domList = []; $('.wrap .ans-cc .ans-attach-ct').each(function(i, t) { if (i < allTasks.length && !isTaskCompleted(allTasks[i])) { taskList.push(allTasks[i]); domList.push($(t).find('iframe')); } }); if (taskList.length > 0 && !isProcessing) { log('🚀 继续处理剩余任务', 'green'); startMission(); } } catch(e) { log('❌ 重新初始化失败: ' + e.message, 'red'); } } $(function() { if (CONFIG.videoRate > 8) CONFIG.videoRate = 8; var savedServerIndex = GM_getValue('current_server_index', 0); if (savedServerIndex >= 0 && savedServerIndex < API_SERVERS.length) currentServerIndex = savedServerIndex; CONFIG.videoMode = GM_getValue('videoMode', 'simulate'); CONFIG.videoRate = GM_getValue('videoRate', 1); if (CONFIG.videoRate > 8) CONFIG.videoRate = 8; CONFIG.showProgressBar = GM_getValue('showProgressBar', 1); UNSAFE_WIN.confirm = function() { return true; }; if (CONFIG.decryptFont) { setTimeout(decryptFont, 2000); setTimeout(function() { if ($('.font-cxsecret').length > 0) decryptFont(); }, 5000); setTimeout(function() { if ($('.font-cxsecret').length > 0) decryptFont(); }, 10000); } if (LOCATION.pathname == '/login' && CONFIG.autoLogin) { showBox(); waitForElement('#phone').then(function() { $('#phone').val(CONFIG.loginPhone); $('#pwd').val(CONFIG.loginPassword); $('#loginBtn').click(); }); } else if (LOCATION.pathname.includes('/mycourse/studentstudy')) { showBox(); var actualInterval = getActualReportInterval(); log('✅ 初始化完毕!视频模式: ' + (CONFIG.videoMode === 'simulate' ? '模拟上报' : '正常播放') + ' | 倍速: ' + CONFIG.videoRate.toFixed(2) + 'x | 上报间隔: ' + actualInterval + '秒', 'green'); } else if (LOCATION.pathname.includes('/knowledge/cards')) { showBox(); var params = getTaskParams(); if (!params || params == '$mArg') { log('⚠️ 无任务点可处理', 'red'); if (!hasTriggeredNoTaskJump) { hasTriggeredNoTaskJump = true; setTimeout(function() { goToNext(); }, 2000); } return; } try { var allTasks = $.parseJSON(params).attachments; if (!allTasks || allTasks.length <= 0) { log('⚠️ 无任务点可处理', 'red'); if (!hasTriggeredNoTaskJump) { hasTriggeredNoTaskJump = true; setTimeout(function() { goToNext(); }, 2000); } return; } } catch(e) { log('⚠️ 解析任务参数失败', 'red'); return; } waitForElement('.wrap .ans-cc .ans-attach-ct').then(function() { if (top.checkJob) top.checkJob = function() { return false; }; var allTasks = $.parseJSON(params).attachments; defaults = $.parseJSON(params).defaults; var pendingTasks = []; for (var i = 0; i < allTasks.length; i++) { var task = allTasks[i]; if (!isTaskCompleted(task)) pendingTasks.push(task); else { var taskName = task.property ? (task.property.name || task.property.title || '任务点' + (i+1)) : '任务点' + (i+1); log('✅ 任务点 ' + taskName + ' 已完成,跳过', 'green'); } } pendingMissionCount = pendingTasks.length; completedMissionCount = 0; if (pendingMissionCount === 0) { log('✅ 所有任务点已完成', 'green'); setTimeout(function() { goToNext(); }, 2000); return; } log('📋 发现 ' + allTasks.length + ' 个任务点,其中 ' + pendingMissionCount + ' 个待处理', 'green'); taskList = []; domList = []; $('.wrap .ans-cc .ans-attach-ct').each(function(i, t) { if (i < allTasks.length && !isTaskCompleted(allTasks[i])) { taskList.push(allTasks[i]); domList.push($(t).find('iframe')); } }); if (taskList.length > 0) { log('🚀 开始处理 ' + taskList.length + ' 个待完成任务', 'green'); startMission(); } else { log('✅ 所有任务点已完成', 'green'); setTimeout(function() { goToNext(); }, 2000); } }).catch(function() { log('❌ 等待元素超时', 'red'); if (!hasTriggeredNoTaskJump) { hasTriggeredNoTaskJump = true; setTimeout(function() { goToNext(); }, 2000); } }); } else if (LOCATION.pathname.includes('/exam/test/reVersionTestStartNew')) { showBox(); waitForElement('.mark_table .whiteDiv').then(function() { processExam(); }); } else if (LOCATION.pathname.includes('/mooc2/work/dowork')) { showBox(); waitForElement('.mark_table form').then(function() { processHomework(); }); } else if (LOCATION.pathname.includes('/work/phone/doHomeWork')) { var oldAlert = UNSAFE_WIN.alert; UNSAFE_WIN.alert = function(msg) { if (msg == '保存成功') return; return oldAlert(msg); }; var oldConfirm = UNSAFE_WIN.confirm; UNSAFE_WIN.confirm = function(msg) { if (msg.includes('确认提交') || msg.includes('未做完')) return true; return oldConfirm(msg); }; } }); var lastChapterUrl = LOCATION.href; setInterval(function() { if (LOCATION.href !== lastChapterUrl) { lastChapterUrl = LOCATION.href; forceCleanupAll(); setTimeout(function() { if (LOCATION.pathname.includes('/knowledge/cards')) reinitTaskList(); }, 3000); } }, 2000); window.addEventListener('beforeunload', function() { forceCleanupAll(); }); $(document).keydown(function(e) { if (e.keyCode == 120) { var $box = $('#ne-21box'); if ($box.length === 0) return; if (isBoxHidden) { $box.show(); isBoxHidden = false; log('📌 面板已显示', 'green'); } else { $box.hide(); isBoxHidden = true; log('📌 面板已隐藏,按F9恢复', 'blue'); } } });