在您安裝前,Greasy Fork希望您了解本腳本包含“負面功能”,可能幫助腳本的作者獲利,而不能給你帶來任何收益。
此腳本會在您造訪的網站插入廣告。
智慧树章节测试自动答题
// ==UserScript== // @name 晚风知到答题小助手 // @namespace 晚风 // @version 1.0 // @description 智慧树章节测试自动答题 // @author 晚风 // @match *://*.zhihuishu.com/stuExamWeb* // @connect fengtingwanli.cn // @run-at document-start // @grant unsafeWindow // @grant GM_xmlhttpRequest // @grant GM_addStyle // @grant GM_getResourceText // @grant GM_setClipboard // @grant GM_setValue // @grant GM_getValue // @license MIT // @antifeature ads // @resource css https://unpkg.com/element-ui/lib/theme-chalk/index.css // ==/UserScript== const logMessages = []; let isCollapsed = false; let windowState = { width: 500, height: 450, left: 20, top: 20, collapsed: false }; function loadWindowState() { try { const savedState = GM_getValue('windowState', null); if (savedState) { windowState = JSON.parse(savedState); isCollapsed = windowState.collapsed; } } catch (e) { logMessage('加载窗口状态失败: ' + e.message, 'warning'); } } function saveWindowState() { try { const floatingWindow = document.getElementById('floating-window'); if (floatingWindow) { windowState.left = parseInt(floatingWindow.style.left); windowState.top = parseInt(floatingWindow.style.top); windowState.collapsed = isCollapsed; GM_setValue('windowState', JSON.stringify(windowState)); } } catch (e) { logMessage('保存窗口状态失败: ' + e.message, 'warning'); } } function logMessage(message, type = 'info') { const timestamp = new Date().toLocaleTimeString(); logMessages.push({ timestamp, message, type }); if (logMessages.length > 30) logMessages.shift(); updateLogDisplay(); } function showTip(message, type = 'info', duration = 2000) { const tipEl = document.createElement('div'); tipEl.textContent = message; tipEl.style.position = 'relative'; tipEl.style.display = 'inline-block'; tipEl.style.marginTop = '8px'; tipEl.style.padding = '6px 12px'; tipEl.style.borderRadius = '4px'; tipEl.style.color = 'white'; tipEl.style.fontSize = '13px'; tipEl.style.fontWeight = '500'; tipEl.style.zIndex = '1000'; tipEl.style.opacity = '0'; tipEl.style.transition = 'opacity 0.3s'; switch(type) { case 'success': tipEl.style.backgroundColor = '#67c23a'; break; case 'warning': tipEl.style.backgroundColor = '#e6a23c'; break; case 'error': tipEl.style.backgroundColor = '#f56c6c'; break; default: tipEl.style.backgroundColor = '#909399'; } const apiContainer = document.getElementById('save-api-key-btn').parentElement; apiContainer.appendChild(tipEl); requestAnimationFrame(() => tipEl.style.opacity = '1'); setTimeout(() => { tipEl.style.opacity = '0'; setTimeout(() => tipEl.remove(), 300); }, duration); } function makeElementResizableDraggable(el) { loadWindowState(); el.style.position = 'absolute'; el.style.left = `${windowState.left}px`; el.style.top = `${windowState.top}px`; if (isCollapsed) { const floatingWindow = document.getElementById('floating-window'); const collapseBtnIcon = document.querySelector('#collapse-btn i'); if (!floatingWindow.dataset.originalHeight) { floatingWindow.dataset.originalHeight = `${floatingWindow.offsetHeight}px`; } floatingWindow.style.height = '40px'; floatingWindow.style.overflow = 'hidden'; if (collapseBtnIcon) collapseBtnIcon.className = 'el-icon-plus'; } const header = el.querySelector('.el-dialog__header'); header.style.cursor = 'move'; header.onmousedown = function (event) { if (event.target.id === 'collapse-btn' || event.target.parentElement.id === 'collapse-btn') { return; } let shiftX = event.clientX - el.getBoundingClientRect().left; let shiftY = event.clientY - el.getBoundingClientRect().top; function moveAt(pageX, pageY) { el.style.left = pageX - shiftX + 'px'; el.style.top = pageY - shiftY + 'px'; } function onMouseMove(event) { moveAt(event.pageX, event.pageY); } document.addEventListener('mousemove', onMouseMove); el.onmouseup = function () { document.removeEventListener('mousemove', onMouseMove); el.onmouseup = null; saveWindowState(); }; }; el.ondragstart = function () { return false; }; } function toggleCollapse() { isCollapsed = !isCollapsed; const collapseBtn = document.querySelector('#collapse-btn i'); const floatingWindow = document.getElementById('floating-window'); if (isCollapsed) { if (!floatingWindow.dataset.originalHeight) { floatingWindow.dataset.originalHeight = `${floatingWindow.offsetHeight}px`; } floatingWindow.style.height = '40px'; floatingWindow.style.overflow = 'hidden'; if (collapseBtn) collapseBtn.className = 'el-icon-plus'; } else { floatingWindow.style.height = floatingWindow.dataset.originalHeight || ''; floatingWindow.style.overflow = ''; if (collapseBtn) collapseBtn.className = 'el-icon-minus'; } saveWindowState(); } //============================================= enableWebpackHook(); const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); const config = { awaitTime: 5000, stopTimer: false, questionCount: 0, finishCount: 0, questionType: { '判断题': 10, '单选题': 20, '多选题': 25, '填空题': 30, '问答题': 40, }, apiKey: GM_getValue('apiKey', '') }; function updateLogDisplay() { const logContainer = document.getElementById('log-container'); if (!logContainer) return; logContainer.innerHTML = ''; logMessages.forEach(entry => { const logItem = document.createElement('div'); logItem.className = `log-item log-${entry.type}`; logItem.innerHTML = ` <span class="log-timestamp">${entry.timestamp}</span> <span class="log-content">${entry.message}</span> `; logContainer.appendChild(logItem); }); logContainer.scrollTop = logContainer.scrollHeight; } function answerQuestion(questionBody, questionIndex) { // 获取题目 const questionTitle = questionBody.querySelector('.subject_describe div,.smallStem_describe p') .__Ivue__._data.shadowDom.textContent .replace(/%C2%A0/g, '%20') .replace(/\s+/g, ' '); console.log(`第 ${questionIndex} 题:`, questionTitle); // 获取选项 const options = questionBody.querySelectorAll(".node_detail"); if (options.length) { options.forEach((option, idx) => { console.log(`选项 ${idx + 1}:`, option.innerText.trim()); }); } else { console.log('未检测到选项'); } appendToTable(questionTitle, "", questionIndex); logMessage(`正在处理第 ${questionIndex} 题: ${questionTitle.substring(0, 30)}...`); const questionType = questionBody.querySelector(".subject_type").innerText.match(/【(.+)】|$/)[1]; let type = config.questionType[questionType]; type = type !== undefined ? type : -1; const apiurl = `https://www.fengtingwanli.cn/answer.php?question=${encodeURIComponent(questionTitle)}${config.apiKey ? `&key=${config.apiKey}` : ''}`; console.log(apiurl) GM_xmlhttpRequest({ method: "GET", url: apiurl, onload: xhr => { try { const res = JSON.parse(xhr.responseText); const msg = res.msg; const answerString = res.answer; const questionTypeText = questionBody.querySelector(".subject_type").innerText.match(/【(.+)】|$/)[1]; let type = config.questionType[questionTypeText]; type = type !== undefined ? type : -1; if (msg === "暂无答案") { updateAnswerInTable(questionIndex, "暂无答案", false); logMessage(`第 ${questionIndex} 题: 暂无答案`, 'warning'); } else if (msg === "无效的API,请检查后重试") { updateAnswerInTable(questionIndex, "API Key无效,请检查后重试", false); logMessage(`第 ${questionIndex} 题: API Key无效`, 'error'); } else { updateAnswerInTable(questionIndex, answerString, true); logMessage(`第 ${questionIndex} 题: 已获取答案`, 'success'); // **这里添加调用选择答案** const selected = chooseAnswer(type, questionBody, answerString); if (!selected) { logMessage(`第 ${questionIndex} 题: 未匹配到网页选项,需要手动选择`, 'warning'); } } // 自动切换下一题 document.querySelectorAll('.switch-btn-box > button')[1]?.click(); } catch (error) { logMessage(`第 ${questionIndex} 题: 解析答案出错 - ${error.message}`, 'error'); } } }); } function chooseAnswer(questionType, questionBody, answerString) { let isSelect = false; const answers = answerString.split(/[\u0001,#=;=-|;、,]+/).map(a => a.trim()).filter(Boolean); if (!questionBody) return isSelect; switch (questionType) { case 10: // 判断题 return handleJudgment(questionBody, answers); case 20: // 单选题 return handleSingleChoice(questionBody, answers); case 25: // 多选题 return handleMultipleChoice(questionBody, answers); case 30: // 填空题 return handleFillInBlank(questionBody, answers); case 40: // 问答题 return handleEssay(questionBody, answerString); default: return isSelect; } } // 判断题 function handleJudgment(questionBody, answers) { const firstOption = questionBody.querySelector(".nodeLab"); const secondOption = questionBody.querySelectorAll(".nodeLab")[1]; if (!firstOption || !secondOption) return false; const optionText = questionBody.querySelector(".node_detail")?.innerText || ""; const givenAnswer = answers[0]?.toLowerCase(); const isCorrectAnswer = /正确|是|对|√|t|true/i.test(givenAnswer); const isOptionCorrect = /正确|是|对|√|t|true/i.test(optionText); if (isCorrectAnswer === isOptionCorrect) { firstOption.click(); } else if (!isOptionCorrect && !isCorrectAnswer) { firstOption.click(); } else if (isCorrectAnswer !== isOptionCorrect) { secondOption.click(); } else { return false; } return true; } function handleSingleChoice(questionBody, answers) { const options = questionBody.querySelectorAll(".node_detail"); if (!options.length) return false; const targetAnswer = answers[0]; if (!targetAnswer) return false; for (let i = 0; i < options.length; i++) { const optionText = options[i].innerText.trim(); if (optionText === targetAnswer){ clickOption(i, questionBody); return true; } } return false; } function handleMultipleChoice(questionBody, answers) { const options = questionBody.querySelectorAll(".node_detail"); if (!options.length || !answers.length) return false; let matched = false; const selectedOptions = new Set(); answers.forEach(answer => { for (let i = 0; i < options.length; i++) { if (selectedOptions.has(i)) continue; const optionText = options[i].innerText.trim(); if (optionText === answer) { clickOption(i, questionBody); selectedOptions.add(i); matched = true; break; } } }); return matched; } function handleFillInBlank(questionBody, answers) { const blanks = questionBody.querySelectorAll(".blankInput"); if (!blanks.length) return false; for (let i = 0; i < blanks.length; i++) { const blank = blanks[i]; if (i < answers.length) { blank.value = answers[i]; } else { blank.value = answers[0] || ""; } } return blanks.length > 0; } function handleEssay(questionBody, answerString) { const answerArea = questionBody.querySelector("textarea"); if (answerArea) { answerArea.value = answerString; return true; } return false; } function clickOption(index, questionBody) { const optionLabel = questionBody.querySelectorAll(".nodeLab")[index]; if (optionLabel) optionLabel.click(); } function appendToTable(questionTitle, answerString, questionIndex) { const table = document.querySelector("#record-table tbody"); table.innerHTML += `<tr><td>${questionIndex}</td><td>${questionTitle}</td><td id="answer${questionIndex}">正在搜索...</td></tr>`; } function changeAnswerInTable(answerString, questionIndex, isSelect) { const answerCell = document.querySelector(`#answer${questionIndex}`); answerCell.innerHTML = answerString; if (answerString === "暂无答案") { answerCell.insertAdjacentHTML('beforeend', `<p style="color:red"><i class="el-icon-error"></i> 暂无答案</p>`); } if (!isSelect) { answerCell.insertAdjacentHTML('beforeend', `<p style="color:red"><i class="el-icon-warning"></i> 未匹配答案,请根据搜索结果手动选择答案</p>`); } } function enableWebpackHook() { const originCall = Function.prototype.call; Function.prototype.call = function (...args) { const result = originCall.apply(this, args); if (args[2]?.default?.version === '2.5.2') { args[2]?.default?.mixin({ mounted: function () { this.$el['__Ivue__'] = this; } }); } return result; } } function updateAnswerInTable(questionIndex, msg, isSelect = true) { const answerCell = document.querySelector(`#answer${questionIndex}`); if (!answerCell) return; answerCell.innerHTML = ''; const mainMsg = document.createElement('div'); mainMsg.textContent = msg; answerCell.appendChild(mainMsg); if (!isSelect) { const warn = document.createElement('p'); warn.style.color = 'red'; warn.innerHTML = '<i class="el-icon-warning"></i> 未匹配答案,请根据搜索结果手动选择'; answerCell.appendChild(warn); logMessage(`第 ${questionIndex} 题: 未匹配答案,请根据搜索结果手动选择`, 'warning'); } const tbody = answerCell.closest('tbody'); if (tbody) tbody.scrollTop = tbody.scrollHeight; } function saveApiKey() { const apiKeyInput = document.getElementById('api-key-input'); const newApiKey = apiKeyInput.value.trim(); if (newApiKey) { GM_setValue('apiKey', newApiKey); config.apiKey = newApiKey; logMessage('API Key 保存成功', 'success'); showTip('API Key 保存成功', 'success'); } else { logMessage('API Key 不能为空', 'warning'); showTip('API Key 不能为空', 'warning'); } } function clearApiKey() { GM_setValue('apiKey', ''); config.apiKey = ''; document.getElementById('api-key-input').value = ''; showTip('API Key 已清除', 'info'); } unsafeWindow.onload = (() => (async () => { const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = 'https://unpkg.com/element-ui/lib/theme-chalk/index.css'; document.head.appendChild(link); // ================== CSS ================== GM_addStyle(` #collapse-btn {cursor: pointer;} .el-dialog { width: 500px; height: 400px; border-radius: 10px; box-shadow: 0 8px 25px rgba(0,0,0,0.15); } .el-dialog__header { background: linear-gradient(90deg, #409EFF, #66b1ff)!important; color: white; display: flex; justify-content: space-between; align-items: center; font-size: 16px; cursor: move; } .nav-bar { display: flex; background-color: #ea96aa; border-bottom: 1px solid #dcdfe6; } .nav-bar button { flex: 1; border: none; background: transparent; padding: 10px 0; font-size: 14px; cursor: pointer; transition: background 0.2s, color 0.2s; } .nav-bar button:hover { background-color: #e6f0ff; } .nav-bar button.active { background-color: #409EFF; color: white; border-radius: 4px 4px 0 0; } .page { display: none; height: calc(100% - 100px); overflow-y: auto; box-sizing: border-box; } .page.active { display: block; } #record-table { width: 100%; table-layout: auto; border-collapse: collapse; } #record-table th, #record-table td { padding: 10px; text-align: center; vertical-align: middle; border: 1px solid #ebeef5; white-space: normal; } #record-table th:nth-child(1), #record-table td:nth-child(1) { width: 10%; } #record-table th:nth-child(2), #record-table td:nth-child(2) { width: 60%; } #record-table th:nth-child(3), #record-table td:nth-child(3) { width: 30%; } .el-card { box-shadow: 0 4px 12px rgba(0,0,0,0.05); margin: 0; background-color: #d4dbca!important; } .el-card__header { background-color: #f5f7fa; font-weight: 500; display: flex; align-items: center; } .el-card__header i { margin: 0; color: #409EFF; } .el-card__body { padding: 0; box-sizing: border-box; } .el-table { border-radius: 6px; width: 100%; border-collapse: collapse; table-layout: auto; /* 内容撑开列宽 */ } .el-table th, .el-table td { padding: 10px 12px; font-size: 14px; border-bottom: 1px solid #ebeef5; text-align: center; vertical-align: middle; white-space: normal; } .el-table th { background-color: #f5f7fa; font-weight: 600; } #log-container { font-size: 13px; overflow-y: auto; background: #f9fafc; padding: 0; line-height: 1.5; } .log-item { margin-bottom: 5px; } .log-info { color: #909399; } .log-success { color: #67c23a; } .log-warning { color: #e6a23c; } .log-error { color: #f56c6c; } /* 输入框和按钮 */ .el-input__inner { height: 36px; line-height: 36px; border-radius: 6px; box-sizing: border-box; } .el-button--medium { padding: 6px 12px; font-size: 13px; border-radius: 4px; } .el-button--primary { background-color: #409EFF; border-color: #409EFF; } .el-button--primary:hover { background-color: #66b1ff; border-color: #66b1ff; } .el-button--danger { background-color: #f56c6c; border-color: #f56c6c; } .el-button--danger:hover { background-color: #f78989; } /* 二维码卡片居中显示 */ .qr-code-container { display: flex; flex-direction: column; align-items: center; text-align: center; } .qr-code-container img { width: auto; max-width: 200px; height: auto; margin: 0 auto; border-radius: 6px; border: 1px solid #ebeef5; } .qr-code-container p { font-size: 12px; color: #606266; margin-top: 8px; } `); const dialogStyle = ` <div id="floating-window" class="el-dialog"> <div class="el-dialog__header"> <span class="el-dialog__title"> <i class="el-icon-lightning" style="color: #ffd700;"></i> 智慧树小助手 </span> <span id="collapse-btn"><i class="el-icon-minus"></i></span> </div> <div class="nav-bar"> <button class="nav-btn active" data-page="log-page">首页</button> <button class="nav-btn" data-page="qa-page">答题</button> <button class="nav-btn" data-page="api-page">API</button> <button class="nav-btn" data-page="qr-page">联系我</button> </div> <div class="page active" id="log-page"> <div class="el-card"> <div class="el-card__header"><i class="el-icon-document"></i> 操作日志</div> <div class="el-card__body"><div id="log-container"></div></div> </div> </div> <div class="page" id="qa-page"> <div class="el-card"> <div class="el-card__header"><i class="el-icon-document"></i> 答题记录</div> <div class="el-card__body"> <div class="el-table__body-wrapper"> <table id="record-table" class="el-table__body"> <thead> <tr class="el-table__header"> <th><div class="cell">序号</div></th> <th><div class="cell">题目</div></th> <th><div class="cell">答案</div></th> </tr> </thead> <tbody></tbody> </table> </div> </div> </div> </div> <div class="page" id="api-page"> <div class="el-card"> <div class="el-card__header"><i class="el-icon-key"></i> API设置</div> <div class="el-card__body"> <div class="el-input el-input--medium"> <input id="api-key-input" type="text" placeholder="请输入API Key" class="el-input__inner" value="${config.apiKey}"> </div> <div style="margin-top: 8px;"> <button id="save-api-key-btn" class="el-button el-button--primary el-button--medium"><i class="el-icon-check"></i> 保存</button> <button id="clear-api-key-btn" class="el-button el-button--danger el-button--medium" style="margin-left:10px"><i class="el-icon-delete"></i> 清除</button> </div> </div> </div> </div> <div class="page" id="qr-page"> <div class="el-card qr-code-container"> <div class="el-card__header"><i class="el-icon-mobile-phone"></i> 扫码联系我更新题库</div> <div class="el-card__body"> <img src="https://www.fengtingwanli.cn/wp-content/uploads/2024/10/1730359368-4bbfc02656cc0da1a6b2d0481a89ff7.jpg" alt="联系我"> <p>联系我更新题库</p> </div> </div> </div> </div> `; document.body.insertAdjacentHTML('beforeend', dialogStyle); makeElementResizableDraggable(document.getElementById('floating-window')); document.querySelectorAll('.nav-btn').forEach(btn => { btn.addEventListener('click', () => { document.querySelectorAll('.nav-btn').forEach(b => b.classList.remove('active')); document.querySelectorAll('.page').forEach(p => p.classList.remove('active')); btn.classList.add('active'); document.getElementById(btn.dataset.page).classList.add('active'); }); }); document.getElementById('collapse-btn').addEventListener('click', toggleCollapse); document.getElementById('save-api-key-btn').addEventListener('click', saveApiKey); document.getElementById('clear-api-key-btn').addEventListener('click', clearApiKey); logMessage('智慧树小助手已启动', 'info'); logMessage('填写API后即可开始答题', 'info'); await sleep(config.awaitTime); const questionBodyAll = document.querySelectorAll(".examPaper_subject.mt20"); if (questionBodyAll.length === 0) { logMessage('未检测到题目', 'warning'); return; } config.questionCount = questionBodyAll.length; logMessage(`共检测到 ${config.questionCount} 道题目`, 'info'); answerQuestion(questionBodyAll[0], 1); let finishCount = 1; const interval = setInterval(() => { if (finishCount < questionBodyAll.length) { answerQuestion(questionBodyAll[finishCount], finishCount + 1); finishCount += 1; } else { clearInterval(interval); logMessage("所有题目已处理完成!", 'success'); } }, 3000); }))();