// ==UserScript== // @name WELearn - Auto Answer // @version 91.78 // @description 点击试题页面即答题 // @match https://welearn.sflep.com/* // @match https://centercourseware.sflep.com/* // @author Rito // ==/UserScript== (function () { 'use strict'; const SCRIPT_STATE_KEY = 'weleran_auto_answer_enabled'; // --- UI创建函数 (仅在顶层窗口运行) --- function createToggleUI() { const isEnabled = () => localStorage.getItem(SCRIPT_STATE_KEY) !== 'false'; const button = document.createElement('div'); button.style.position = 'fixed'; button.style.bottom = '20px'; button.style.right = '20px'; button.style.width = '60px'; button.style.height = '60px'; button.style.borderRadius = '50%'; button.style.color = 'white'; button.style.display = 'flex'; button.style.justifyContent = 'center'; button.style.alignItems = 'center'; button.style.cursor = 'pointer'; button.style.zIndex = '9999'; button.style.fontSize = '14px'; button.style.boxShadow = '0 2px 10px rgba(0,0,0,0.2)'; button.title = '点击切换自动答题'; const updateButtonState = () => { if (isEnabled()) { // button.style.backgroundColor = '#4CAF50'; // Green button.textContent = '答题:开'; } else { // button.style.backgroundColor = '#f44336'; // Red button.textContent = '答题:关'; } }; // --- 彩色循环样式 --- let hue = 0; setInterval(() => { hue = (hue + 1) % 360; if (!isEnabled()) { button.style.backgroundColor = `hsl(${hue}, 80%, 60%)`; } else { button.style.backgroundColor = `hsl(${hue}, 80%, 30%)`; // Darker when on } }, 20); // --- 拖动功能 --- let isDragging = false; let hasDragged = false; let offsetX, offsetY; button.addEventListener('mousedown', (e) => { isDragging = true; hasDragged = false; const rect = button.getBoundingClientRect(); offsetX = e.clientX - rect.left; offsetY = e.clientY - rect.top; button.style.cursor = 'grabbing'; e.preventDefault(); }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; hasDragged = true; let newX = e.clientX - offsetX; let newY = e.clientY - offsetY; // 限制在视窗内 newX = Math.max(0, Math.min(newX, window.innerWidth - button.offsetWidth)); newY = Math.max(0, Math.min(newY, window.innerHeight - button.offsetHeight)); button.style.left = `${newX}px`; button.style.top = `${newY}px`; button.style.bottom = 'auto'; button.style.right = 'auto'; }); document.addEventListener('mouseup', () => { if (isDragging) { isDragging = false; button.style.cursor = 'pointer'; } }); button.addEventListener('click', (e) => { // 如果发生了拖动,则阻止点击事件 if (hasDragged) { e.stopPropagation(); return; } const current = isEnabled(); localStorage.setItem(SCRIPT_STATE_KEY, !current); updateButtonState(); // 刷新iframe以立即应用更改 const iframe = document.querySelector('iframe'); if (iframe) { iframe.contentWindow.location.reload(); } }); updateButtonState(); document.body.appendChild(button); } // --- 核心逻辑 (仅在iframe内运行) --- function runAutoAnswer() { // 检查开关状态 if (localStorage.getItem(SCRIPT_STATE_KEY) === 'false') { console.log('[WELearn Auto Answer] 功能已关闭,跳过执行。'); return; } console.log('[WELearn Auto Answer] Running in iframe:', location.href); // 0) 先展开,避免未渲染导致找不到 document.querySelectorAll('.white_frame').forEach(frame => { const toggleBtn = frame.querySelector('[data-itemtype="toggle"], .toggle, .expand'); if (toggleBtn) toggleBtn.click(); frame.style.display = 'block'; frame.style.visibility = 'visible'; }); // 已填标记,避免重复填同一控件 const filledInputs = new Set(); const filledChoices = new Set(); // 1) 按页面顺序遍历每一个 data-solution(每个小问一个) const nodes = document.querySelectorAll('[data-solution]'); nodes.forEach((node, idx) => { // 提取答案文本(优先属性) const ans = node.getAttribute('data-solution')?.trim() || node.textContent.trim(); if (!ans) { console.log(`第${idx + 1}小问:答案为空,跳过`); return; } // 2) 选择题:data-solution 在
  • 上,直接标记该
  • if (node.tagName.toLowerCase() === 'li') { if (!filledChoices.has(node)) { node.setAttribute('data-choiced', ''); // 可选:触发点击以激活平台事件 try { node.click(); } catch { } filledChoices.add(node); } console.log(`第${idx + 1}小问(选择题)已选中:${node.textContent.trim()}`); return; } // 3) 填空题:data-solution 不在
  • 上,需要只填入当前小问的控件 // 3.1 找到当前小问的最近容器(避免作用到整题) const subContainer = node.closest('[data-itemtype="blank"], [data-itemtype="options"], [data-controltype]') || node.parentElement; // 3.2 在该小问容器内寻找尚未填过的输入控件(input/textarea/contenteditable) const target = subContainer.querySelector('input:not([data-filled]), textarea:not([data-filled]), [contenteditable]:not([data-filled])') // 如果上面没找到,退一步:先找跟 node 最近的一个输入控件 || node.closest('.white_frame')?.querySelector('input:not([data-filled]), textarea:not([data-filled]), [contenteditable]:not([data-filled])'); if (!target) { console.log(`第${idx + 1}小问:未找到可填入的控件,答案=${ans}`); return; } // 3.3 精准填入当前控件,并标记避免重复 if ('value' in target) target.value = ans; else target.textContent = ans; target.setAttribute('data-filled', ''); filledInputs.add(target); // 可选:触发事件让平台感知输入变化 try { target.dispatchEvent(new Event('input', { bubbles: true })); target.dispatchEvent(new Event('change', { bubbles: true })); target.dispatchEvent(new Event('blur', { bubbles: true })); } catch { } console.log(`第${idx + 1}小问(填空题)已填入:${ans}`); }); console.log('完成:按小问粒度填入,已避免整题串填。'); } // --- 脚本入口 --- if (window.top !== window) { // 在 iframe 中,执行核心逻辑 runAutoAnswer(); } else { // 在顶层窗口中,创建UI并输出提示 console.log('[WELearn Auto Answer] Running in top frame (UI created):', location.href); // 等待DOM加载完毕再创建UI if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', createToggleUI); } else { createToggleUI(); } } })();