// ==UserScript== // @name 武汉讯网在线课程自动答题(自用) // @namespace http://tampermonkey.net/ // @version 1.0 // @description 监控 practise.do 接口拿答案,自动选择、点击确定并点击关闭,用于开发者测试 // @author Jruing97 // @match *://0155.whxunw.com/* // @grant none // @license MIT // ==/UserScript== (function () { 'use strict'; // ================= 1. 全局变量与核心业务逻辑 ================= let currentAnswer = null; // 【新增】:答案转换映射表 const answerMap = { 'N': 'B', 'Y': 'A' }; // 处理网络请求,提取 key function handleResponseData(url, responseText) { if (url.includes('/exam/user/kjxx/practise.do')) { try { const res = JSON.parse(responseText); if (res.code === 0 && res.data && res.data.key) { let rawKey = res.data.key; // 【新增】:判断并转换答案 // 如果取到 N 则转换为 B,取到 Y 则转换为 A,否则保持原值 currentAnswer = answerMap[rawKey] || rawKey; console.log(`[测试] 接口原始答案: ${rawKey} -> 转换后答案: ${currentAnswer}`); } } catch (e) { console.warn('[测试] practise.do 返回非 JSON 数据或解析失败'); } } } // 处理动态弹框,匹配答案并点击 function handleDynamicPopup(popupNode) { if (!currentAnswer) { console.log('[测试] 弹框出现了,但还没拿到答案,等待中...'); return; } // 【功能1】:在标题右侧显示红色答案 const headerNode = popupNode.querySelector('div.header'); if (headerNode && !headerNode.querySelector('.tm-answer-hint')) { const answerSpan = document.createElement('span'); answerSpan.className = 'tm-answer-hint'; answerSpan.textContent = ` (答案: ${currentAnswer})`; answerSpan.style.color = 'red'; answerSpan.style.fontWeight = 'bold'; answerSpan.style.marginLeft = '10px'; headerNode.appendChild(answerSpan); } // 【功能2】:自动选择答案 const options = popupNode.querySelectorAll('div.li'); let targetClicked = false; options.forEach(opt => { const unitNode = opt.querySelector('div.unit'); if (unitNode) { const letter = unitNode.textContent.trim(); if (letter === currentAnswer) { console.log(`[测试] 找到选项 ${currentAnswer},模拟点击`); opt.click(); targetClicked = true; } } }); // 【功能3】:自动点击“确定”按钮 if (targetClicked) { setTimeout(() => { const confirmBtn = popupNode.querySelector('button.el-button--primary'); if (confirmBtn) { console.log('[测试] 自动点击“确定”按钮'); confirmBtn.click(); // 【功能4】:点击“确定”后,自动点击“关闭”按钮 // 延迟 1000ms,给页面留出渲染“关闭”按钮的时间 setTimeout(() => { // 再次查找按钮,并严格判断文本内容是否为“关闭” const closeBtn = popupNode.querySelector('button.el-button--primary'); if (closeBtn && closeBtn.textContent.trim() === '关闭') { console.log('[测试] 自动点击“关闭”按钮'); closeBtn.click(); // 重置答案,准备迎接下一道题 currentAnswer = null; } }, 1000); } }, 500); } else { console.warn(`[测试] 弹框中未找到选项 ${currentAnswer},请检查 DOM 结构`); } } // ================= 2. 监听动态 DOM 变化 ================= const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { for (const node of mutation.addedNodes) { if (node.nodeType !== 1) continue; if (node.classList && node.classList.contains('dilog')) { handleDynamicPopup(node); } } } }); observer.observe(document.body, { childList: true, subtree: true }); // ================= 3. 拦截网络请求 (Hook) ================= const originalOpen = XMLHttpRequest.prototype.open; const originalSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open = function (method, url, ...rest) { this._hookUrl = url; return originalOpen.apply(this, [method, url, ...rest]); }; XMLHttpRequest.prototype.send = function (...args) { this.addEventListener('load', function () { if (this.responseType === '' || this.responseType === 'text') { handleResponseData(this._hookUrl, this.responseText); } }); return originalSend.apply(this, args); }; const originalFetch = window.fetch; window.fetch = function (...args) { return originalFetch.apply(this, args).then(response => { const clonedResponse = response.clone(); const url = typeof args[0] === 'string' ? args[0] : args[0].url; clonedResponse.text().then(text => handleResponseData(url, text)); return response; }); }; // ================= 4. 30s 定时检查 ================= setInterval(() => { const popup = document.querySelector('div.dilog'); if (popup && currentAnswer) { console.log('[测试] 定时器发现未处理的弹框,执行点击'); handleDynamicPopup(popup); } }, 30000); console.log('[测试] 脚本已加载,监控 practise.do 接口与动态弹框已启动。'); })();