// ==UserScript== // @name 文才学校自动答题与题库记录 // @namespace http://tampermonkey.net/ // @version 1.4 // @description 自动答题并在有参考答案页面记录到题库,支持单选题、多选题和判断题 // @match *://*.wencaischool.net/* // @grant GM_setValue // @grant GM_getValue // @grant GM_listValues // @grant GM_deleteValue // @grant unsafeWindow // ==/UserScript== (function() { 'use strict'; console.log('文才答题助手脚本开始加载...'); // 题库存储键名 const QUESTION_DB_KEY = 'wencai_question_database'; // 初始化题库 let questionDatabase = GM_getValue(QUESTION_DB_KEY, {}); // 配置选项 const config = { autoAnswer: true, // 是否自动答题 recordToDB: true, // 是否记录到题库 showNotifications: true, // 是否显示通知 answerDelay: 500, // 答题延迟(ms) debugMode: true // 调试模式 - 打开以便查看问题 }; // 全局变量,用于管理打开的窗口 let currentViewer = null; let currentESCListener = null; // 添加脚本运行标志到window对象,避免重复执行 if (window.wencaiScriptLoaded) { console.log('脚本已在当前窗口加载,跳过重复执行'); return; } window.wencaiScriptLoaded = true; // 工具函数:提取问题纯文本 function extractQuestionText(element) { // 从题目元素中提取问题文本 // 查找包含问题的div或table let questionText = ''; // 尝试从div中提取 const questionDiv = element.querySelector('div[style*="line-height:20px;font-size:10pt"]'); if (questionDiv) { // 复制div的内容,避免修改原DOM const tempDiv = questionDiv.cloneNode(true); // 移除选项表格 const optionTable = tempDiv.querySelector('table[isitemoption="1"]'); if (optionTable) { optionTable.remove(); } // 移除参考答案div const answerDiv = tempDiv.querySelector('div[style*="color:darkred"]'); if (answerDiv) { answerDiv.remove(); } // 获取文本 questionText = tempDiv.textContent.trim(); // 清理文本 questionText = questionText.replace(/\[参考答案.*?\]/g, ''); questionText = questionText.replace(/分值:\d+/g, ''); questionText = questionText.replace(/\s+/g, ' ').trim(); } else { // 尝试从table中提取 const questionTable = element.querySelector('table[isitem="1"]'); if (questionTable) { const questionTd = questionTable.querySelector('tr:first-child td'); if (questionTd) { questionText = questionTd.textContent.trim(); questionText = questionText.replace(/\[参考答案.*?\]/g, ''); questionText = questionText.replace(/分值:\d+/g, ''); questionText = questionText.replace(/\s+/g, ' ').trim(); } } } return questionText || null; } // 工具函数:提取选项 function extractOptions(element) { const options = []; const optionTable = element.querySelector('table[isitemoption="1"]'); if (!optionTable) return options; // 判断题目类型 const optionType = optionTable.getAttribute('optiontype') || 'radio'; const isCheckbox = optionType === 'checkbox'; const optionRows = optionTable.querySelectorAll('tr'); optionRows.forEach(row => { const input = row.querySelector('input[type="radio"], input[type="checkbox"]'); const label = row.querySelector('label'); if (input && label) { options.push({ value: input.value, text: label.textContent.trim(), type: input.type }); } }); return options; } // 工具函数:提取参考答案 function extractCorrectAnswer(element) { // 从深红色div中提取参考答案 const answerDiv = element.querySelector('div[style*="color:darkred"]'); if (!answerDiv) return null; const text = answerDiv.textContent; const match = text.match(/参考答案:([A-Z]+)/); return match ? match[1] : null; } // 判断题目类型 function getQuestionType(element) { const optionTable = element.querySelector('table[isitemoption="1"]'); if (!optionTable) return 'unknown'; const optionType = optionTable.getAttribute('optiontype'); if (optionType === 'checkbox') { return 'multiple'; } else if (optionType === 'radio') { const options = extractOptions(element); // 判断题通常只有A和B两个选项,且文本为"对"和"错" if (options.length === 2) { const optionTexts = options.map(opt => opt.text.toLowerCase()); if ((optionTexts.includes('对') && optionTexts.includes('错')) || (optionTexts.includes('正确') && optionTexts.includes('错误')) || (optionTexts.includes('是') && optionTexts.includes('否'))) { return 'judgement'; } } return 'single'; } return 'unknown'; } // 生成问题指纹(用于唯一标识问题) function generateQuestionFingerprint(questionText, options, questionType) { // 基于问题文本、选项和题目类型生成哈希 let content = questionText + questionType; options.forEach(opt => { content += opt.value + opt.text.substring(0, 20); }); // 简单哈希函数 let hash = 0; for (let i = 0; i < content.length; i++) { const char = content.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; } return hash.toString(36).substring(1); } // 记录问题到题库 function recordQuestionToDatabase(questionElement) { if (!config.recordToDB) return; const questionText = extractQuestionText(questionElement); if (!questionText) return; const options = extractOptions(questionElement); if (options.length === 0) return; const correctAnswer = extractCorrectAnswer(questionElement); if (!correctAnswer) return; const questionType = getQuestionType(questionElement); // 生成问题指纹 const fingerprint = generateQuestionFingerprint(questionText, options, questionType); // 构建问题对象 const questionObj = { id: fingerprint, text: questionText, options: options, correctAnswer: correctAnswer, questionType: questionType, timestamp: new Date().toISOString(), source: window.location.href }; // 保存到数据库 if (!questionDatabase[fingerprint]) { questionDatabase[fingerprint] = questionObj; GM_setValue(QUESTION_DB_KEY, questionDatabase); // 更新控制面板的题库数量显示 updateQuestionCount(); if (config.showNotifications) { showNotification(`已记录新题目: ${questionText.substring(0, 30)}...`); } if (config.debugMode) { console.log('记录到题库:', questionObj); } } return fingerprint; } // 从题库获取答案 function getAnswerFromDatabase(questionElement) { const questionText = extractQuestionText(questionElement); if (!questionText) return null; const options = extractOptions(questionElement); if (options.length === 0) return null; const questionType = getQuestionType(questionElement); const fingerprint = generateQuestionFingerprint(questionText, options, questionType); if (questionDatabase[fingerprint]) { return questionDatabase[fingerprint].correctAnswer; } return null; } // 自动答题函数 function autoAnswerQuestion(questionElement) { if (!config.autoAnswer) return; let correctAnswer = null; // 首先尝试从页面提取参考答案 correctAnswer = extractCorrectAnswer(questionElement); // 如果没有页面答案,从题库查找 if (!correctAnswer) { correctAnswer = getAnswerFromDatabase(questionElement); } // 找到答案则自动选择 if (correctAnswer) { const questionType = getQuestionType(questionElement); let answered = false; if (questionType === 'multiple') { // 多选题:答案可能是多个字母,如"ABC" for (let i = 0; i < correctAnswer.length; i++) { const answerValue = correctAnswer[i]; const checkboxSelector = `input[type="checkbox"][value="${answerValue}"]`; const checkbox = questionElement.querySelector(checkboxSelector); if (checkbox && !checkbox.checked) { checkbox.click(); checkbox.checked = true; // 触发相关事件 const event = new Event('change', { bubbles: true }); checkbox.dispatchEvent(event); answered = true; if (config.debugMode) { console.log(`已选择答案: ${answerValue}`, checkbox); } } } } else { // 单选题或判断题 const radioSelector = `input[type="radio"][value="${correctAnswer}"]`; const radioButton = questionElement.querySelector(radioSelector); if (radioButton && !radioButton.checked) { radioButton.click(); radioButton.checked = true; // 触发相关事件 const event = new Event('change', { bubbles: true }); radioButton.dispatchEvent(event); answered = true; if (config.debugMode) { console.log(`已选择答案: ${correctAnswer}`, radioButton); } } } return answered; } return false; } // 显示通知 function showNotification(message) { if (!config.showNotifications) return; // 检查是否已存在通知 const existingNotification = document.querySelector('.wencai-notification'); if (existingNotification) { existingNotification.remove(); } // 创建通知元素 const notification = document.createElement('div'); notification.className = 'wencai-notification'; notification.style.cssText = ` position: fixed; top: 20px; right: 20px; background: #4CAF50; color: white; padding: 15px; border-radius: 5px; z-index: 99999; font-family: Arial, sans-serif; font-size: 14px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); animation: fadeInOut 3s ease-in-out; `; // 添加CSS动画 if (!document.querySelector('#wencai-notification-style')) { const style = document.createElement('style'); style.id = 'wencai-notification-style'; style.textContent = ` @keyframes fadeInOut { 0% { opacity: 0; transform: translateY(-20px); } 10% { opacity: 1; transform: translateY(0); } 90% { opacity: 1; transform: translateY(0); } 100% { opacity: 0; transform: translateY(-20px); } } `; document.head.appendChild(style); } notification.textContent = message; document.body.appendChild(notification); // 3秒后移除 setTimeout(() => { if (notification.parentNode) { notification.parentNode.removeChild(notification); } }, 3000); } // 处理所有题目 function processAllQuestions() { // 查找所有题目元素 const questionElements = document.querySelectorAll('td[id^="trScore_"], td:has(table[isitem="1"]), td:has(div[style*="line-height:20px;font-size:10pt"])'); if (config.debugMode) { console.log(`找到 ${questionElements.length} 个题目`); } let answeredCount = 0; let recordedCount = 0; let singleChoiceCount = 0; let multipleChoiceCount = 0; let judgementCount = 0; questionElements.forEach((element, index) => { setTimeout(() => { // 判断题目类型 const questionType = getQuestionType(element); switch(questionType) { case 'single': singleChoiceCount++; break; case 'multiple': multipleChoiceCount++; break; case 'judgement': judgementCount++; break; } // 记录到题库(如果有参考答案) if (element.querySelector('div[style*="color:darkred"]')) { const fingerprint = recordQuestionToDatabase(element); if (fingerprint) recordedCount++; } // 自动答题 if (autoAnswerQuestion(element)) { answeredCount++; } // 显示统计 if (index === questionElements.length - 1 && config.showNotifications) { if (answeredCount > 0 || recordedCount > 0) { let stats = `处理完成: ${answeredCount}题已答, ${recordedCount}题已记录`; if (singleChoiceCount > 0 || multipleChoiceCount > 0 || judgementCount > 0) { stats += ` (单选题:${singleChoiceCount}, 多选题:${multipleChoiceCount}, 判断题:${judgementCount})`; } showNotification(stats); } } }, index * config.answerDelay); }); } // 更新题目计数 function updateQuestionCount() { const viewDbBtn = document.querySelector('#view-db-btn'); if (viewDbBtn) { viewDbBtn.innerHTML = `查看题库(${Object.keys(questionDatabase).length})`; } } // 添加控制面板 function addControlPanel() { console.log('开始添加控制面板...'); // 检查是否已存在控制面板 const existingPanel = document.getElementById('wencai-auto-helper'); if (existingPanel) { console.log('控制面板已存在,移除旧的面板'); existingPanel.remove(); } const panel = document.createElement('div'); panel.id = 'wencai-auto-helper'; panel.style.cssText = ` position: fixed; top: 10px; right: 10px; background: white; border: 2px solid #4CAF50; border-radius: 8px; padding: 10px; z-index: 10000; font-family: Arial, sans-serif; box-shadow: 0 2px 10px rgba(0,0,0,0.2); min-width: 200px; `; panel.innerHTML = `