// ==UserScript== // @name 学习通学习助手 不知名 免费题库 // @version 3.2.0 // @author BZM // @license Apache // @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/5717 // @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 // @resource Table https://pkpkq.ucuc.net/font-decrypt/Table.json // @require https://pkpkq.ucuc.net/font-decrypt/TyprMd5.js // @connect tk.swk.tw // @connect pkpkq.n1t.cn // @connect soti.ucuc.net // @connect n1t.cn // @tag 免费搜题 // @tag 可选付费 // @antifeature payment 脚本存在第三方答题接口付费功能 // ==/UserScript== /********************************* 配置管理器 ****************************************/ var SettingsManager = { // 默认配置 _defaults: { showBox: 1, // 显示脚本浮窗 showMaskImg: 1, // 显示皮卡丘 taskOnly: 0, // 只处理任务点任务 handleVideo: 1, // 处理视频 handleAudio: 1, // 处理音频 videoRate: 1, // 视频/音频倍速 reviewMode: 0, // 复习模式 handleQuiz: 1, // 测验自动处理 answerInterval: 5000, // 答题时间间隔(ms) autoSubmit: 0, // 测验自动提交 forceSubmit: 0, // 测验强制提交 decryptFont: 1, // 字体解密 autoNextExam: 1, // 考试自动跳转 examNextDelay: 1, // 考试自动跳转随机间隔 insertAnswer: 1, // 答案插入题目 autoLogin: 0, // 自动登录 loginPhone: '', // 登录手机号 loginPassword: '' // 登录密码 }, // 获取配置 get: function(key) { 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); this._defaults[key] = value; return value; }, // 加载所有配置 load: function() { for (var key in this._defaults) { if (localStorage.getItem('setting_' + key) !== null) { var stored = localStorage.getItem('setting_' + key); if (stored === 'true') this._defaults[key] = true; else if (stored === 'false') this._defaults[key] = false; else if (!isNaN(parseFloat(stored)) && isFinite(stored)) this._defaults[key] = parseFloat(stored); else this._defaults[key] = stored; } } if (this._defaults.videoRate > 8) this._defaults.videoRate = 8; return this._defaults; } }; // 全局配置引用 var CONFIG = SettingsManager.load(); // 限制倍速最高8倍 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'); } /********************************* 题型映射扩展 ****************************************/ 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; } /********************************* 全局变量 ****************************************/ var UNSAFE_WIN = unsafeWindow; var LOCATION = location; var DOCUMENT = UNSAFE_WIN.document; var $ = UNSAFE_WIN.jQuery || top.jQuery; var UE = UNSAFE_WIN.UE; // API配置 - 支持多个备用接口 var API_SERVERS = [ { url: "https://soti.ucuc.net/api/search.php", name: "主接口" }, { url: "https://n1t.cn/api/search.php", name: "备用接口1" }, { url: "https://tk.swk.tw/api/search.php", name: "备用接口2" } ]; var currentServerIndex = 0; function getCurrentApiUrl() { return API_SERVERS[currentServerIndex].url; } 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(); var taskList = null; var defaults = null; var domList = null; var submitBtn = null; var saveBtn = null; var frameContent = null; var okBtn = null; var reportUrlChange = 0; var isProcessing = false; var isBoxHidden = false; var pikaqiuAdded = false; var pendingMissionCount = 0; var completedMissionCount = 0; var isJumping = false; var currentProcessingTask = null; var totalMissionCount = 0; var currentVideoInterval = null; var hasTriggeredNoTaskJump = false; var logQueue = []; var logTimer = null; // ==================== 选项清理函数 ==================== 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 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 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 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 = '
';
$(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) {}
}
$(document).keydown(function(e) {
if (e.keyCode == 120) { // F9
var $box = $('#ne-21box');
if ($box.length === 0) return;
if (isBoxHidden) {
$box.css('display', 'block');
$box.css('opacity', '1');
isBoxHidden = false;
log('📌 面板已显示', 'green');
} else {
$box.css('display', 'none');
isBoxHidden = true;
log('📌 面板已隐藏,按F9键恢复显示', 'blue');
}
}
});
function testApi(key, serverIndex) {
return new Promise(function(resolve, reject) {
var testQuestion = "test";
var testType = "单选题";
var testOptions = ["选项A", "选项B"];
var apiUrl = API_SERVERS[serverIndex || 0].url;
var formData = 'question=' + encodeURIComponent(testQuestion) + '&key=' + encodeURIComponent(key) + '&type=' + encodeURIComponent(testType) + '&options=' + encodeURIComponent(JSON.stringify(testOptions));
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 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;
}
// ==================== 检查API次数是否足够用于提交 ====================
function hasEnoughQuotaForSubmit() {
quotaInfo = getQuotaInfo();
var hasKey = hasApiKey();
var hasQuota = (quotaInfo.remaining > 0 || quotaInfo.freeRemaining > 0);
return hasKey && hasQuota;
}
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) {
log('❌ 无法自动提交:API次数已用完,请充值后重新提交', 'red');
return { action: 'save', reason: '次数不足,改为保存' };
} else if (CONFIG.forceSubmit && !hasQuota) {
log('❌ 无法强制提交:API次数已用完,请充值后重新提交', 'red');
return { action: 'save', reason: '次数不足,改为保存' };
} else {
return { action: 'save', reason: '自动保存' };
}
}
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));
}
if (index === startServerIndex) {
log('🔍 请求答案 - 题型:' + typeText + ' 题目:' + questionText.substring(0, 40) + '...', 'blue');
}
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) {
var answerData = null;
if (Array.isArray(res.data) && res.data.length > 0) {
if (res.data[0].answer) {
answerData = res.data[0].answer;
} else if (res.data[0].question && res.data[0].question !== "抱歉无答案") {
answerData = res.data[0].question;
}
} else if (typeof res.data === 'string') {
answerData = res.data;
} else if (res.data && typeof res.data === 'object' && res.data.answer) {
answerData = res.data.answer;
}
if (answerData && answerData !== "抱歉无答案" && !answerData.includes("无答案")) {
if (res.quota_info) updateQuotaInfo(res.quota_info);
currentServerIndex = index;
GM_setValue('current_server_index', index);
log('✅ 获取答案成功: ' + answerData, 'purple');
resolve(answerData);
return;
}
}
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 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 name = $(this).attr('name');
var id = $(this).attr('id');
if (name) {
uniqueIdentifiers.add(name);
} else if (id) {
uniqueIdentifiers.add(id);
} else {
var parentHtml = $(this).parent().html() || '';
uniqueIdentifiers.add($(this).index() + '_' + parentHtml.substring(0, 50));
}
});
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() {
var id = $(this).attr('id');
var name = $(this).attr('name');
if (id) {
uniquePCInputs.add(id);
} else if (name) {
uniquePCInputs.add(name);
} else {
uniquePCInputs.add($(this).index());
}
});
count = uniquePCInputs.size;
if (count > 0) return count;
}
return count === 0 ? 1 : count;
}
function fillBlankAnswer($question, answers, isPhoneMode, contextWindow) {
var answerList = splitAnswer(answers);
log('📝 填空题答案: ' + answerList.join(' | '), 'blue');
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 name = $(this).attr('name');
var id = $(this).attr('id');
var key = name || id || $(this).index();
if (!seen.has(key)) {
seen.add(key);
uniqueInputs.push(this);
}
});
log('📝 找到 ' + uniqueInputs.length + ' 个填空输入框,准备填写', 'blue');
$(uniqueInputs).each(function(i) {
setTimeout(function() {
var answerValue = answerList[i] !== undefined ? answerList[i] : (answerList[0] || '');
$(this).val(answerValue);
$(this).trigger('input').trigger('change');
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);
}
});
log('📝 找到 ' + uniqueEditors.length + ' 个编辑器,准备填写', 'blue');
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) {
var instantKey = 'ueditorInstant' + editorIndex;
ueditor = contextWindow.UE.instants[instantKey];
}
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) {}
}, i * 300);
});
return true;
}
}
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 id = $(this).attr('id');
var name = $(this).attr('name');
var key = id || name || $(this).index();
if (!seenPC.has(key)) {
seenPC.add(key);
uniquePCInputs.push(this);
}
});
log('📝 找到 ' + uniquePCInputs.length + ' 个填空输入框 (PC),准备填写', 'blue');
$(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'))) {
UE.getEditor($this.attr('id')).setContent(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) {
var instantKey = 'ueditorInstant' + editorIndex;
ueditor = contextWindow.UE.instants[instantKey];
}
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;
}
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 parseUrlParams() {
var query = window.location.search.substring(1);
var vars = query.split("&");
var params = {};
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split("=");
params[pair[0]] = pair[1];
}
return params;
}
function getElement(parent, selector, timeout) {
timeout = timeout || 10000;
return new Promise(function(resolve) {
var result = parent.querySelector(selector);
if (result) return resolve(result);
var timer;
var observer = new MutationObserver(function(mutations) {
for (var i = 0; i < mutations.length; i++) {
var mutation = mutations[i];
for (var j = 0; j < mutation.addedNodes.length; j++) {
var node = mutation.addedNodes[j];
if (node instanceof Element) {
result = node.matches(selector) ? node : node.querySelector(selector);
if (result) {
observer.disconnect();
timer && clearTimeout(timer);
return resolve(result);
}
}
}
}
});
observer.observe(parent, { childList: true, subtree: true });
timer = setTimeout(function() {
observer.disconnect();
resolve(null);
}, timeout);
});
}
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) {
var params = extractString(scripts[i].innerHTML.replace(/\s/g, ""), 'try{mArg=', ';}catch');
return params;
}
}
return null;
} catch(e) {
return 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;
}
function goToNext() {
if (isJumping) return;
isJumping = true;
log('🔄 检查是否有下一章节...', 'blue');
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();
log('📖 已点击跳转按钮', 'green');
isJumping = false;
}, 3000);
return true;
} else {
log('📚 课程已全部完成,无更多章节可跳转', 'green');
}
} catch(e) {
log('❌ 跳转失败: ' + e.message, 'red');
}
isJumping = false;
return false;
}
function completeCurrentTask() {
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 (currentVideoInterval) {
clearInterval(currentVideoInterval);
currentVideoInterval = null;
}
if (taskList && taskList.length > 0) {
log('📋 还有 ' + taskList.length + ' 个任务点待处理,继续...', 'green');
setTimeout(function() {
startMission();
}, 2000);
} else {
log('✅ 此页面所有任务处理完毕', 'green');
goToNext();
}
}
function switchMission() {
if (taskList.length <= 0) {
log('✅ 此页面任务处理完毕', 'green');
isProcessing = false;
goToNext();
return;
}
isProcessing = false;
setTimeout(startMission, 3000);
}
function startMission() {
if (isProcessing) return;
if (taskList.length <= 0) {
log('✅ 此页面任务处理完毕', 'green');
isProcessing = false;
goToNext();
return;
}
isProcessing = true;
var taskType = taskList[0]['type'];
var dom = domList[0];
var task = taskList[0];
currentProcessingTask = task;
if (taskType == undefined) {
taskType = taskList[0]['property']["module"];
}
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 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;
}
var target = dom.length > 0 ? dom[0] : null;
if (!target) {
log('⚠️ 未找到视频iframe,3秒后重试', 'orange');
isProcessing = false;
setTimeout(function() {
processVideo(dom, obj);
}, 3000);
return;
}
log('🎬 处理视频:' + name, 'purple');
var executed = false;
var doc = target.contentDocument || target.contentWindow.document;
if (currentVideoInterval) {
clearInterval(currentVideoInterval);
currentVideoInterval = null;
}
currentVideoInterval = setInterval(function() {
var media = doc.querySelector('video') || doc.querySelector('audio');
if (media && !executed) {
executed = true;
log('✅ ' + name + ' 开始播放', 'green');
media.pause();
media.muted = true;
media.playbackRate = CONFIG.videoRate > 1 ? Math.min(CONFIG.videoRate, 16) : 1;
media.play();
var resumePlay = function() {
if (media.paused && !media.ended) {
media.play();
log('🔄 ' + name + ' 恢复播放', 'blue');
}
};
media.addEventListener('pause', resumePlay);
var onVideoEnd = function() {
log('✅ ' + name + ' 播放完成', 'green');
media.removeEventListener('pause', resumePlay);
media.removeEventListener('ended', onVideoEnd);
if (currentVideoInterval) {
clearInterval(currentVideoInterval);
currentVideoInterval = null;
}
completeCurrentTask();
};
media.addEventListener('ended', onVideoEnd);
if (media.ended) {
onVideoEnd();
}
} else if (media && executed) {
if (media.ended) {
clearInterval(currentVideoInterval);
currentVideoInterval = null;
completeCurrentTask();
}
}
}, 1500);
}
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 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() {
submitBtn.click();
setTimeout(function() {
okBtn.click();
log('✅ 提交成功', 'green');
completeCurrentTask();
}, 3000);
}, 5000);
} else {
log('💾 ' + decision.reason + ',自动保存', 'green');
setTimeout(function() {
saveBtn.click();
setTimeout(function() {
log('✅ 保存成功', 'green');
completeCurrentTask();
}, 3000);
}, 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);
}, CONFIG.answerInterval);
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()];
log('📝 填空题传入空数量: ' + blankCount, 'blue');
} 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);
}, CONFIG.answerInterval);
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 normalizedAnswer.indexOf(t) !== -1 || t.indexOf(normalizedAnswer) !== -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 ansArr = splitAnswer(answer); $opts.each(function(i, t) { var normalizedOpt = normalizeForCompare(optsText[i]); for (var j = 0; j < ansArr.length; j++) { var normalizedAns = normalizeForCompare(ansArr[j]); if (normalizedOpt === normalizedAns || normalizedOpt.indexOf(normalizedAns) !== -1 || normalizedAns.indexOf(normalizedOpt) !== -1) { setTimeout(function() { $(t).click(); }, 300); break; } } }); } 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 (trueKeywords.indexOf(answer) != -1 || trueKeywords.toLowerCase().indexOf(answer.toLowerCase()) != -1) { $pd.each(function(i, t) { if ($(t).attr('val-param') == 'true') { $(t).click(); log('✅ 判断题自动选择: 正确 (接口返回: ' + answer + ')', 'green'); } }); } else { $pd.each(function(i, t) { if ($(t).attr('val-param') == 'false') { $(t).click(); log('✅ 判断题自动选择: 错误 (接口返回: ' + answer + ')', 'green'); } }); } } else if (questionType == 4) { fillShortAnswer($question, answer, true, contextWindow); } log('✅ 第' + (index + 1) + '题自动答题成功', 'green'); setTimeout(function() { processPhoneQuestions(index + 1, questionList, taskObj); }, CONFIG.answerInterval); }).catch(function(err) { log('⚠️ 第' + (index + 1) + '题获取答案失败: ' + err, 'orange'); setTimeout(function() { processPhoneQuestions(index + 1, questionList, taskObj); }, CONFIG.answerInterval); }); } 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(index, doms, 0, questionList, taskObj); } // ========== PC端答题函数 ========== function processPCQuestions(index, doms, qIndex, questionList, taskObj) { if (qIndex == questionList.length) { var decision = getSubmitDecision(); if (decision.action === 'submit') { log('✅ 测验处理完成,准备' + decision.reason + '。', 'green'); setTimeout(function() { submitBtn.click(); setTimeout(function() { frameContent.find('#confirmSubWin > div > div > a.bluebtn').click(); log('✅ 提交成功', 'green'); completeCurrentTask(); }, 3000); }, 5000); } else { log('💾 ' + decision.reason + ',自动保存', 'green'); setTimeout(function() { saveBtn.click(); setTimeout(function() { log('✅ 保存成功', 'green'); completeCurrentTask(); }, 3000); }, 5000); } return; } var $question = $(questionList[qIndex]); 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 != '暂无答案') { 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 normalizedAnswer.indexOf(t) !== -1 || t.indexOf(normalizedAnswer) !== -1; }); } if (idx != -1) { $($opts[idx]).parent().click(); log('✅ PC单选题已选: ' + optsText[idx], 'green'); } else { log('⚠️ 未找到匹配选项: ' + answer, 'orange'); } } else if (questionType == 1) { var ansArr = splitAnswer(answer); $opts.each(function(i, t) { var normalizedOpt = normalizeForCompare(optsText[i]); for (var j = 0; j < ansArr.length; j++) { var normalizedAns = normalizeForCompare(ansArr[j]); if (normalizedOpt === normalizedAns || normalizedOpt.indexOf(normalizedAns) !== -1 || normalizedAns.indexOf(normalizedOpt) !== -1) { setTimeout(function() { $($opts[i]).parent().click(); }, 300); break; } } }); } 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 (trueKeywords.indexOf(answer) != -1 || trueKeywords.toLowerCase().indexOf(answer.toLowerCase()) != -1) { var clicked = false; $pdContainer.each(function(i, t) { var $opt = $(t).find('a, .answer_p'); if ($opt.length === 0) $opt = $(t); var valAttr = $(t).find('span').attr('val-param') || $(t).attr('val-param'); if (valAttr == 'true') { $(t).click(); clicked = true; log('✅ PC判断题自动选择: 正确 (接口返回: ' + answer + ')', 'green'); } }); if (!clicked && $pdContainer.length > 0) { $($pdContainer[0]).click(); log('✅ PC判断题自动选择: 正确(默认第一个) (接口返回: ' + answer + ')', '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判断题自动选择: 错误 (接口返回: ' + answer + ')', 'green'); } }); if (!clicked && $pdContainer.length > 1) { $($pdContainer[1]).click(); log('✅ PC判断题自动选择: 错误(默认第二个) (接口返回: ' + answer + ')', 'green'); } else if (!clicked && $pdContainer.length === 1) { $($pdContainer[0]).click(); log('✅ PC判断题自动选择: 错误(唯一选项) (接口返回: ' + answer + ')', 'green'); } } } else if (questionType == 4) { fillShortAnswer($question, answer, false, null); } log('✅ PC第' + (qIndex + 1) + '题自动答题成功', 'green'); } else { log('⚠️ PC第' + (qIndex + 1) + '题无法获取答案', 'orange'); } setTimeout(function() { processPCQuestions(index, doms, qIndex + 1, questionList, taskObj); }, CONFIG.answerInterval); }).catch(function() { setTimeout(function() { processPCQuestions(index, doms, qIndex + 1, questionList, taskObj); }, CONFIG.answerInterval); }); } 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); }, CONFIG.answerInterval); 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 != '暂无答案') { 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 normalizedAnswer.indexOf(t) !== -1 || t.indexOf(normalizedAnswer) !== -1; }); } if (idx != -1) { $($opts[idx]).parent().click(); log('✅ 作业单选题已选: ' + optsText[idx], 'green'); } else { log('⚠️ 未找到匹配选项: ' + answer, 'orange'); } } else if (questionType == 1) { var ansArr = splitAnswer(answer); $opts.each(function(i, t) { var normalizedOpt = normalizeForCompare(optsText[i]); for (var j = 0; j < ansArr.length; j++) { var normalizedAns = normalizeForCompare(ansArr[j]); if (normalizedOpt === normalizedAns || normalizedOpt.indexOf(normalizedAns) !== -1 || normalizedAns.indexOf(normalizedOpt) !== -1) { setTimeout(function() { $($opts[i]).parent().click(); }, 300); break; } } }); } 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 (trueKeywords.indexOf(answer) != -1 || trueKeywords.toLowerCase().indexOf(answer.toLowerCase()) != -1) { if ($pdContainer.length > 0) { $($pdContainer[0]).click(); log('✅ 作业判断题自动选择: 正确 (接口返回: ' + answer + ')', 'green'); } } else { if ($pdContainer.length > 1) { $($pdContainer[1]).click(); log('✅ 作业判断题自动选择: 错误 (接口返回: ' + answer + ')', 'green'); } else if ($pdContainer.length === 1) { $($pdContainer[0]).click(); log('✅ 作业判断题自动选择: 错误(唯一选项) (接口返回: ' + answer + ')', 'green'); } } } else if (questionType == 4) { fillShortAnswer($question, answer, false, null); } log('✅ 作业第' + (index + 1) + '题自动答题成功', 'green'); } else { log('⚠️ 作业第' + (index + 1) + '题无法获取答案', 'orange'); } setTimeout(function() { doHomework(index + 1, questionList); }, CONFIG.answerInterval); }).catch(function() { setTimeout(function() { doHomework(index + 1, questionList); }, CONFIG.answerInterval); }); } 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 != '暂无答案') { 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 normalizedAnswer.indexOf(t) !== -1 || t.indexOf(normalizedAnswer) !== -1; }); } if (idx != -1) { $($opts[idx]).parent().click(); log('✅ 考试单选题已选: ' + optsText[idx], 'green'); } else { log('⚠️ 未找到匹配选项: ' + answer, 'orange'); } } else if (questionType == 1) { var ansArr = splitAnswer(answer); $opts.each(function(i, t) { var normalizedOpt = normalizeForCompare(optsText[i]); for (var j = 0; j < ansArr.length; j++) { var normalizedAns = normalizeForCompare(ansArr[j]); if (normalizedOpt === normalizedAns || normalizedOpt.indexOf(normalizedAns) !== -1 || normalizedAns.indexOf(normalizedOpt) !== -1) { setTimeout(function() { $($opts[i]).parent().click(); }, 300); break; } } }); } 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 (trueKeywords.indexOf(answer) != -1 || trueKeywords.toLowerCase().indexOf(answer.toLowerCase()) != -1) { if ($pdContainer.length > 0) { $($pdContainer[0]).click(); log('✅ 考试判断题自动选择: 正确 (接口返回: ' + answer + ')', 'green'); } } else { if ($pdContainer.length > 1) { $($pdContainer[1]).click(); log('✅ 考试判断题自动选择: 错误 (接口返回: ' + answer + ')', 'green'); } else if ($pdContainer.length === 1) { $($pdContainer[0]).click(); log('✅ 考试判断题自动选择: 错误(唯一选项) (接口返回: ' + answer + ')', 'green'); } } } else if (questionType == 4) { fillShortAnswer($answerDom, answer, false, null); } log('✅ 考试自动答题成功', 'green'); } else { log('⚠️ 考试无法获取答案', 'orange'); } setTimeout(function() { goToNextExam(); }, CONFIG.answerInterval); }).catch(function() { setTimeout(function() { goToNextExam(); }, CONFIG.answerInterval); }); } 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 decryptFont() { var TyprInstance = null; var md5Instance = null; 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; } if (typeof unsafeWindow !== 'undefined' && unsafeWindow.md5) { md5Instance = unsafeWindow.md5; } else if (typeof window !== 'undefined' && window.md5) { md5Instance = window.md5; } else if (typeof md5 !== 'undefined') { md5Instance = md5; } if (!TyprInstance || !md5Instance || !TyprInstance.U) { setTimeout(function() { decryptFont(); }, 1000); return; } var $tip = $('style:contains(font-cxsecret)'); if (!$tip.length) return; try { 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; } // 【关键修复】只处理页面中实际出现的加密字符,避免遍历2万多个字符 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++) { existingChars.add(html.charCodeAt(j)); } } }); if (existingChars.size === 0) return; var match = {}; var charCodes = Array.from(existingChars); log('🔓 发现 ' + charCodes.length + ' 个加密字符,开始解密...', 'blue'); 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 = md5Instance(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'); } } 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 getSettingsPanelHTML() { return '