// ==UserScript== // @name 无用之脚本,没有任何作用 // @version 3.3 // @description 自动填入答案、自动提交、自动翻页,全流程自动化(带悬浮面板+倒计时+日志) // @author Claude Code With DeepSeek-v4 // @match https://welearn.sflep.com/student/* // @grant none // @run-at document-idle // ==/UserScript== (function() { 'use strict'; // ============================================================ // 默认配置 // ============================================================ const DEFAULTS = { pageDelay: 3, submitDelay: 1.5, noAnswerDelay: 1.5, betweenCycleDelay: 2, }; function loadConfig() { try { var saved = localStorage.getItem('autofill_config'); if (saved) return JSON.parse(saved); } catch (e) {} return Object.assign({}, DEFAULTS); } function saveConfig(cfg) { try { localStorage.setItem('autofill_config', JSON.stringify(cfg)); } catch (e) {} } var CONFIG = loadConfig(); // ============================================================ // 日志系统 // ============================================================ var logs = []; var MAX_LOGS = 80; function addLog(msg, type) { type = type || 'info'; var now = new Date(); var time = now.getHours().toString().padStart(2, '0') + ':' + now.getMinutes().toString().padStart(2, '0') + ':' + now.getSeconds().toString().padStart(2, '0'); logs.push({ time: time, msg: msg, type: type }); if (logs.length > MAX_LOGS) logs.shift(); renderLogs(); // 同时输出到控制台 var prefix = '[AutoFill]'; if (type === 'ok') console.log(prefix, '✅', msg); else if (type === 'err') console.error(prefix, '❌', msg); else if (type === 'warn') console.warn(prefix, '⚠️', msg); else console.log(prefix, msg); } function renderLogs() { if (!ui.logContainer) return; var icons = { ok: '✅', err: '❌', warn: '⚠️', info: 'ℹ️', fill: '✍️', nav: '➡️', sub: '📤' }; var html = ''; // 从最新到最旧显示 for (var i = logs.length - 1; i >= 0; i--) { var l = logs[i]; var icon = icons[l.type] || 'ℹ️'; html += '
' + l.time + ' ' + icon + ' ' + escHtml(l.msg) + '
'; } ui.logContainer.innerHTML = html || '
暂无日志
'; // 自动滚动到顶部(最新) ui.logContainer.scrollTop = 0; } function escHtml(s) { return s.replace(/&/g, '&').replace(//g, '>'); } function clearLogs() { logs = []; renderLogs(); } // ============================================================ // 运行时状态 // ============================================================ var STATE = 'idle'; var currentCycle = 0; var stopRequested = false; // ============================================================ // 工具函数 // ============================================================ function sleep(ms) { return new Promise(function(resolve) { setTimeout(resolve, ms); }); } function cleanAnswer(raw) { if (!raw) return ''; return raw.replace(/\s+/g, ' ').trim(); } function getIframeDoc(iframe) { try { return iframe.contentDocument || iframe.contentWindow.document; } catch (e) { return null; } } function waitForIframeLoad(iframe) { return new Promise(function(resolve) { var doc = getIframeDoc(iframe); if (doc && doc.readyState === 'complete') { var body = doc.querySelector('body'); if (body && body.innerHTML.trim().length > 0) { resolve(); return; } } iframe.addEventListener('load', function handler() { iframe.removeEventListener('load', handler); resolve(); }); }); } /** * 判断页面是否有可操作题目(填空/选择/自评) */ function hasAnswers(doc) { if (!doc) return false; return doc.querySelectorAll( 'input[data-solution], textarea[data-solution], [data-controltype="choice"] ul[data-itemtype="options"]' ).length > 0; } function setupAutoConfirm(iframe) { try { var win = iframe.contentWindow; if (win) { win['CONFIRM_AUTO_CALL'] = [ { title: '您确定现在提交吗?', action: true }, { title: '习题答案只能提交一次', action: true }, { title: '确认继续提交?', action: true }, ]; } } catch (e) {} } async function waitWithCountdown(seconds, icon, prefix, badge) { var step = (seconds % 1 === 0) ? 1 : 0.5; for (var remaining = seconds; remaining > 0; remaining -= step) { if (remaining < step) remaining = step; updateUI(icon, prefix + remaining.toFixed(1).replace(/\.0$/, '') + 's', badge); await sleep(step * 1000); if (stopRequested) break; } } // ============================================================ // 填答案函数 // ============================================================ function clickOption(li) { var span = li.querySelector('span'); if (span) { span.click(); } else { li.click(); } li.dispatchEvent(new MouseEvent('mousedown', { bubbles: true })); li.dispatchEvent(new MouseEvent('mouseup', { bubbles: true })); } function fillInputs(doc) { var inputs = doc.querySelectorAll('input[data-solution]'); var count = 0; inputs.forEach(function(inp) { var cleaned = cleanAnswer(inp.getAttribute('data-solution')); if (cleaned) { inp.value = cleaned; inp.dispatchEvent(new Event('input', { bubbles: true })); inp.dispatchEvent(new Event('change', { bubbles: true })); count++; } }); if (count > 0) addLog('填入 ' + count + ' 个 input', 'fill'); return count; } function fillTextareas(doc) { var textareas = doc.querySelectorAll('textarea[data-solution]'); var count = 0; textareas.forEach(function(ta) { var cleaned = cleanAnswer(ta.getAttribute('data-solution')); if (cleaned) { ta.value = cleaned; ta.dispatchEvent(new Event('input', { bubbles: true })); ta.dispatchEvent(new Event('change', { bubbles: true })); count++; } }); if (count > 0) addLog('填入 ' + count + ' 个 textarea', 'fill'); return count; } function fillChoices(doc) { var choices = doc.querySelectorAll('[data-controltype="choice"]'); var correctCount = 0; var selfAssessCount = 0; choices.forEach(function(choice) { var targetLi = choice.querySelector('li[data-solution]'); if (targetLi) { clickOption(targetLi); correctCount++; } else { var firstLi = choice.querySelector('ul[data-itemtype="options"] li:first-child'); if (firstLi) { clickOption(firstLi); selfAssessCount++; } } }); if (correctCount > 0) addLog('选择 ' + correctCount + ' 个正确答案', 'fill'); if (selfAssessCount > 0) addLog('自评选择 ' + selfAssessCount + ' 项(默认第1项)', 'fill'); return correctCount + selfAssessCount; } function fillAllAnswers(doc) { return fillInputs(doc) + fillTextareas(doc) + fillChoices(doc); } function clickSubmit(doc) { var submitBtn = doc.querySelector('[data-controltype="submit"]'); if (submitBtn) { submitBtn.click(); addLog('点击 Submit 按钮', 'sub'); return true; } addLog('未找到 Submit 按钮', 'warn'); return false; } // ============================================================ // 悬浮面板 UI // ============================================================ var ui = {}; function injectUI() { var style = document.createElement('style'); style.textContent = [ '#autofill-float {', ' position: fixed; bottom: 16px; right: 16px; z-index: 2147483647;', ' font-family: "Microsoft YaHei", "PingFang SC", sans-serif; font-size: 13px;', ' background: rgba(30,30,30,0.92); color: #e0e0e0;', ' border-radius: 10px; box-shadow: 0 4px 20px rgba(0,0,0,0.45);', ' width: 260px; max-height: 520px; display: flex; flex-direction: column;', ' user-select: none; transition: background 0.3s;', '}', '#autofill-bar {', ' display: flex; align-items: center; justify-content: space-between;', ' padding: 8px 12px; cursor: pointer; border-radius: 10px; flex-shrink: 0;', '}', '#autofill-bar:hover { background: rgba(255,255,255,0.06); }', '#autofill-icon { font-size: 16px; margin-right: 6px; }', '#autofill-msg { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }', '#autofill-badge {', ' background: #4caf50; color: #fff; font-size: 11px;', ' padding: 1px 7px; border-radius: 10px; margin-left: 8px;', ' display: none;', '}', '#autofill-panel {', ' display: none; padding: 4px 12px 12px 12px; border-top: 1px solid rgba(255,255,255,0.1);', ' overflow-y: auto; flex: 1;', '}', '#autofill-panel hr { border: none; border-top: 1px solid rgba(255,255,255,0.08); margin: 6px 0; }', '#autofill-panel .row {', ' display: flex; justify-content: space-between; align-items: center; padding: 5px 0;', '}', '#autofill-panel .row span.label { flex: 1; }', '#autofill-panel .row input[type=number] {', ' width: 52px; padding: 2px 4px; text-align: center;', ' border: 1px solid rgba(255,255,255,0.2); border-radius: 4px;', ' background: rgba(255,255,255,0.08); color: #81c784; font-size: 13px;', ' margin: 0 4px;', '}', '#autofill-panel .row input[type=number]:focus {', ' outline: none; border-color: #4caf50;', '}', '#autofill-panel .row span.unit { color: #999; font-size: 12px; }', '#autofill-panel button {', ' width: 100%; padding: 7px 0; margin-top: 4px; border: none; border-radius: 6px;', ' font-size: 13px; cursor: pointer; font-weight: bold;', '}', '#autofill-btn-start { background: #4caf50; color: #fff; }', '#autofill-btn-start:hover { background: #43a047; }', '#autofill-btn-stop { background: #f44336; color: #fff; }', '#autofill-btn-stop:hover { background: #e53935; }', '#autofill-btn-clear { background: rgba(255,255,255,0.1); color: #999; margin-top: 2px; font-weight: normal; }', '#autofill-btn-clear:hover { background: rgba(255,255,255,0.2); color: #fff; }', '#autofill-log {', ' max-height: 160px; overflow-y: auto; margin-top: 6px;', ' background: rgba(0,0,0,0.3); border-radius: 6px; padding: 4px 6px;', ' font-size: 11px; line-height: 1.6;', '}', '#autofill-log .logline {', ' padding: 2px 0; border-bottom: 1px solid rgba(255,255,255,0.03); white-space: nowrap;', '}', '#autofill-log .logtime { color: #666; margin-right: 4px; }', '#autofill-log::-webkit-scrollbar { width: 4px; }', '#autofill-log::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.15); border-radius: 2px; }', ].join('\n'); document.head.appendChild(style); var keys = ['pageDelay', 'submitDelay', 'noAnswerDelay', 'betweenCycleDelay']; var labels = ['填答案前等待', '提交后等待', '无答案页等待', '翻页后等待']; var rows = ''; for (var i = 0; i < keys.length; i++) { rows += [ '
', ' ' + labels[i] + '', ' ', ' ', '
', ].join('\n'); } var html = [ '
', '
', ' 💤', ' 就绪', ' ', '
', '
', rows, '
', ' ', ' ', '
', '
', ' 📋 日志', ' ', '
', '
暂无日志
', '
', '
', ].join('\n'); var container = document.createElement('div'); container.innerHTML = html; document.body.appendChild(container.firstElementChild); // 缓存元素 ui.float = document.getElementById('autofill-float'); ui.bar = document.getElementById('autofill-bar'); ui.icon = document.getElementById('autofill-icon'); ui.msg = document.getElementById('autofill-msg'); ui.badge = document.getElementById('autofill-badge'); ui.panel = document.getElementById('autofill-panel'); ui.btnStart = document.getElementById('autofill-btn-start'); ui.btnStop = document.getElementById('autofill-btn-stop'); ui.btnClear = document.getElementById('autofill-btn-clear'); ui.logContainer = document.getElementById('autofill-log'); ui.inputs = {}; keys.forEach(function(k) { ui.inputs[k] = document.getElementById('cfg-' + k); }); // 折叠/展开 ui.bar.addEventListener('click', function() { ui.panel.style.display = ui.panel.style.display === 'none' ? 'block' : 'none'; }); // 输入变更 → 保存 Object.keys(ui.inputs).forEach(function(k) { ui.inputs[k].addEventListener('input', function() { var v = parseFloat(this.value); if (!isNaN(v) && v >= 0) { CONFIG[k] = v; saveConfig(CONFIG); } }); }); ui.btnStart.addEventListener('click', function(e) { e.stopPropagation(); startAutomation(); }); ui.btnStop.addEventListener('click', function(e) { e.stopPropagation(); stopAutomation(); }); ui.btnClear.addEventListener('click', function(e) { e.stopPropagation(); clearLogs(); }); addLog('面板初始化完成', 'ok'); } function updateUI(icon, msg, badge) { if (!ui.icon) return; ui.icon.textContent = icon || '💤'; ui.msg.textContent = msg || '就绪'; if (badge !== undefined && badge !== null && badge !== '') { ui.badge.style.display = 'inline-block'; ui.badge.textContent = badge; } else { ui.badge.style.display = 'none'; } } function setRunningUI(running) { if (!ui.btnStart) return; if (running) { ui.btnStart.style.display = 'none'; ui.btnStop.style.display = 'block'; ui.float.style.background = 'rgba(20,20,20,0.94)'; Object.keys(ui.inputs).forEach(function(k) { ui.inputs[k].disabled = true; }); } else { ui.btnStart.style.display = 'block'; ui.btnStop.style.display = 'none'; ui.float.style.background = 'rgba(30,30,30,0.92)'; Object.keys(ui.inputs).forEach(function(k) { ui.inputs[k].disabled = false; }); } } // ============================================================ // 核心自动循环 // ============================================================ function stopAutomation() { if (STATE === 'idle') return; stopRequested = true; STATE = 'stopping'; updateUI('🛑', '正在停止...'); addLog('用户请求停止', 'warn'); } async function startAutomation() { if (STATE === 'running') return; // 从输入框同步最新配置 Object.keys(ui.inputs).forEach(function(k) { var v = parseFloat(ui.inputs[k].value); if (!isNaN(v) && v >= 0) CONFIG[k] = v; }); saveConfig(CONFIG); STATE = 'running'; stopRequested = false; setRunningUI(true); addLog('===== 自动循环开始 =====', 'ok'); var iframe = document.getElementById('contentFrame'); if (!iframe) { updateUI('❌', '未找到 contentFrame'); addLog('未找到 contentFrame 元素', 'err'); setRunningUI(false); STATE = 'idle'; return; } var reachedEnd = false; var origAlert = window.alert; window.alert = function(msg) { if (typeof msg === 'string' && msg.indexOf('已到达结尾') !== -1) reachedEnd = true; origAlert.apply(window, arguments); }; updateUI('⏳', '等待页面加载...'); addLog('等待初始页面加载', 'info'); await waitForIframeLoad(iframe); if (stopRequested) { cleanup(origAlert); return; } currentCycle = 0; while (!reachedEnd && !stopRequested) { currentCycle++; addLog('--- 第 ' + currentCycle + ' 页 ---', 'nav'); await waitWithCountdown(CONFIG.betweenCycleDelay, '⏳', '翻页后等待: ', '#' + currentCycle); if (stopRequested) break; var iframeDoc = getIframeDoc(iframe); if (!iframeDoc) { updateUI('⚠️', '无法访问 iframe', '#' + currentCycle); addLog('无法访问 iframe document', 'err'); await sleep(2000); continue; } setupAutoConfirm(iframe); if (hasAnswers(iframeDoc)) { addLog('检测到题目,等待 ' + CONFIG.pageDelay + 's', 'info'); await waitWithCountdown(CONFIG.pageDelay, '⏱️', '倒计时: ', '#' + currentCycle); if (stopRequested) break; updateUI('✍️', '正在填入答案...', '#' + currentCycle); var count = fillAllAnswers(iframeDoc); addLog('共填入/选择 ' + count + ' 项', 'ok'); if (count > 0) { updateUI('📤', '正在提交...', '#' + currentCycle); clickSubmit(iframeDoc); await waitWithCountdown(CONFIG.submitDelay, '⏱️', '提交后等待: ', '#' + currentCycle); if (stopRequested) break; } } else { addLog('此页面无题目', 'info'); await waitWithCountdown(CONFIG.noAnswerDelay, '⏭️', '无答案页等待: ', '#' + currentCycle); if (stopRequested) break; } if (reachedEnd) break; updateUI('➡️', '前往下一页...', '#' + currentCycle); if (typeof NextSCO === 'function') { NextSCO(); addLog('调用 NextSCO() 翻页', 'nav'); } else { updateUI('❌', 'NextSCO 不存在'); addLog('NextSCO 函数不存在,停止', 'err'); break; } updateUI('⏳', '等待新页面加载...', '#' + currentCycle); await waitForIframeLoad(iframe); } cleanup(origAlert); } function cleanup(origAlert) { window.alert = origAlert; STATE = 'idle'; setRunningUI(false); if (stopRequested) { updateUI('💤', '已停止', '共' + currentCycle + '页'); addLog('已停止,共处理 ' + currentCycle + ' 页', 'warn'); } else { updateUI('🏁', '课程结尾', '共' + currentCycle + '页'); addLog('到达课程结尾,共处理 ' + currentCycle + ' 页', 'ok'); } stopRequested = false; currentCycle = 0; } // ============================================================ // 启动 // ============================================================ if (document.readyState === 'complete') { injectUI(); } else { window.addEventListener('load', injectUI); } })();