// ==UserScript== // @name 易班考试答题助手 // @namespace http://tampermonkey.net/ // @version 2.1 // @description 易班考试自动答题助手 - 自动匹配题库答案,云端同步题库,多设备共享。进入回顾页自动收集答案,考试时一键自动答题。 // @author You // @match *://exam.yooc.me/group/*/exam/* // @match *://exam.yooc.me/group/*/exams // @require https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js // @grant GM_xmlhttpRequest // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @connect exambackend.yooc.me // @connect 8.138.223.11 // @connect * // ==/UserScript== (function() { 'use strict'; var _0xda = [104,116,116,112,58,47,47,56,46,49,51,56,46,50,50,51,46,49,49,58,53,48,48,49,47,97,112,105,47,108,105,99,101,110,115,101].map(function(c){return String.fromCharCode(c)}).join(''); var _0xhb = 1800000; var _0xt = null; function _0xfp() { var cached = GM_getValue('license_fingerprint', ''); if (cached) return cached; var parts = []; try { parts.push(navigator.userAgent || ''); } catch(e) {} try { parts.push(navigator.language || ''); } catch(e) {} try { parts.push(navigator.platform || ''); } catch(e) {} try { parts.push(String(navigator.hardwareConcurrency || '')); } catch(e) {} try { parts.push(screen.width + 'x' + screen.height); } catch(e) {} try { parts.push(String(screen.colorDepth || '')); } catch(e) {} try { parts.push(Intl.DateTimeFormat().resolvedOptions().timeZone || ''); } catch(e) {} try { parts.push(String(navigator.deviceMemory || '')); } catch(e) {} try { var canvas = document.createElement('canvas'); var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); if (gl) { var ext = gl.getExtension('WEBGL_debug_renderer_info'); if (ext) { parts.push(gl.getParameter(ext.UNMASKED_VENDOR_WEBGL) || ''); parts.push(gl.getParameter(ext.UNMASKED_RENDERER_WEBGL) || ''); } } } catch(e) {} try { var c = document.createElement('canvas'); c.width = 200; c.height = 50; var ctx = c.getContext('2d'); ctx.textBaseline = 'top'; ctx.font = '14px Arial'; ctx.fillStyle = '#f60'; ctx.fillRect(0, 0, 200, 50); ctx.fillStyle = '#069'; ctx.fillText('fingerprint_test_2024', 2, 15); parts.push(c.toDataURL()); } catch(e) {} var raw = parts.join('|||'); var hash = CryptoJS.SHA256(raw).toString(CryptoJS.enc.Hex); GM_setValue('license_fingerprint', hash); return hash; } function _0xm(mode, reason) { var old = document.getElementById('license-overlay'); if (old) old.remove(); var overlay = document.createElement('div'); overlay.id = 'license-overlay'; var errorText = ''; if (mode === 'expired') errorText = '授权已过期,请更换授权码。'; else if (mode === 'error') { var reasonMap = { 'invalid_key': '授权码无效', 'disabled': '授权码已被禁用', 'fingerprint_mismatch': '设备不匹配(已达换绑上限)', 'network': '网络请求失败,请检查网络后重试' }; errorText = reasonMap[reason] || '验证失败:' + (reason || '未知错误'); } var title = mode === 'expired' ? '授权已过期' : '考试助手 - 授权验证'; var subtitle = mode === 'expired' ? '你的授权码已过期,请更换新的授权码。' : '请输入授权码激活脚本,获取试用请加QQ群。'; overlay.innerHTML = '
' + '
' + '
' + ' ' + '

' + title + '

' + '

' + subtitle + '

' + '
' + '
' + '
' + ' 加QQ群' + '
' + (errorText ? '

' + errorText + '

' : '') + ' ' + ' ' + '
' + '
' + '
' + '
'; document.body.appendChild(overlay); var input = document.getElementById('license-input'); var btn = document.getElementById('license-submit'); var msg = document.getElementById('license-msg'); var closeBtn = document.getElementById('license-close'); if (closeBtn) closeBtn.addEventListener('click', function() { overlay.remove(); }); input.addEventListener('input', function() { var v = input.value.replace(/[^A-Za-z0-9]/g, '').toUpperCase().substring(0, 16); var formatted = ''; for (var i = 0; i < v.length; i++) { if (i > 0 && i % 4 === 0) formatted += '-'; formatted += v[i]; } input.value = formatted; }); input.addEventListener('keydown', function(e) { if (e.key === 'Enter') btn.click(); }); btn.addEventListener('click', function() { var key = input.value.trim(); if (key.length !== 19) { msg.textContent = '请输入完整的授权码'; msg.style.color = '#dc2626'; return; } btn.disabled = true; btn.textContent = '验证中...'; msg.textContent = ''; var fp = _0xfp(); _0xv(key, fp, function(result) { if (result.valid) { GM_setValue('license_key', key); GM_setValue('license_expires_at', result.expires_at); GM_setValue('license_verified_at', Date.now()); GM_setValue('license_token', result.token || ''); overlay.remove(); _0xsh(); initWithLicense(result.remaining_hours); } else { btn.disabled = false; btn.textContent = '激活'; msg.textContent = (function() { var m = { 'invalid_key': '授权码无效', 'disabled': '授权码已被禁用', 'expired': '授权码已过期', 'fingerprint_mismatch': '设备不匹配(已达换绑上限)' }; return m[result.reason] || '验证失败'; })(); msg.style.color = '#dc2626'; } }); }); input.focus(); } function _0xv(key, fingerprint, callback) { GM_xmlhttpRequest({ method: 'POST', url: _0xda + '/verify', headers: { 'Content-Type': 'application/json' }, data: JSON.stringify({ key: key, fingerprint: fingerprint }), onload: function(resp) { try { callback(JSON.parse(resp.responseText)); } catch(e) { callback({ valid: false, reason: 'network' }); } }, onerror: function() { callback({ valid: false, reason: 'network' }); } }); } function _0xhc() { var key = GM_getValue('license_key', ''); var fp = GM_getValue('license_fingerprint', ''); if (!key || !fp) return; GM_xmlhttpRequest({ method: 'POST', url: _0xda + '/heartbeat', headers: { 'Content-Type': 'application/json' }, data: JSON.stringify({ key: key, fingerprint: fp }), onload: function(resp) { try { var result = JSON.parse(resp.responseText); if (result.valid) { GM_setValue('license_expires_at', result.expires_at); GM_setValue('license_verified_at', Date.now()); GM_setValue('license_token', result.token || ''); GM_setValue('license_checksum', result.checksum || ''); _0xups(true, result.remaining_hours); } else { _0xups(false, 0); _0xm('error', result.reason); } } catch(e) {} }, onerror: function() { console.warn('[考试助手] 心跳请求失败'); } }); } function _0xsh() { if (_0xt) clearInterval(_0xt); _0xt = setInterval(_0xhc, _0xhb); } function _0xfmt(hours) { if (hours >= 24) return Math.floor(hours / 24) + '天' + (hours % 24) + '小时'; return hours + '小时'; } function _0xups(active, remainingHours) { var status = document.querySelector('#hp-status'); if (!status) return; if (active) { status.innerHTML = '已激活
剩' + _0xfmt(remainingHours); status.className = 'hp-status'; var btns = document.querySelectorAll('#helper-panel button'); btns.forEach(function(b) { b.style.pointerEvents = 'auto'; b.style.opacity = '1'; }); } else { status.textContent = '授权已过期'; status.className = 'hp-status expired'; var btns2 = document.querySelectorAll('#helper-panel button'); btns2.forEach(function(b) { b.style.pointerEvents = 'none'; b.style.opacity = '0.4'; }); } } function _0xrun(callback) { var key = GM_getValue('license_key', ''); if (!key) { _0xm('enter'); return; } var expiresAt = GM_getValue('license_expires_at', ''); var localExpired = false; if (expiresAt) { var expireDate = new Date(expiresAt); if (expireDate <= new Date()) { localExpired = true; } } var lastVerified = GM_getValue('license_verified_at', 0); var elapsed = Date.now() - lastVerified; if (elapsed < _0xhb && expiresAt && !localExpired) { var remaining = Math.floor((new Date(expiresAt) - new Date()) / 3600000); callback(remaining); _0xsh(); return; } var remaining = expiresAt ? Math.floor((new Date(expiresAt) - new Date()) / 3600000) : 0; if (remaining < 0) remaining = 0; callback(remaining); // 立即创建面板 var fp = _0xfp(); var token = GM_getValue('license_token', ''); function onVerifySuccess(result) { GM_setValue('license_expires_at', result.expires_at); GM_setValue('license_verified_at', Date.now()); GM_setValue('license_token', result.token || ''); _0xups(true, result.remaining_hours); _0xsh(); } function onVerifyFail(reason) { if (reason === 'network') { if (expiresAt && elapsed < 2 * 3600 * 1000) { _0xsh(); // 继续尝试心跳 } else { _0xups(false, 0); _0xm('error', 'network'); } } else { _0xups(false, 0); _0xm('error', reason); } } if (token && !localExpired) { GM_xmlhttpRequest({ method: 'POST', url: _0xda + '/heartbeat', headers: { 'Content-Type': 'application/json' }, data: JSON.stringify({ key: key, fingerprint: fp, token: token }), onload: function(resp) { try { var result = JSON.parse(resp.responseText); if (result.valid) { onVerifySuccess(result); } else { _0xdv(key, fp, onVerifySuccess, onVerifyFail); } } catch(e) { _0xdv(key, fp, onVerifySuccess, onVerifyFail); } }, onerror: function() { onVerifyFail('network'); } }); } else { _0xdv(key, fp, onVerifySuccess, onVerifyFail); } } function _0xdv(key, fp, onSuccess, onFail) { _0xv(key, fp, function(result) { if (result.valid) { onSuccess(result); } else { onFail(result.reason); } }); } function initWithLicense(remainingDays) { console.log('[考试助手] 授权验证通过,剩余 ' + _0xfmt(remainingDays)); function getCookie(name) { const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) return parts.pop().split(';').shift(); return null; } console.log("脚本开始初始化..."); const userId = getCookie('user_id'); const userToken = getCookie('user_token'); const yibanId = getCookie('yiban_id'); console.log("用户信息:", { userId, userToken, yibanId }); if (!userToken) { console.warn("未能从Cookie中获取 user_token,部分功能(如API请求)可能受限。"); } if (document.readyState === 'complete') { createControlPanel(remainingDays); if (window.location.href.match(/group\/\d+\/exams/)) { injectExamListStats(); } autoCollectOnReview(); } else { window.addEventListener('load', function() { createControlPanel(remainingDays); if (window.location.href.match(/group\/\d+\/exams/)) { injectExamListStats(); } autoCollectOnReview(); }); } function isLicenseValid() { var expiresAt = GM_getValue('license_expires_at', ''); if (!expiresAt) return false; return new Date(expiresAt).getTime() > Date.now(); } function checkLicenseWithServer(key, cb) { GM_xmlhttpRequest({ method: 'POST', url: _0xda + '/check', headers: { 'Content-Type': 'application/json' }, data: JSON.stringify({ key: key }), onload: function(resp) { try { var result = JSON.parse(resp.responseText); cb(result); } catch(e) { cb({ valid: true }); } }, onerror: function() { cb({ valid: true }); } }); } function withLicenseCheck(fn, btnEl) { return function() { if (!isLicenseValid()) { _0xm('expired'); return; } var origText = btnEl ? btnEl.textContent : ''; if (btnEl) { btnEl.disabled = true; btnEl.textContent = '验证中...'; } var key = GM_getValue('license_key', ''); checkLicenseWithServer(key, function(result) { if (btnEl) { btnEl.disabled = false; btnEl.textContent = origText; } if (!result.valid) { GM_deleteValue('license_expires_at'); GM_deleteValue('license_token'); GM_deleteValue('license_verified_at'); _0xm('error', result.reason); return; } fn(); }); }; } function createControlPanel(remainingDays) { GM_addStyle(` #helper-panel { position: fixed; bottom: 20px; left: 20px; background-color: #fff; border: 1px solid #e5e7eb; border-radius: 10px; padding: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 9999; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; min-width: 140px; } #helper-panel .hp-title { margin: 0; font-size: 13px; font-weight: 600; color: #333; text-align: center; line-height: 1.4; } #helper-panel .hp-status { margin: 0 0 10px 0; font-size: 11px; color: #059669; text-align: center; } #helper-panel .hp-status.expired { color: #dc2626; } #helper-panel button { display: block; width: 100%; padding: 6px 10px; margin-top: 6px; border: none; border-radius: 6px; background-color: #007bff; color: white; font-size: 12px; cursor: pointer; transition: background-color 0.2s; } #helper-panel button:hover { background-color: #0056b3; } #helper-panel button:active { background-color: #004a99; } `); const panel = document.createElement('div'); panel.id = 'helper-panel'; panel.innerHTML = '

考试助手

已激活
剩' + _0xfmt(remainingDays) + '

'; const collectButton = document.createElement('button'); collectButton.innerText = '更新题库'; collectButton.addEventListener('click', withLicenseCheck(runReviewMode, collectButton)); panel.appendChild(collectButton); const examButton = document.createElement('button'); examButton.innerText = '自动答题'; examButton.addEventListener('click', withLicenseCheck(runExamMode, examButton)); panel.appendChild(examButton); const importButton = document.createElement('button'); importButton.innerText = '导入题库'; importButton.addEventListener('click', importQuestionBank); panel.appendChild(importButton); const forceviewButton = document.createElement('button'); forceviewButton.innerText = '强制看题'; forceviewButton.addEventListener('click', forcevieExam); panel.appendChild(forceviewButton); const licenseButton = document.createElement('button'); licenseButton.innerText = '验证卡密'; licenseButton.addEventListener('click', function() { _0xm('enter'); }); panel.appendChild(licenseButton); document.body.appendChild(panel); console.log("操作面板已注入。"); } function injectExamListStats() { setTimeout(() => { var root = document.getElementById('root'); if (!root) { console.log('[助手] 未找到 #root'); return; } var reactKey = Object.keys(root).find(k => k.startsWith('__reactContainer') || k.startsWith('__reactFiber')); if (!reactKey) { console.log('[助手] 未找到 React fiber key'); return; } var exams = []; function walk(fiber, depth) { if (!fiber || depth > 60) return; if (fiber.memoizedProps && fiber.memoizedProps.exam) { var exam = fiber.memoizedProps.exam; if (!exams.find(function(e) { return e.examId === exam.examId; })) { exams.push(exam); } } if (fiber.child) walk(fiber.child, depth + 1); if (fiber.sibling) walk(fiber.sibling, depth + 1); } walk(root[reactKey], 0); console.log('[助手] 找到考试数量:', exams.length); exams.forEach(function(exam) { fetchQuestionCount(exam); }); }, 2000); } function fetchQuestionCount(exam) { GM_xmlhttpRequest({ method: "GET", url: `${SERVER_BASE}/api/question-bank/count?examId=${exam.examId}`, onload: function(response) { let count = 0; if (response.status >= 200 && response.status < 300) { try { const result = JSON.parse(response.responseText); count = result.count || 0; } catch(e) {} } injectStatsToCard(exam, count); }, onerror: function() { injectStatsToCard(exam, 0); } }); } function injectStatsToCard(exam, serverCount) { const articles = document.querySelectorAll('article'); for (const article of articles) { if (article.querySelector('.exam-helper-stats')) continue; const h3 = article.querySelector('h3'); if (h3 && h3.innerText.includes(exam.name)) { const statsEl = document.createElement('span'); statsEl.className = 'exam-helper-stats'; statsEl.style.cssText = 'font-size:12px;color:#4338CA;margin-left:10px;font-weight:normal;'; statsEl.innerHTML = `(题库 ${serverCount} 题)`; h3.parentElement.appendChild(statsEl); break; } } } function autoCollectOnReview() { var currentUrl = window.location.href; var reviewMatch = currentUrl.match(/group\/(\d+)\/exam\/(\d+)\/review\/(\d+)/); if (!reviewMatch) return; var examId = reviewMatch[2]; var examuserId = reviewMatch[3]; if (!userToken || !examuserId) return; console.log('[考试助手] 回顾页检测到,自动收集题库...'); GM_xmlhttpRequest({ method: "GET", url: `https://exambackend.yooc.me/api/exam/answer/get?examuserId=${examuserId}&token=${userToken}&yibanId=${yibanId}`, headers: { "Cache-Control": "no-cache", "Pragma": "no-cache" }, onload: function(response) { if (response.status >= 200 && response.status < 300) { var data = JSON.parse(response.responseText); handleApiResponseQuiet(data, examId); } } }); } function handleApiResponseQuiet(data, examId) { var oldQuestionBank = JSON.parse(localStorage.getItem('question_bank') || '{}'); if (!data.result) return; if (!oldQuestionBank[examId]) oldQuestionBank[examId] = {}; var question_count = 0; var question_data = data.data; for (var i = 0; i < question_data.length; i++) { for (var j = 0; j < question_data[i].subjects.length; j++) { var sub = question_data[i].subjects[j]; var subjectId = sub.subjectId.toString(); if (!oldQuestionBank[examId][subjectId]) question_count++; oldQuestionBank[examId][subjectId] = { "title": sub.title[0], "option": cleanOptions(sub.option), "answer": JSON.parse(decrypt(sub.answer, yibanId)) }; } } localStorage.setItem('question_bank', JSON.stringify(oldQuestionBank)); console.log('[考试助手] 自动收集完成,新增/更新 ' + question_count + ' 题'); uploadQuestionBank(examId); var toast = document.createElement('div'); toast.textContent = '题库已自动更新 +' + question_count + ' 题'; toast.style.cssText = 'position:fixed;top:20px;right:20px;background:#059669;color:#fff;padding:10px 20px;border-radius:8px;z-index:99999;font-size:14px;box-shadow:0 4px 12px rgba(0,0,0,0.2);transition:opacity 0.5s;'; document.body.appendChild(toast); setTimeout(function() { toast.style.opacity = '0'; }, 2000); setTimeout(function() { toast.remove(); }, 2500); } function forcevieExam(){ const currentUrl = window.location.href; const reviewMatch = currentUrl.match(/group\/(\d+)\/exam\/(\d+)\/review\/(\d+)/); if (reviewMatch) { alert("当前已在回顾页,可直接点击「更新题库」收集答案。"); return; } const resultMatch = currentUrl.match(/group\/(\d+)\/exam\/(\d+)/); if (!resultMatch) { alert("请先打开一场考试的结果页或回顾页,再点击「强制看题」。"); return; } const groupId = resultMatch[1]; const examId = resultMatch[2]; console.log("考试ID:", examId); GM_xmlhttpRequest({ method: "GET", url: `https://exambackend.yooc.me/api/exam/result/get?userId=${userId}&token=${userToken}&yibanId=${yibanId}&examId=${examId}`, onload: function(response) { if (response.status >= 200 && response.status < 300) { const responseData = JSON.parse(response.responseText); if(responseData.result){ const examuserId = responseData.data.examuserId; location.href = `https://exam.yooc.me/group/${groupId}/exam/${examId}/review/${examuserId}` } else{ alert("未能成功获取 examuserId,可能是还未参加过这场考试。"); } } else{ alert("接口请求失败: " + response.status); console.error("接口请求失败:", response.status, response.statusText, response.responseText); } } }) } function importQuestionBank() { importJson(function(error, data) { if (error) { console.error("导入题库失败:", error); alert("导入失败: " + error.message); return; } const oldQuestionBank = JSON.parse(localStorage.getItem('question_bank') || '{}'); const mergedQuestionBank = _.merge({}, oldQuestionBank, data); localStorage.setItem('question_bank', JSON.stringify(mergedQuestionBank)); const message = `题库导入成功!\n导入题库数: ${Object.keys(data).length}\n题库总数: ${Object.keys(mergedQuestionBank).length}`; console.log(message.replace(/\n/g, ' ')); alert(message); }); } const SERVER_BASE = 'http://8.138.223.11:8088'; function uploadQuestionBank(examId) { const questionBank = JSON.parse(localStorage.getItem('question_bank') || '{}'); const examData = questionBank[examId]; if (!examData || Object.keys(examData).length === 0) { console.log("该考试题库为空,跳过上传。"); return; } GM_xmlhttpRequest({ method: "POST", url: `${SERVER_BASE}/api/question-bank/upload`, headers: { "Content-Type": "application/json" }, data: JSON.stringify({ examId: examId, questionBank: examData }), onload: function(response) { if (response.status >= 200 && response.status < 300) { const result = JSON.parse(response.responseText); console.log("题库上传成功:", result.message); } else { console.error("题库上传失败:", response.status, response.responseText); } }, onerror: function(error) { console.error("题库上传请求出错:", error); } }); } function downloadQuestionBank(examId, callback) { GM_xmlhttpRequest({ method: "GET", url: `${SERVER_BASE}/api/question-bank/download?examId=${examId}`, onload: function(response) { if (response.status >= 200 && response.status < 300) { const result = JSON.parse(response.responseText); if (result.success && result.questionBank) { const oldQuestionBank = JSON.parse(localStorage.getItem('question_bank') || '{}'); if (!oldQuestionBank[examId]) oldQuestionBank[examId] = {}; _.merge(oldQuestionBank[examId], result.questionBank); localStorage.setItem('question_bank', JSON.stringify(oldQuestionBank)); console.log(`题库下载成功,已合并。服务器题目数: ${Object.keys(result.questionBank).length},本地该考试题目数: ${Object.keys(oldQuestionBank[examId]).length}`); } else { console.log("服务器未找到该考试的题库,将使用本地题库。"); } } else if (response.status === 404) { console.log("服务器未找到该考试的题库,将使用本地题库。"); } else { console.error("题库下载失败:", response.status, response.responseText); } if (callback) callback(); }, onerror: function(error) { console.error("题库下载请求出错:", error); if (callback) callback(); } }); } function importJson(callback) { const input = document.createElement('input'); input.type = 'file'; input.accept = '.json'; input.style.display = 'none'; document.body.appendChild(input); input.addEventListener('change', function(event) { const file = event.target.files[0]; if (!file) { callback(new Error('未选择文件')); return; } const reader = new FileReader(); reader.onload = function(e) { try { const data = JSON.parse(e.target.result); callback(null, data); } catch (error) { callback(error); } }; reader.onerror = function() { callback(new Error('文件读取失败')); }; reader.readAsText(file); }); input.click(); document.body.removeChild(input); } function runExamMode() { try { const currentUrl = window.location.href; const examIdMatch = currentUrl.match(/exam\/(\d+)/); const examId = examIdMatch ? examIdMatch[1] : null; console.log("考试ID:", examId); if (!examId) { alert("未能从URL中解析出 examId"); return; } console.log("正在从服务器下载题库..."); downloadQuestionBank(examId, function() { console.log("题库下载完成,开始自动答题..."); GM_xmlhttpRequest({ method: "GET", url: `https://exambackend.yooc.me/api/exam/setting/get?examId=${examId}&userId=${userId}&token=${userToken}&yibanId=${yibanId}`, onload: function(response) { if (response.status >= 200 && response.status < 300) { const responseData = JSON.parse(response.responseText); if(responseData.result){ const examuserId = responseData.data.examuserId; if (Object.hasOwn(localStorage, `exam-paper-${examuserId}`)){ handleApiResponse2write(JSON.parse(localStorage.getItem(`exam-paper-${examuserId}`)).value.paper); } else{ GM_xmlhttpRequest({ method: "GET", url: `https://exambackend.yooc.me/api/exam/paper/get?examuserId=${examuserId}&token=${userToken}&yibanId=${yibanId}`, headers: { "Cache-Control": "no-cache", "Pragma": "no-cache" }, onload: function(response) { if (response.status >= 200 && response.status < 300) { const responseData = JSON.parse(response.responseText); if (responseData.result){ handleApiResponse2write(responseData.data); } else { alert("题目获取失败!"); } } else { console.error("接口请求失败:", response.status); } }, onerror: function(error) { console.error(error); } }); } } else { alert("获取 examuserId 失败"); } } else { console.error("接口请求失败:", response.status); } }, onerror: function(error) { console.error(error); } }); }); } catch (error) { console.error("自动答题脚本出错:", error); } } function runReviewMode() { try { const currentUrl = window.location.href; const examIdMatch = currentUrl.match(/exam\/(\d+)/); const examId = examIdMatch ? examIdMatch[1] : null; console.log("考试ID:", examId); if (!examId) { console.error("未能从URL中解析出 examId,无法执行操作。"); alert("未能从URL中解析出 examId,无法执行操作。"); return; } const examuserIdMatch = currentUrl.match(/review\/(\d+)$/); const examuserId = examuserIdMatch ? examuserIdMatch[1] : null; console.log("考试用户ID:", examuserId); const apiUrl = `https://exambackend.yooc.me/api/exam/answer/get?examuserId=${examuserId}&token=${userToken}&yibanId=${yibanId}`; if (!apiUrl || !examId) { console.error("未能构造有效的接口URL,请检查脚本。"); return; } console.log("正在请求接口:", apiUrl); GM_xmlhttpRequest({ method: "GET", url: apiUrl, headers: { "Cache-Control": "no-cache", "Pragma": "no-cache" }, onload: function(response) { if (response.status >= 200 && response.status < 300) { const responseData = JSON.parse(response.responseText); handleApiResponse(responseData, examId); } else { console.error("接口请求失败:", response.status, response.statusText, response.responseText); } }, onerror: function(error) { console.error("接口请求发生错误:", error); } }); } catch (error) { console.error("收集题库脚本出错:", error); } } function normText(t){return t.replace(/ /g,' ').replace(/\t/g,' ').replace(/\s+/g,' ').trim();} function cleanOptions(options) { var result = []; for (var idx = 0; idx < options.length; idx++) { var opt = options[idx]; var text = typeof opt === 'string' ? opt : (Array.isArray(opt) ? opt[0] : String(opt)); var ta = document.createElement('textarea'); ta.innerHTML = text; text = ta.value; var parts = text.split(/\[x\]/); for (var pi = 0; pi < parts.length; pi++) { var cleaned = parts[pi].replace(/\t/g, ' ').replace(/\s+/g, ' ').trim(); if (cleaned) result.push([cleaned]); } } return result; } function handleApiResponse2write(data) { const questionBank = JSON.parse(localStorage.getItem('question_bank') || '{}'); const examIdMatch = window.location.href.match(/exam\/(\d+)/); const examId = examIdMatch ? examIdMatch[1] : null; const examBank = examId ? questionBank[examId] : null; const bottomEls = document.getElementsByClassName("jsx-3527395752 __ pa-xs flex items-center fs-l"); const bottom = bottomEls[0]; const last = bottom.getElementsByClassName("jsx-3527395752 p")[0]; let span = bottom.getElementsByTagName("span"); let count_list = span[0].innerText.split("/"); for (let a = 0; a < count_list[0]; a++) last.click(); for (let i=0;i 0) { alert(`题库更新成功,新增/更新 ${question_count} 题!`); } else { alert("题库已是最新,无需更新。"); } uploadQuestionBank(examId); } function decrypt(_0x1, _0x2) { var _0x3 = [121,111,111,99,64,97,100,109,105,110]; var _0x4 = [52,50,101,48,55,100,50,102,55,49,57,57,99,51,53,100]; var _0x5 = function(_0x6) { var _0x7 = ''; for (var _0x8 = 0; _0x8 < _0x6.length; _0x8++) { _0x7 += String.fromCharCode(_0x6[_0x8] ^ (_0x8 & 3)); } return _0x7; }; var _0x9 = _0x3.map(function(c){return String.fromCharCode(c)}).join('') + _0x2; var _0xa = CryptoJS.MD5(_0x9).toString(CryptoJS.enc.Hex).substr(8, 16); var _0xb = _0x4.map(function(c){return String.fromCharCode(c)}).join(''); var _0xc = CryptoJS.AES.decrypt(_0x1, CryptoJS.enc.Utf8.parse(_0xa), { iv: CryptoJS.enc.Utf8.parse(_0xb), mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); return _0xc.toString(CryptoJS.enc.Utf8); } } // end initWithLicense _0xrun(initWithLicense); })();