// ==UserScript== // @name Welearn助手 // @namespace https://bbs.tampermonkey.net.cn/ // @version 0.1.6 // @author 恶搞之家 // @description 自动选择Welearn平台的选择题、判断题、填空题和下拉框答案 // @match *://course.sflep.com/* // @match *://welearn.sflep.com/* // @match *://wetest.sflep.com/* // @match *://courseappserver.sflep.com/* // @match *://centercourseware.sflep.com/* // @match *://*.sflep.com/* // ==/UserScript== (function () { 'use strict'; let isAutoLoopActive = false; let autoLoopTimeout = null; let waitTimerActive = false; let waitTimerInterval = null; let defaultWaitTime = 30; const createEvent = (type, options = {}) => new Event(type, {bubbles: true, cancelable: true, ...options}); const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); async function autoFillAnswers() { console.log('开始自动填空'); const answerElements = document.querySelectorAll('span.key, div.key'); const inputBoxes = document.querySelectorAll('span[autocapitalize="none"], textarea.blank'); console.log('找到填空题数量:', inputBoxes.length); if (answerElements.length === 0 || inputBoxes.length === 0) { console.log('没有找到填空题'); return; } answerElements.forEach((element, index) => { try { const answer = element.textContent; const inputBox = inputBoxes[index]; if (!inputBox) return; inputBox.tagName === 'SPAN' ? inputBox.textContent = answer : inputBox.value = answer; ['click', 'input'].forEach(evt => inputBox.dispatchEvent(createEvent(evt))); console.log(`已填写第${index + 1}题答案:`, answer); } catch (error) { console.log(`填写第${index + 1}题出错:`, error); } }); await sleep(500); inputBoxes.forEach((inputBox, index) => { try { if (!document.body.contains(inputBox)) return; inputBox.dispatchEvent(createEvent('click')); inputBox.dispatchEvent(createEvent('focus')); setTimeout(() => { inputBox.dispatchEvent(createEvent('blur')); }, 50); if (inputBox.tagName === 'TEXTAREA') { inputBox.dispatchEvent(createEvent('change')); } } catch (error) { console.log(`额外点击第${index + 1}个填空框出错:`, error); } }); console.log('填空题处理完成'); } async function autoSelectCorrectAnswers() { console.log('开始自动选择答案'); let allQuestions = []; for (let i = 0; i < 10 && allQuestions.length === 0; i++) { const choiceQuestions = document.querySelectorAll('et-choice[et-index]'); const tfQuestions = document.querySelectorAll('et-tof[et-index]'); allQuestions = [...choiceQuestions, ...tfQuestions]; if (allQuestions.length === 0) { console.log('等待题目加载...'); await sleep(700); } } console.log('找到题目数量:', allQuestions.length); if (allQuestions.length === 0) { console.log('没有找到题目'); return; } for (let i = 0; i < allQuestions.length; i++) { const question = allQuestions[i]; const isTF = question.tagName.toLowerCase() === 'et-tof'; let options = []; for (let j = 0; j < 10 && options.length === 0; j++) { if (isTF) { options = question.querySelectorAll('span[ng-class*="chosen:tof.value"], div.wrapper span'); } else { options = question.querySelectorAll('ol > li, div.wrapper li, span[ng-click*="choice.select"], li[class]'); } if (options.length === 0) { console.log(`等待第${i + 1}题选项加载...`); await sleep(500); } } console.log(`处理第${i + 1}题,选项数量:`, options.length); if (options.length === 0) { console.log('未找到选项,跳过'); continue; } let isCorrect = false; for (let attempt = 0; attempt < 3 && !isCorrect; attempt++) { for (const option of options) { try { const wrapper = isTF ? option.closest('div[ng-class*="isKeyVisible"]') : option.closest('div.wrapper'); if (wrapper?.classList.contains('correct')) { console.log('此选项已经正确'); isCorrect = true; break; } option.click(); await sleep(500); const updatedWrapper = isTF ? option.closest('div[ng-class*="isKeyVisible"]') : option.closest('div.wrapper'); if (updatedWrapper?.classList.contains('correct')) { console.log('找到正确答案:', option.textContent.trim()); isCorrect = true; break; } } catch (error) { console.log('点击选项出错:', error); } } if (!isCorrect) await sleep(600); } await sleep(500); } console.log('所有题目处理完成'); } function autoSelectDropdownAnswers() { console.log('开始处理下拉框选择题'); const dropdowns = document.querySelectorAll('select[ng-model]'); console.log('找到下拉框数量:', dropdowns.length); if (dropdowns.length === 0) return; dropdowns.forEach((dropdown, index) => { try { const correctOption = dropdown.querySelector('option.key'); if (!correctOption) return; dropdown.value = correctOption.value; dropdown.dispatchEvent(createEvent('change')); console.log(`已选择第${index + 1}个下拉框答案:`, correctOption.textContent.trim()); } catch (error) { console.log(`处理第${index + 1}个下拉框出错:`, error); } }); } async function autoMatchLines() { console.log('开始处理连线题'); const matchingElements = document.querySelectorAll('et-matching[et-index]'); if (matchingElements.length === 0) return; console.log(`找到连线题数量: ${matchingElements.length}`); for (const matchingElement of matchingElements) { const matchingKey = matchingElement.getAttribute('key'); if (!matchingKey) continue; console.log(`处理连线题,答案key: ${matchingKey}`); try { const pairs = matchingKey.split(',').map(pair => { const [left, right] = pair.split('-'); return { leftIndex: parseInt(left) - 1, rightIndex: parseInt(right) - 1 }; }); let angularSuccess = false; if (typeof angular !== 'undefined') { try { const scope = angular.element(matchingElement).scope(); if (scope?.matching) { if (Array.isArray(scope.matching.lines)) { scope.matching.lines = []; } for (const {leftIndex, rightIndex} of pairs) { const leftCircle = scope.matching.circles?.A?.[leftIndex]; const rightCircle = scope.matching.circles?.B?.[rightIndex]; if (!leftCircle || !rightCircle) continue; if (typeof leftCircle.select === 'function') { leftCircle.select(); await sleep(200); rightCircle.select?.(); } else if (typeof scope.matching.connect === 'function') { scope.matching.connect(leftCircle, rightCircle); } else if (Array.isArray(scope.matching.lines)) { scope.matching.lines.push({ x1: leftCircle.x, y1: leftCircle.y, x2: rightCircle.x, y2: rightCircle.y, circleA: leftCircle, circleB: rightCircle }); } await sleep(200); } scope.$apply?.(); await sleep(500); if (Array.isArray(scope.matching.lines) && scope.matching.lines.length > 0) { angularSuccess = true; } } } catch (e) { console.error('Angular模型操作失败:', e); } } if (angularSuccess) continue; console.log('尝试DOM操作方式'); let leftCircles = []; let rightCircles = []; leftCircles = Array.from(matchingElement.querySelectorAll('circle[data-circle="A"]')); rightCircles = Array.from(matchingElement.querySelectorAll('circle[data-circle="B"]')); if (leftCircles.length === 0 || rightCircles.length === 0) { const allCircles = matchingElement.querySelectorAll('circle[ng-repeat]'); leftCircles = []; rightCircles = []; for (const circle of allCircles) { const ngRepeat = circle.getAttribute('ng-repeat'); if (ngRepeat?.includes('matching.circles.A')) { leftCircles.push(circle); } else if (ngRepeat?.includes('matching.circles.B')) { rightCircles.push(circle); } } } if (leftCircles.length === 0 || rightCircles.length === 0) { const allCircles = matchingElement.querySelectorAll('circle'); if (allCircles.length > 1) { const circlesWithCoords = Array.from(allCircles) .map(circle => ({ element: circle, x: parseFloat(circle.getAttribute('cx')), y: parseFloat(circle.getAttribute('cy')) })) .filter(c => !isNaN(c.x) && !isNaN(c.y)); if (circlesWithCoords.length > 1) { circlesWithCoords.sort((a, b) => a.x - b.x); const midX = (circlesWithCoords[0].x + circlesWithCoords[circlesWithCoords.length - 1].x) / 2; leftCircles = circlesWithCoords.filter(c => c.x < midX).map(c => c.element); rightCircles = circlesWithCoords.filter(c => c.x >= midX).map(c => c.element); } } } if (leftCircles.length === 0 || rightCircles.length === 0) continue; for (const {leftIndex, rightIndex} of pairs) { if (leftIndex < 0 || leftIndex >= leftCircles.length || rightIndex < 0 || rightIndex >= rightCircles.length) continue; const leftCircle = leftCircles[leftIndex]; const rightCircle = rightCircles[rightIndex]; const leftX = parseFloat(leftCircle.getAttribute('cx')); const leftY = parseFloat(leftCircle.getAttribute('cy')); const rightX = parseFloat(rightCircle.getAttribute('cx')); const rightY = parseFloat(rightCircle.getAttribute('cy')); try { const createMouseEvent = (type, x, y) => new MouseEvent(type, { bubbles: true, cancelable: true, view: window, clientX: x, clientY: y }); ['mousedown', 'click'].forEach(type => leftCircle.dispatchEvent(createMouseEvent(type, leftX, leftY))); await sleep(300); ['mousedown', 'click'].forEach(type => rightCircle.dispatchEvent(createMouseEvent(type, rightX, rightY))); await sleep(300); if (typeof angular !== 'undefined') { const scope = angular.element(leftCircle).scope(); scope?.$apply?.(); } } catch (e) { console.error('点击圆圈出错:', e); } } } catch (error) { console.error(`处理连线题出错:`, error); } await new Promise(resolve => setTimeout(resolve, 1000)); } console.log('所有连线题处理完成'); } function isElementVisible(element) { if (!element) return false; try { const style = window.getComputedStyle(element); return style.display !== 'none' && style.visibility !== 'hidden' && element.offsetParent !== null; } catch (e) { return true; } } async function clickElement(element) { if (!element) return; try { element.click(); await sleep(100); const cx = parseFloat(element.getAttribute('cx')) || 0; const cy = parseFloat(element.getAttribute('cy')) || 0; const mouseEventOptions = { view: window, bubbles: true, cancelable: true, clientX: cx, clientY: cy }; ['mousedown', 'mouseup', 'click'].forEach(type => element.dispatchEvent(new MouseEvent(type, mouseEventOptions))); if (typeof angular !== 'undefined') { const scope = angular.element(element).scope(); scope?.$apply?.(); if (element.tagName === 'circle' && element.hasAttribute('data-index')) { const dataIndex = element.getAttribute('data-index'); const dataCircle = element.getAttribute('data-circle'); const matchingElement = element.closest('et-matching[et-index]'); if (matchingElement && dataCircle) { const matchingScope = angular.element(matchingElement).scope(); const circles = matchingScope?.matching?.circles?.[dataCircle]; const circle = circles?.[dataIndex]; if (circle?.select) { circle.select(); matchingScope.$apply?.(); } } } } } catch (error) { console.error('点击元素出错:', error); } } async function submitAnswers() { console.log('准备提交答案'); let submitButton; for (let i = 0; i < 10 && !submitButton; i++) { try { const etButtons = document.querySelectorAll('et-button[right][action="item.submit()"]'); for (const etBtn of etButtons) { const btn = etBtn.querySelector('button[ng-click="btn.doAction()"]'); if (btn) { submitButton = btn; break; } } if (!submitButton) { submitButton = Array.from(document.querySelectorAll('button')).find(btn => btn.textContent.trim() === 'Submit' || btn.querySelector('span')?.textContent.trim() === 'Submit' ); } if (!submitButton) { submitButton = document.querySelector('button[ng-click*="btn.doAction()"]'); } if (!submitButton) { for (const etBtn of document.querySelectorAll('et-button')) { const btn = etBtn.querySelector('button'); if (btn?.textContent.includes('Submit') || btn?.innerHTML.includes('Submit')) { submitButton = btn; break; } } } if (submitButton && (!submitButton.disabled && submitButton.offsetParent !== null)) { break; } else { submitButton = null; } } catch (error) { console.log('查找Submit按钮出错:', error); } if (!submitButton) { await sleep(1000); } } if (!submitButton) { console.log('未找到Submit按钮'); return false; } console.log('找到Submit按钮'); try { submitButton.scrollIntoView({ behavior: 'smooth', block: 'center' }); await sleep(1000); submitButton.click(); await sleep(500); submitButton.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window })); if (typeof angular !== 'undefined') { const etButtonElement = submitButton.closest('et-button'); if (etButtonElement?.getAttribute('action') === 'item.submit()') { const scope = angular.element(etButtonElement).scope(); scope?.item?.submit?.(); scope?.$apply?.(); } const scope = angular.element(submitButton).scope(); scope?.btn?.doAction?.(); scope?.$apply?.(); } await sleep(4000); return await autoNextSection(); } catch (error) { console.log('点击Submit按钮出错:', error); return false; } } async function autoNextSection() { console.log('准备自动跳转到下一章节'); const isInIframe = window !== window.top; console.log(`跳转环境: ${isInIframe ? 'iframe内' : '主窗口'}`); if (isInIframe) { console.log('尝试在父窗口中执行NextSCO函数'); try { const scriptForParent = document.createElement('script'); scriptForParent.textContent = ` try { if (window.parent && window.parent !== window) { if (typeof window.parent.NextSCO === 'function') { window.parent.NextSCO(); console.log("在父窗口中成功调用NextSCO"); } else { console.log("父窗口中不存在NextSCO函数"); window.parent.location.href = 'javascript:void(0); try { NextSCO(); } catch(e) {}'; } } else { console.log("无法访问父窗口"); } } catch(e) { console.error("向父窗口发送NextSCO调用失败:", e); } `; document.body.appendChild(scriptForParent); document.body.removeChild(scriptForParent); try { window.parent.postMessage({ action: 'executeNextSCO' }, '*'); console.log('已向父窗口发送postMessage请求'); const listenerScript = document.createElement('script'); listenerScript.textContent = ` (function() { if (window.parent === window) { window.addEventListener('message', function(event) { if (event.data && event.data.action === 'executeNextSCO') { console.log('收到执行NextSCO的消息'); try { if (typeof NextSCO === 'function') { NextSCO(); console.log('成功执行NextSCO'); } else { console.log('NextSCO函数不存在'); try { location.href = 'javascript:NextSCO();'; } catch(e) {} } } catch(e) { console.error('执行NextSCO失败:', e); } } }); } })(); `; document.body.appendChild(listenerScript); document.body.removeChild(listenerScript); } catch (e) { console.log('发送postMessage失败:', e); } try { window.top.location.href = 'javascript:void(0); try { NextSCO(); } catch(e) {}'; console.log('已尝试改变顶层窗口location'); } catch (e) { console.log('改变顶层窗口location失败:', e); } return true; } catch (e) { console.log('跳出iframe失败:', e); } } try { console.log('尝试点击页面中的javascript:NextSCO()链接'); const navLinks = document.querySelectorAll('a[href*="javascript:NextSCO()"], a[href*="javascript:PrevSCO()"]'); if (navLinks.length > 0) { const nextLink = Array.from(navLinks).find(link => link.href.includes('NextSCO') && !link.href.includes('PrevSCO') ); if (nextLink) { console.log('找到NextSCO链接,尝试点击'); nextLink.click(); return; } } } catch (e) { console.log('点击NextSCO链接失败:', e); } try { const textNodes = []; function findTextNodes(node) { if (node.nodeType === 3) { if (node.nodeValue.includes('javascript:NextSCO()')) { textNodes.push(node); } } else if (node.nodeType === 1) { for (let i = 0; i < node.childNodes.length; i++) { findTextNodes(node.childNodes[i]); } } } findTextNodes(document.body); if (textNodes.length > 0) { console.log('找到包含NextSCO文本的节点:', textNodes.length, '个'); for (const textNode of textNodes) { let current = textNode.parentElement; while (current && current.tagName !== 'A' && current.tagName !== 'BUTTON') { current = current.parentElement; } if (current) { console.log('点击包含NextSCO文本的元素'); current.click(); return; } } } } catch (e) { console.log('查找NextSCO文本失败:', e); } try { console.log('尝试查找侧边栏中的蓝色箭头按钮'); const potentialNavButtons = [ ...document.querySelectorAll('.courseware_sidebar a, .courseware_sidebar_2 a'), ...document.querySelectorAll('a[class*="nav"], img[src*="arrow"], img[src*="next"]'), ...document.querySelectorAll('a img[src*="arrow"], a img[src*="next"]'), ...document.querySelectorAll('ul.c_s_3 li a'), ...document.querySelectorAll('li.c_s_3_2 a') ]; const navButtons = Array.from(potentialNavButtons).filter(btn => { const rect = btn.getBoundingClientRect(); const isAtSide = rect.left < 100 || rect.right > window.innerWidth - 100; try { const style = window.getComputedStyle(btn); const hasTurquoiseColor = style.color.includes('rgb(0, 255, 255)') || style.color.includes('rgb(0, 255, 255)') || style.backgroundColor.includes('rgb(0, 255, 255)') || style.background.includes('rgb(0, 255, 255)') || btn.innerHTML.includes('rgb(0, 255, 255)') || btn.innerHTML.includes('#00ffff') || btn.innerHTML.includes('cyan'); const isArrow = btn.textContent.includes('→') || btn.textContent.includes('▶') || btn.textContent.includes('▷') || btn.innerHTML.includes('arrow') || (btn.querySelector('img') && ( btn.querySelector('img').src.includes('arrow') || btn.querySelector('img').src.includes('next') )); return isAtSide || hasTurquoiseColor || isArrow; } catch (e) { return isAtSide; } }); console.log(`找到${navButtons.length}个可能的导航按钮`); if (navButtons.length > 0) { for (const btn of navButtons) { console.log(`点击导航按钮: ${btn.textContent || btn.className || btn.id || '未命名按钮'}`); btn.click(); await new Promise(resolve => setTimeout(resolve, 500)); } return; } } catch (e) { console.log('查找导航按钮出错:', e); } try { console.log('尝试直接执行NextSCO函数'); const scriptEl = document.createElement('script'); scriptEl.textContent = ` try { if (typeof NextSCO === 'function') { NextSCO(); console.log("成功执行NextSCO函数"); } else { console.log("NextSCO函数未定义"); } } catch(e) { console.error("执行NextSCO出错:", e); } `; document.body.appendChild(scriptEl); document.body.removeChild(scriptEl); setTimeout(() => { try { window.location.href = 'javascript:void(0); try { NextSCO(); } catch(e) {}'; } catch (e) { } }, 1000); } catch (e) { console.log('执行脚本失败:', e); } } async function ensureCorrectContext() { const isInIframe = window !== window.top; console.log(`当前环境: ${isInIframe ? 'iframe内' : '主窗口'}`); if (isInIframe && window.name === 'contentFrame') { console.log('已在答题的iframe中,可以直接答题'); return; } if (!isInIframe) { console.log('在主窗口中,尝试切换到contentFrame'); let contentFrame = document.getElementById('contentFrame'); if (!contentFrame) { const frames = document.getElementsByTagName('iframe'); for (let i = 0; i < frames.length; i++) { if (frames[i].name === 'contentFrame') { contentFrame = frames[i]; break; } } } if (contentFrame) { console.log('找到contentFrame,准备在其中执行答题'); try { return executeInContentFrame(contentFrame); } catch (e) { console.error('向contentFrame执行答题失败:', e); } } else { const iframes = document.querySelectorAll('iframe'); console.log(`共找到${iframes.length}个iframe`); for (let i = 0; i < iframes.length; i++) { try { const iframe = iframes[i]; if (!iframe.contentDocument) continue; const hasQuizElements = iframe.contentDocument.querySelector('et-choice') || iframe.contentDocument.querySelector('et-tof') || iframe.contentDocument.querySelector('span.key') || iframe.contentDocument.querySelector('div.key') || iframe.contentDocument.querySelector('select[ng-model]') || iframe.contentDocument.querySelector('button'); if (hasQuizElements) { console.log(`找到可能的答题iframe: ${iframe.name || iframe.id || i}`); return executeInContentFrame(iframe); } } catch (e) { console.log(`检查iframe ${i}失败:`, e); } } console.log('未找到答题iframe,直接在当前窗口执行'); } } } async function executeInContentFrame(frame) { console.log('准备在iframe中执行答题'); try { if (typeof frame.contentWindow.autoSelectCorrectAnswers === 'function') { console.log('iframe中已存在答题函数,直接调用'); return { autoSelectCorrectAnswers: frame.contentWindow.autoSelectCorrectAnswers, autoFillAnswers: frame.contentWindow.autoFillAnswers, autoSelectDropdownAnswers: frame.contentWindow.autoSelectDropdownAnswers, autoMatchLines: frame.contentWindow.autoMatchLines, submitAnswers: frame.contentWindow.submitAnswers }; } const scriptContent = ` if (!window.weLearnHelperInjected) { window.weLearnHelperInjected = true; window.parent.postMessage({ action: 'iframeReady', frameId: '${frame.id || frame.name || 'unknown'}' }, '*'); const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); const createEvent = (type, options = {}) => new Event(type, {bubbles: true, cancelable: true, ...options}); ${autoFillAnswers.toString()} ${autoSelectCorrectAnswers.toString()} ${autoSelectDropdownAnswers.toString()} ${autoMatchLines.toString()} ${submitAnswers.toString()} window.addEventListener('message', function(event) { if (event.data && event.data.action === 'runQuizFunctions') { console.log('iframe收到执行答题请求'); const executeAsync = async () => { try { if (event.data.step === 'selectAnswers') { await autoSelectCorrectAnswers(); window.parent.postMessage({ action: 'stepComplete', step: 'selectAnswers' }, '*'); } else if (event.data.step === 'fillAnswers') { autoFillAnswers(); window.parent.postMessage({ action: 'stepComplete', step: 'fillAnswers' }, '*'); } else if (event.data.step === 'selectDropdowns') { autoSelectDropdownAnswers(); window.parent.postMessage({ action: 'stepComplete', step: 'selectDropdowns' }, '*'); } else if (event.data.step === 'matchLines') { await autoMatchLines(); window.parent.postMessage({ action: 'stepComplete', step: 'matchLines' }, '*'); } else if (event.data.step === 'submitAnswers') { await submitAnswers(); window.parent.postMessage({ action: 'stepComplete', step: 'submitAnswers' }, '*'); } else if (event.data.step === 'all') { await autoSelectCorrectAnswers(); autoFillAnswers(); autoSelectDropdownAnswers(); await autoMatchLines(); await submitAnswers(); window.parent.postMessage({ action: 'stepComplete', step: 'all' }, '*'); } } catch (error) { console.error('执行iframe中的答题函数出错:', error); window.parent.postMessage({ action: 'stepError', error: error.message }, '*'); } }; executeAsync(); } }); } `; const script = document.createElement('script'); script.textContent = scriptContent; try { frame.contentDocument.body.appendChild(script); frame.contentDocument.body.removeChild(script); console.log('已将答题脚本注入到iframe中'); window.addEventListener('message', handleIframeMessage); return { autoSelectCorrectAnswers: () => sendCommandToIframe(frame, 'selectAnswers'), autoFillAnswers: () => sendCommandToIframe(frame, 'fillAnswers'), autoSelectDropdownAnswers: () => sendCommandToIframe(frame, 'selectDropdowns'), autoMatchLines: () => sendCommandToIframe(frame, 'matchLines'), submitAnswers: () => sendCommandToIframe(frame, 'submitAnswers') }; } catch (e) { console.error('无法向iframe注入脚本:', e); } console.log('尝试直接在iframe中执行答题代码'); } catch (e) { console.error('iframe操作失败:', e); } return null; } function handleIframeMessage(event) { if (event.data && event.data.action) { if (event.data.action === 'iframeReady') { console.log(`iframe已就绪: ${event.data.frameId}`); } else if (event.data.action === 'stepComplete') { console.log(`iframe完成步骤: ${event.data.step}`); } else if (event.data.action === 'stepError') { console.error(`iframe执行出错: ${event.data.error}`); } } } function sendCommandToIframe(frame, step) { return new Promise((resolve, reject) => { const timeout = setTimeout(() => { cleanup(); reject(new Error(`向iframe发送${step}命令超时`)); }, 30000); const messageHandler = (event) => { if (event.data && event.data.action === 'stepComplete' && event.data.step === step) { cleanup(); resolve(); } else if (event.data && event.data.action === 'stepError') { cleanup(); reject(new Error(event.data.error || '未知iframe错误')); } }; const cleanup = () => { clearTimeout(timeout); window.removeEventListener('message', messageHandler); }; window.addEventListener('message', messageHandler); try { frame.contentWindow.postMessage({ action: 'runQuizFunctions', step: step }, '*'); console.log(`已向iframe发送${step}命令`); } catch (e) { cleanup(); reject(e); } }); } function createIntegratedUI() { const globalStyle = document.createElement('style'); globalStyle.id = 'welearn-helper-style'; globalStyle.textContent = ` @keyframes button-pulse { 0% { transform: scale(1); } 50% { transform: scale(1.03); } 100% { transform: scale(1); } } .welearn-button { position: static; flex: 1; padding: 10px 15px; border: none; border-radius: 6px; cursor: pointer; font-weight: bold; font-size: 14px; transition: all 0.3s ease; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2), 0 1px 2px rgba(0, 0, 0, 0.1); } .welearn-button:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2), 0 2px 5px rgba(0, 0, 0, 0.1); } .welearn-button:active { transform: translateY(1px); box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15); } .welearn-auto-button { background: linear-gradient(135deg, #00ff9d 0%, #00c17b 100%); color: rgba(0, 0, 0, 0.8); } .welearn-auto-button:hover { background: linear-gradient(135deg, #00ffaa 0%, #00d38a 100%); } .welearn-loop-button { background: linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%); color: white; } .welearn-loop-button:hover { background: linear-gradient(135deg, #5CBF60 0%, #3E8D42 100%); } .welearn-loop-active { background: linear-gradient(135deg, #f44336 0%, #d32f2f 100%); animation: button-pulse 2s infinite ease-in-out; } .welearn-loop-active:hover { background: linear-gradient(135deg, #ff5252 0%, #e53935 100%); } `; const existingStyle = document.getElementById('welearn-helper-style'); if (existingStyle) existingStyle.remove(); document.head.appendChild(globalStyle); const mainPanel = document.createElement('div'); mainPanel.id = 'welearnHelperPanel'; mainPanel.style.cssText = ` position: fixed; top: 10px; left: 10px; z-index: 10000; background-color: rgba(33, 33, 33, 0.85); border-radius: 10px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); padding: 12px; color: white; font-size: 13px; width: 240px; display: flex; flex-direction: column; gap: 10px; backdrop-filter: blur(5px); border: 1px solid rgba(255, 255, 255, 0.1); `; const titleBar = document.createElement('div'); titleBar.style.cssText = ` display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid rgba(255, 255, 255, 0.15); padding-bottom: 8px; margin-bottom: 5px; `; const title = document.createElement('div'); title.textContent = 'Welearn助手'; title.style.cssText = ` font-weight: bold; font-size: 15px; color: #ffffff; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); `; const minimizeBtn = document.createElement('button'); minimizeBtn.textContent = '-'; minimizeBtn.style.cssText = ` background: none; border: none; color: white; font-size: 18px; cursor: pointer; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; padding: 0; border-radius: 50%; background-color: rgba(255, 255, 255, 0.1); transition: background-color 0.2s ease; `; minimizeBtn.onmouseover = () => { minimizeBtn.style.backgroundColor = 'rgba(255, 255, 255, 0.2)'; }; minimizeBtn.onmouseout = () => { minimizeBtn.style.backgroundColor = 'rgba(255, 255, 255, 0.1)'; }; minimizeBtn.onclick = () => { const content = document.getElementById('welearnHelperContent'); if (content.style.display === 'none') { content.style.display = 'flex'; minimizeBtn.textContent = '-'; } else { content.style.display = 'none'; minimizeBtn.textContent = '+'; } }; titleBar.appendChild(title); titleBar.appendChild(minimizeBtn); mainPanel.appendChild(titleBar); const content = document.createElement('div'); content.id = 'welearnHelperContent'; content.style.cssText = ` display: flex; flex-direction: column; gap: 10px; `; mainPanel.appendChild(content); const buttonContainer = document.createElement('div'); buttonContainer.style.cssText = ` display: flex; gap: 10px; `; const autoAnswerButton = document.createElement('button'); autoAnswerButton.id = 'autoAnswerButton'; autoAnswerButton.textContent = '一键全自动'; autoAnswerButton.className = 'welearn-button welearn-auto-button'; const autoLoopButton = document.createElement('button'); autoLoopButton.id = 'autoLoopButton'; autoLoopButton.textContent = '开始自动挂机'; autoLoopButton.className = 'welearn-button welearn-loop-button'; autoAnswerButton.onclick = async function() { await runAutoAnswerProcess(false); }; autoLoopButton.onclick = async function() { await toggleAutoLoop(); }; buttonContainer.appendChild(autoAnswerButton); buttonContainer.appendChild(autoLoopButton); content.appendChild(buttonContainer); const timerContainer = document.createElement('div'); timerContainer.id = 'waitTimerContainer'; timerContainer.style.cssText = ` display: flex; align-items: center; background-color: rgba(255, 255, 255, 0.08); padding: 8px 10px; border-radius: 6px; border: 1px solid rgba(255, 255, 255, 0.05); `; const timeInput = document.createElement('input'); timeInput.id = 'waitTimeInput'; timeInput.type = 'number'; timeInput.min = '1'; timeInput.max = '3600'; timeInput.value = defaultWaitTime.toString(); timeInput.style.cssText = ` width: 45px; padding: 5px; border-radius: 4px; border: 1px solid rgba(255, 255, 255, 0.2); text-align: center; margin: 0 5px; color: black; background-color: rgba(255, 255, 255, 0.9); font-weight: bold; box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); `; timeInput.addEventListener('change', function() { const newTime = parseInt(this.value, 10); if (!isNaN(newTime) && newTime > 0) { defaultWaitTime = newTime; const countdownDisplay = document.getElementById('countdownDisplay'); if (countdownDisplay) { const originalText = countdownDisplay.textContent; const originalColor = countdownDisplay.style.color; countdownDisplay.textContent = '已保存'; countdownDisplay.style.color = '#4CAF50'; setTimeout(() => { if (countdownDisplay) { countdownDisplay.textContent = originalText; countdownDisplay.style.color = originalColor; } }, 1000); } } }); const label = document.createElement('span'); label.textContent = '刷时长:'; label.style.cssText = ` margin-right: 4px; font-weight: 500; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3); `; const secondsLabel = document.createElement('span'); secondsLabel.textContent = '秒'; secondsLabel.style.cssText = ` margin-right: 5px; font-weight: 500; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3); `; const statusDisplay = document.createElement('div'); statusDisplay.id = 'countdownDisplay'; statusDisplay.textContent = '未开始'; statusDisplay.style.cssText = ` margin-left: auto; font-weight: bold; color: #00ffcc; min-width: 60px; text-align: right; text-shadow: 0 0 10px rgba(0, 255, 204, 0.5); `; timerContainer.appendChild(label); timerContainer.appendChild(timeInput); timerContainer.appendChild(secondsLabel); timerContainer.appendChild(statusDisplay); content.appendChild(timerContainer); const statusContainer = document.createElement('div'); statusContainer.style.cssText = ` font-size: 12px; color: #ccc; text-align: center; padding: 5px; background-color: rgba(255, 255, 255, 0.05); border-radius: 4px; `; statusContainer.textContent = 'by恶搞之家'; content.appendChild(statusContainer); const loopStatus = document.createElement('div'); loopStatus.id = 'loopStatusIndicator'; loopStatus.style.cssText = ` display: none; align-items: center; justify-content: center; background-color: rgba(220, 20, 60, 0.2); padding: 6px; border-radius: 5px; margin-top: 2px; font-weight: bold; font-size: 12px; border: 1px solid rgba(220, 20, 60, 0.3); `; content.appendChild(loopStatus); return mainPanel; } async function executeWaitTimer(callback) { const timeInput = document.getElementById('waitTimeInput'); const countdownDisplay = document.getElementById('countdownDisplay'); let waitTime = parseInt(timeInput?.value || defaultWaitTime); if (isNaN(waitTime) || waitTime < 1) { waitTime = defaultWaitTime; if (timeInput) timeInput.value = defaultWaitTime.toString(); } else { defaultWaitTime = waitTime; } if (waitTime <= 0) { if (typeof callback === 'function') callback(); return; } if (countdownDisplay) { countdownDisplay.textContent = waitTime + ' 秒'; countdownDisplay.style.color = '#00ffcc'; countdownDisplay.style.display = 'block'; } if (waitTimerInterval) { clearInterval(waitTimerInterval); } waitTimerActive = true; let remainingTime = waitTime; return new Promise(resolve => { waitTimerInterval = setInterval(() => { remainingTime--; if (countdownDisplay) { countdownDisplay.textContent = remainingTime + ' 秒'; if (remainingTime <= 10) { countdownDisplay.style.color = '#ff6b6b'; } } if (remainingTime <= 0) { clearInterval(waitTimerInterval); waitTimerInterval = null; waitTimerActive = false; if (countdownDisplay) { countdownDisplay.textContent = '完成!'; countdownDisplay.style.color = '#4CAF50'; setTimeout(() => { if (countdownDisplay) { countdownDisplay.textContent = '未开始'; countdownDisplay.style.color = '#00ffcc'; } }, 3000); } if (typeof callback === 'function') callback(); resolve(); } }, 1000); }); } function cancelWaitTimer() { if (waitTimerInterval) { clearInterval(waitTimerInterval); waitTimerInterval = null; } waitTimerActive = false; const countdownDisplay = document.getElementById('countdownDisplay'); if (countdownDisplay) { countdownDisplay.textContent = '已取消'; countdownDisplay.style.color = '#ff9800'; setTimeout(() => { if (countdownDisplay) countdownDisplay.textContent = ''; }, 3000); } } function addButton() { if (window !== window.top) { console.log('当前在iframe中,不添加按钮'); return; } removeExistingButtons(); const uiPanel = createIntegratedUI(); document.body.appendChild(uiPanel); updateButtonsState(); } function updateButtonsState() { const loopButton = document.getElementById('autoLoopButton'); if (loopButton) { if (isAutoLoopActive) { loopButton.textContent = '停止自动挂机'; loopButton.className = 'welearn-button welearn-loop-active'; showLoopStatus(true); } else { loopButton.textContent = '开始自动挂机'; loopButton.className = 'welearn-button welearn-loop-button'; showLoopStatus(false); } } } function showLoopStatus(isActive) { let statusIndicator = document.getElementById('loopStatusIndicator'); if (!statusIndicator) return; if (isActive) { statusIndicator.innerHTML = '<span style="display:inline-block;width:10px;height:10px;background-color:red;border-radius:50%;margin-right:5px;animation:blink 1s infinite;"></span> 自动挂机中'; statusIndicator.style.display = 'flex'; const style = document.createElement('style'); style.id = 'loopStatusStyle'; style.textContent = ` @keyframes blink { 0% { opacity: 1; } 50% { opacity: 0.3; } 100% { opacity: 1; } } `; if (!document.getElementById('loopStatusStyle')) { document.head.appendChild(style); } } else { statusIndicator.style.display = 'none'; } } async function toggleAutoLoop() { if (!isAutoLoopActive) { isAutoLoopActive = true; updateButtonsState(); showLoopStatus(true); await runAutoAnswerProcess(true); } else { stopAutoLoop(); } } function stopAutoLoop() { console.log('停止自动挂机'); isAutoLoopActive = false; if (autoLoopTimeout) { clearTimeout(autoLoopTimeout); autoLoopTimeout = null; } cancelWaitTimer(); updateButtonsState(); showLoopStatus(false); } async function runAutoAnswerProcess(isLoop) { if (!isLoop && isAutoLoopActive) { console.log('自动挂机已激活,单次执行被忽略'); return; } try { const answerButton = document.getElementById('autoAnswerButton'); if (isLoop) { if (answerButton) { answerButton.textContent = '等待中...'; answerButton.style.backgroundColor = 'orange'; } await executeWaitTimer(() => { if (answerButton) { answerButton.textContent = '开始答题...'; } }); if (!isAutoLoopActive) return; } if (answerButton) { answerButton.textContent = '检测环境...'; answerButton.style.backgroundColor = 'orange'; } const iframeFunctions = await ensureCorrectContext(); const selectAnswersFn = iframeFunctions ? iframeFunctions.autoSelectCorrectAnswers : autoSelectCorrectAnswers; const fillAnswersFn = iframeFunctions ? iframeFunctions.autoFillAnswers : autoFillAnswers; const selectDropdownsFn = iframeFunctions ? iframeFunctions.autoSelectDropdownAnswers : autoSelectDropdownAnswers; const matchLinesFn = iframeFunctions ? iframeFunctions.autoMatchLines : autoMatchLines; const submitAnswersFn = iframeFunctions ? iframeFunctions.submitAnswers : submitAnswers; if (answerButton) { answerButton.textContent = '答题中...'; } await selectAnswersFn(); if (answerButton) { answerButton.textContent = '填写空白...'; } await fillAnswersFn(); if (answerButton) { answerButton.textContent = '处理下拉框...'; } await selectDropdownsFn(); if (answerButton) { answerButton.textContent = '处理连线题...'; } await matchLinesFn(); if (answerButton) { answerButton.textContent = '提交中...'; } const submitResult = await submitAnswersFn(); if (!submitResult) { if (answerButton) { answerButton.textContent = '跳转中...'; answerButton.style.backgroundColor = 'blue'; } await autoNextSection(); } if (isLoop && isAutoLoopActive) { if (answerButton) { answerButton.textContent = '准备下一轮...'; answerButton.style.backgroundColor = 'purple'; } console.log('等待页面加载,准备下一轮答题...'); autoLoopTimeout = setTimeout(async () => { console.log('开始下一轮答题'); if (isAutoLoopActive) { await runAutoAnswerProcess(true); } }, 3000); } else { if (answerButton) { answerButton.textContent = '已完成'; answerButton.style.backgroundColor = 'green'; setTimeout(() => { if (answerButton && !isAutoLoopActive) { answerButton.textContent = '一键全自动'; answerButton.style.backgroundColor = 'rgb(0, 255, 127)'; } }, 3000); } } } catch (error) { console.error('自动化流程出错:', error); const answerButton = document.getElementById('autoAnswerButton'); if (answerButton) { answerButton.textContent = '出错了!'; answerButton.style.backgroundColor = 'red'; } if (isLoop && isAutoLoopActive) { console.log('自动挂机出错,10秒后重试...'); autoLoopTimeout = setTimeout(async () => { console.log('重新开始挂机'); if (isAutoLoopActive) { await runAutoAnswerProcess(true); } }, 10000); } else { setTimeout(() => { if (answerButton && !isAutoLoopActive) { answerButton.textContent = '一键全自动'; answerButton.style.backgroundColor = 'rgb(0, 255, 127)'; } }, 5000); } } } function removeExistingButtons() { const welearnPanel = document.getElementById('welearnHelperPanel'); if (welearnPanel && welearnPanel.parentNode) { welearnPanel.parentNode.removeChild(welearnPanel); } const oldElements = [ document.getElementById('autoAnswerButton'), document.getElementById('autoLoopButton'), document.getElementById('autoAnswerButtonContainer'), document.getElementById('autoAnswerInfoText'), document.getElementById('loopStatusIndicator'), document.getElementById('waitTimerContainer') ]; oldElements.forEach(element => { if (element && element.parentNode) { element.parentNode.removeChild(element); } }); const allFixedElements = document.querySelectorAll('*[style*="position: fixed"], *[style*="position:fixed"]'); for (const element of allFixedElements) { const computedStyle = window.getComputedStyle(element); const top = parseInt(computedStyle.top); const left = parseInt(computedStyle.left); if (top <= 50 && left <= 300 && !element.closest('#welearnHelperPanel') && (element.tagName === 'BUTTON' || element.tagName === 'DIV' || element.id.includes('auto') || element.id.includes('button') || (element.textContent && ( element.textContent.includes('一键') || element.textContent.includes('全自动') || element.textContent.includes('自动') || element.textContent.includes('挂机') || element.textContent.includes('答题') || element.textContent.includes('提交') )) )) { console.log('强制移除外部元素:', element.tagName, element.id || element.className, element.textContent?.substring(0, 20)); if (element.parentNode) { element.parentNode.removeChild(element); } } } const loopStyle = document.getElementById('loopStatusStyle'); if (loopStyle && loopStyle.parentNode) { loopStyle.parentNode.removeChild(loopStyle); } cancelWaitTimer(); console.log('移除已存在的UI元素'); } function clearExternalButtons() { const allButtons = document.querySelectorAll('button'); allButtons.forEach(button => { if (button.closest('#welearnHelperPanel')) { return; } if (button.textContent && ( button.textContent.includes('一键') || button.textContent.includes('全自动') || button.textContent.includes('自动') || button.textContent.includes('挂机') || button.textContent === '一键全自动' || button.textContent === '开始自动挂机' || button.textContent === '停止自动挂机' )) { console.log('强制移除外部按钮:', button.textContent); if (button.parentNode) { button.parentNode.removeChild(button); } } }); const containers = document.querySelectorAll('div[style*="position: fixed"], div[style*="position:fixed"]'); containers.forEach(container => { if (container.id === 'welearnHelperPanel' || container.closest('#welearnHelperPanel')) { return; } const style = window.getComputedStyle(container); const top = parseInt(style.top); const left = parseInt(style.left); if (top <= 50 && left <= 50) { const innerButtons = container.querySelectorAll('button'); if (innerButtons.length > 0) { console.log('移除外部按钮容器:', container.id || container.className); if (container.parentNode) { container.parentNode.removeChild(container); } } } }); const buttonContainer = document.getElementById('autoAnswerButtonContainer'); if (buttonContainer) { console.log('移除autoAnswerButtonContainer'); buttonContainer.parentNode.removeChild(buttonContainer); } } window.addEventListener('beforeunload', () => { stopAutoLoop(); }); let observer; function setupObserver() { if (observer) { observer.disconnect(); } observer = new MutationObserver((mutations) => { clearExternalButtons(); const hasPanel = document.getElementById('welearnHelperPanel'); const hasButton = document.getElementById('autoAnswerButton'); if ((!hasPanel || !hasButton) && window === window.top) { console.log('DOM变化检测到面板或按钮不存在,添加面板'); removeExistingButtons(); addButton(); } setTimeout(clearExternalButtons, 100); }); if (window === window.top) { observer.observe(document.body, { childList: true, subtree: true }); clearExternalButtons(); } } function setupPageListeners() { window.removeEventListener('load', handlePageLoad); window.addEventListener('load', handlePageLoad); if (window === window.top) { let lastUrl = window.location.href; const checkURLChange = () => { if (lastUrl !== window.location.href) { console.log('URL已变化,重新添加按钮'); lastUrl = window.location.href; setTimeout(() => { removeExistingButtons(); addButton(); }, 1000); } }; setInterval(checkURLChange, 1000); } } function handlePageLoad() { console.log('页面加载完成,检查按钮'); if (window === window.top) { removeExistingButtons(); addButton(); } } function initialize() { if (window.weLearnHelperInitialized) { console.log('助手已初始化,跳过'); return; } window.weLearnHelperInitialized = true; console.log('初始化WeLearn助手'); if (window === window.top) { removeExistingButtons(); addButton(); setupObserver(); setupPageListeners(); } } initialize(); async function simulateHTML5DragDrop(sourceElement, targetElement) { if (!sourceElement || !targetElement) return; console.log('使用HTML5拖放API模拟拖拽'); try { const dragStartEvent = new DragEvent('dragstart', { bubbles: true, cancelable: true, dataTransfer: new DataTransfer() }); dragStartEvent.dataTransfer.setData('text/plain', 'dragged'); sourceElement.dispatchEvent(dragStartEvent); await new Promise(resolve => setTimeout(resolve, 200)); const dragOverEvent = new DragEvent('dragover', { bubbles: true, cancelable: true, dataTransfer: dragStartEvent.dataTransfer }); targetElement.dispatchEvent(dragOverEvent); await new Promise(resolve => setTimeout(resolve, 200)); const dropEvent = new DragEvent('drop', { bubbles: true, cancelable: true, dataTransfer: dragStartEvent.dataTransfer }); targetElement.dispatchEvent(dropEvent); const dragEndEvent = new DragEvent('dragend', { bubbles: true, cancelable: true, dataTransfer: dragStartEvent.dataTransfer }); sourceElement.dispatchEvent(dragEndEvent); console.log('HTML5拖放事件序列完成'); } catch (e) { console.error('HTML5拖放模拟失败:', e); } } async function simulateMouseDrag(sourceElement, targetElement, coords) { if (!sourceElement || !targetElement) return; console.log('使用鼠标事件模拟拖拽'); const { fromX, fromY, toX, toY } = coords; try { const mouseDownEvent = new MouseEvent('mousedown', { bubbles: true, cancelable: true, view: window, clientX: fromX, clientY: fromY, button: 0 }); sourceElement.dispatchEvent(mouseDownEvent); await new Promise(resolve => setTimeout(resolve, 100)); const steps = 5; for (let i = 1; i <= steps; i++) { const moveX = fromX + (toX - fromX) * (i / steps); const moveY = fromY + (toY - fromY) * (i / steps); const mouseMoveEvent = new MouseEvent('mousemove', { bubbles: true, cancelable: true, view: window, clientX: moveX, clientY: moveY, button: 0 }); document.elementFromPoint(moveX, moveY)?.dispatchEvent(mouseMoveEvent); await new Promise(resolve => setTimeout(resolve, 50)); } const mouseUpEvent = new MouseEvent('mouseup', { bubbles: true, cancelable: true, view: window, clientX: toX, clientY: toY, button: 0 }); targetElement.dispatchEvent(mouseUpEvent); console.log('鼠标拖拽事件序列完成'); } catch (e) { console.error('鼠标拖拽模拟失败:', e); } } async function simulateDragDrop(sourceElement, targetElement, coords) { if (!sourceElement || !targetElement) return; console.log('使用组合事件模拟拖拽'); const { fromX, fromY, toX, toY } = coords; try { sourceElement.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true, view: window, clientX: fromX, clientY: fromY })); await new Promise(resolve => setTimeout(resolve, 100)); const dragStartEvent = new Event('dragstart', {bubbles: true, cancelable: true}); sourceElement.dispatchEvent(dragStartEvent); await new Promise(resolve => setTimeout(resolve, 100)); document.dispatchEvent(new MouseEvent('mousemove', { bubbles: true, cancelable: true, view: window, clientX: toX, clientY: toY })); await new Promise(resolve => setTimeout(resolve, 100)); targetElement.dispatchEvent(new Event('dragover', {bubbles: true, cancelable: true})); await new Promise(resolve => setTimeout(resolve, 100)); targetElement.dispatchEvent(new Event('drop', {bubbles: true, cancelable: true})); sourceElement.dispatchEvent(new Event('dragend', {bubbles: true, cancelable: true})); targetElement.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, cancelable: true, view: window, clientX: toX, clientY: toY })); console.log('组合拖拽事件序列完成'); } catch (e) { console.error('组合拖拽模拟失败:', e); } } })();