// ==UserScript== // @name 智慧树共享课全自动刷课(可拖动+精准识别) // @namespace zhihuishu-share-drag // @version 10.0.0 // @description 可拖动面板+精准识别共享课+25分钟自动切课+自动答题+弹窗处理 // @author 适配你的页面 // @match *://*.zhihuishu.com/* // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @grant GM_xmlhttpRequest // @connect tk.enncy.cn // @run-at document-idle // ==/UserScript== (function () { 'use strict'; // ===================== 基础配置 ===================== const DEFAULT_CONFIG = { watchTime: 25, // 单课时长(分钟) playbackRate: 1.25, // 播放倍速 autoSwitch: true, // 自动切课 autoPlay: true, // 自动播放 autoClosePopup: true, // 自动关弹窗 autoAnswer: true, // 自动答题 reviewCompleted: true, // 复看已完成课程 useOnlineBankFirst: true, // 优先言溪题库 }; // 言溪题库配置 const YANXI_BANK_CONFIG = { url: "https://tk.enncy.cn/query", token: "0abc866e094c4714936e88a84ae3cb93", }; // 全局变量 let state = { currentCourseIndex: 0, currentTime: 0, isRunning: false, totalCourses: 0, }; let config = { ...DEFAULT_CONFIG }; let allCourseList = []; // 所有课程列表 let selectedCourseList = []; // 选中的课程 let localQuestionBank = {}; // 本地题库 let timer = null; let popupTimer = null; let answerTimer = null; let panel = null; let isDragging = false; // 拖动状态 let dragOffset = { x: 0, y: 0 }; // 拖动偏移量 // ===================== 存储相关 ===================== function loadAllData() { try { config = { ...DEFAULT_CONFIG, ...JSON.parse(GM_getValue("zhs_full_config")) } } catch (e) { } try { state = { ...state, ...JSON.parse(GM_getValue("zhs_full_state")) } } catch (e) { } try { selectedCourseList = JSON.parse(GM_getValue("zhs_selected_courses")) || [] } catch (e) { } try { localQuestionBank = JSON.parse(GM_getValue("zhs_local_bank")) || {} } catch (e) { } } function saveAllData() { GM_setValue("zhs_full_config", JSON.stringify(config)); GM_setValue("zhs_full_state", JSON.stringify(state)); GM_setValue("zhs_selected_courses", JSON.stringify(selectedCourseList)); GM_setValue("zhs_local_bank", JSON.stringify(localQuestionBank)); } // ===================== 【核心修复】精准识别你的共享课 ===================== function getAllCourseCards() { let courseList = []; // ----------------===== 【你的页面专属】精准匹配共享课 =====---------------- // 完全适配你截图里的DOM结构:.datalist dl 就是每一个课程卡片 const shareCourseSelectors = [ ".datalist dl", // 你的共享课核心容器 ".sharing-new-style .datalist dl", ".course-list dl", ".shared-course-wrap dl", ]; // 遍历所有选择器,抓取课程 for (const selector of shareCourseSelectors) { const elements = document.querySelectorAll(selector); elements.forEach((el, index) => { // 提取课程标题(过滤掉老师、进度、无关文字) const titleEl = el.querySelector("h3, .course-title, .course-name, dt"); let title = ""; if (titleEl) { title = titleEl.textContent.trim().replace(/\s+/g, " "); } else { // 兜底:提取纯课程名,过滤进度、老师信息 const fullText = el.innerText.trim().replace(/\s+/g, " "); title = fullText.split("在学:")[0].split("进度:")[0].trim(); } // 过滤无效内容 if (!title || title.length < 4 || /登录|注册|客服|帮助/.test(title)) return; // 去重 if (courseList.some(item => item.title === title)) return; // 加入课程列表 courseList.push({ id: btoa(title + index), // 唯一ID index: index, title: title, element: el // 课程DOM元素,用于点击进入 }); }); } allCourseList = courseList; return courseList; } // ===================== 【核心新增】面板拖动功能 ===================== function initDrag() { const header = panel.querySelector(".panel-header"); if (!header) return; // 鼠标按下:开始拖动 header.addEventListener("mousedown", (e) => { isDragging = true; // 计算鼠标和面板左上角的偏移量 const rect = panel.getBoundingClientRect(); dragOffset.x = e.clientX - rect.left; dragOffset.y = e.clientY - rect.top; // 选中文字不影响拖动 e.preventDefault(); }); // 鼠标移动:拖动面板 document.addEventListener("mousemove", (e) => { if (!isDragging) return; // 计算新位置 let newLeft = e.clientX - dragOffset.x; let newTop = e.clientY - dragOffset.y; // 边界限制:不能拖出屏幕外 const windowWidth = window.innerWidth; const windowHeight = window.innerHeight; const panelWidth = panel.offsetWidth; const panelHeight = panel.offsetHeight; // 左右边界 if (newLeft < 0) newLeft = 0; if (newLeft + panelWidth > windowWidth) newLeft = windowWidth - panelWidth; // 上下边界 if (newTop < 0) newTop = 0; if (newTop + panelHeight > windowHeight) newTop = windowHeight - panelHeight; // 设置新位置 panel.style.left = newLeft + "px"; panel.style.top = newTop + "px"; panel.style.right = "unset"; // 取消原来的right定位 }); // 鼠标松开:结束拖动 document.addEventListener("mouseup", () => { isDragging = false; }); // 鼠标离开窗口:结束拖动 document.addEventListener("mouseleave", () => { isDragging = false; }); } // ===================== 面板创建 ===================== function createPanel() { // 移除已存在的面板 const existingPanel = document.getElementById("zhs-auto-panel"); if (existingPanel) existingPanel.remove(); panel = document.createElement("div"); panel.id = "zhs-auto-panel"; // 初始定位 panel.style.position = "fixed"; panel.style.top = "20px"; panel.style.right = "20px"; panel.style.zIndex = "999999"; panel.innerHTML = `
智慧树共享课刷课助手
正在加载课程...
运行状态: 未运行
当前课程: 0/0
计时: 00:00 / ${config.watchTime}分钟
`; document.body.appendChild(panel); // 初始化拖动功能 initDrag(); } // ===================== 刷新课程列表到面板 ===================== function refreshCourseList() { const courseList = getAllCourseCards(); const listBox = document.getElementById("courseList"); if (courseList.length === 0) { listBox.innerHTML = `
未找到课程,请确保在共享课列表页
`; return; } // 生成勾选列表 let html = ""; courseList.forEach(course => { const isChecked = selectedCourseList.some(item => item.id === course.id); html += `
`; }); listBox.innerHTML = html; // 绑定勾选事件 bindCourseCheckEvent(); // 更新状态 state.totalCourses = selectedCourseList.length; updateUI(); addLog(`成功加载${courseList.length}门共享课`); } // 绑定课程勾选事件 function bindCourseCheckEvent() { document.querySelectorAll("#courseList input[type=checkbox]").forEach(checkbox => { checkbox.onchange = () => { const courseId = checkbox.dataset.id; const course = allCourseList.find(item => item.id === courseId); if (!course) return; if (checkbox.checked) { // 选中,加入列表 if (!selectedCourseList.some(item => item.id === courseId)) { selectedCourseList.push(course); } } else { // 取消选中,移除 selectedCourseList = selectedCourseList.filter(item => item.id !== courseId); } saveAllData(); state.totalCourses = selectedCourseList.length; updateUI(); addLog(`${checkbox.checked ? "选中" : "取消选中"}:${course.title}`); }; }); } // ===================== 刷课核心逻辑 ===================== // 开始刷课 function startWatch() { if (selectedCourseList.length === 0) { alert("请先勾选要刷的课程!"); addLog("请先勾选要刷的课程", "error"); return; } if (state.isRunning) return; state.isRunning = true; state.currentTime = 0; state.currentCourseIndex = 0; saveAllData(); updateUI(); addLog("开始自动刷课", "success"); // 打开第一门课 openTargetCourse(); } // 暂停刷课 function stopWatch() { state.isRunning = false; saveAllData(); updateUI(); clearAllTimer(); addLog("已暂停自动刷课"); } // 打开目标课程 function openTargetCourse() { if (!state.isRunning) return; if (state.currentCourseIndex >= selectedCourseList.length) { addLog("全部课程刷完!", "success"); alert("✅ 全部选中的课程已刷完!"); stopWatch(); return; } const targetCourse = selectedCourseList[state.currentCourseIndex]; if (!targetCourse || !targetCourse.element) { addLog("未找到目标课程", "error"); stopWatch(); return; } // 点击进入课程 targetCourse.element.click(); addLog(`已打开课程:${targetCourse.title}`, "success"); updateUI(); // 进入课程后,启动计时 setTimeout(() => { startTimer(); setupVideo(); }, 2000); } // 计时核心 function startTimer() { clearInterval(timer); timer = setInterval(() => { if (!state.isRunning) return; state.currentTime++; saveAllData(); updateUI(); // 时长到了,切课 if (state.currentTime >= config.watchTime * 60) { clearInterval(timer); addLog(`【${selectedCourseList[state.currentCourseIndex].title}】时长已满`, "success"); state.currentTime = 0; state.currentCourseIndex++; saveAllData(); // 返回课程列表,打开下一门 backToCourseList(); setTimeout(() => { openTargetCourse(); }, 3000 + Math.random() * 1000); } }, 1000); } // 返回课程列表 function backToCourseList() { const backBtn = document.querySelector('a:has-text("返回学堂"), .back-btn, a[href*="onlinestuh5"], .header-left a'); if (backBtn && backBtn.offsetParent !== null) { backBtn.click(); addLog("已点击「返回学堂」,回到课程列表", "success"); } else { addLog("未找到返回按钮,手动跳转", "error"); window.location.href = "https://onlineweb.zhihuishu.com/onlinestuh5"; } } // 视频设置 function setupVideo() { let attempts = 0; const detectTimer = setInterval(() => { const video = document.querySelector("video"); if (video) { clearInterval(detectTimer); video.playbackRate = config.playbackRate; if (config.autoPlay) { video.play().catch(() => { addLog("自动播放失败,请手动点击播放", "error"); }); } addLog("视频设置完成,已自动播放", "success"); // 视频结束自动续播下一节 video.addEventListener("ended", () => { addLog("当前小节播放结束,自动播放下一节", "success"); setTimeout(() => { const nextBtn = document.querySelector(".next-btn, .nav-next, .catalog-item.active + .catalog-item"); if (nextBtn) nextBtn.click(); }, 1500); }); } else if (attempts > 30) { clearInterval(detectTimer); addLog("未找到视频元素", "error"); } attempts++; }, 500); } // ===================== 自动答题&弹窗处理 ===================== // 弹窗监控 function startPopupMonitor() { if (popupTimer) clearInterval(popupTimer); popupTimer = setInterval(() => { if (!config.autoClosePopup) return; // 挂机验证弹窗 const continueBtns = document.querySelectorAll('.dialog-footer .btn, .vjs-modal-dialog .vjs-close-button, .continue-btn, .confirm-btn, button[title="继续播放"]'); continueBtns.forEach(btn => { if (btn.offsetParent !== null) { btn.click(); addLog("已自动关闭挂机验证弹窗", "success"); } }); // 普通关闭弹窗 const closeBtns = document.querySelectorAll('.close, .btn-close, .modal-close, .dialog-close, [class*="close"]'); closeBtns.forEach(btn => { if (btn.offsetParent !== null && !btn.closest(".video-js")) { btn.click(); addLog("已自动关闭弹窗", "success"); } }); window.onbeforeunload = null; }, 2000); } // 自动答题监控 function startAnswerMonitor() { if (answerTimer) clearInterval(answerTimer); answerTimer = setInterval(() => { if (!config.autoAnswer) return; const popupSelectors = [ '.question-modal', '.test-popup', '.dialog-box:has(.question-stem)', '.el-dialog:has(.question-title)', '.popupsbox:has(.option-item)' ]; let answerPopup = null; for (const sel of popupSelectors) { const el = document.querySelector(sel); if (el && el.offsetParent !== null) { answerPopup = el; break; } } if (answerPopup) handleAnswerPopup(answerPopup); }, 1000); } // 处理答题 async function handleAnswerPopup(popup) { if (popup.dataset.processed === "true") return; popup.dataset.processed = "true"; // 提取题干 const stemEl = popup.querySelector('.question-stem, .question-title, .stem, .title h3'); const stem = stemEl ? stemEl.textContent.trim().replace(/\s+/g, "") : ""; if (!stem) { popup.dataset.processed = "false"; return; } addLog(`识别到题目:${stem}`); // 提取选项 const optionEls = Array.from(popup.querySelectorAll('.option-item, .answer-option, .option, .el-radio, .el-checkbox')).filter(el => el.offsetParent !== null); if (optionEls.length === 0) { popup.dataset.processed = "false"; return; } const optionList = optionEls.map(el => { const textEl = el.querySelector('.option-text, .label, span') || el; return { el: el, text: textEl.textContent.trim().replace(/\s+/g, "") }; }); const optionTextList = optionList.map(opt => opt.text); const isMulti = popup.querySelector('.el-checkbox') !== null; const questionType = isMulti ? "multi" : "single"; // 真人延迟 await new Promise(r => setTimeout(r, 1000 + Math.random() * 1000)); // 在线搜题 let correctAnswers = []; if (config.useOnlineBankFirst) { correctAnswers = await searchYanxiBank(stem, optionTextList, questionType); } // 本地题库兜底 if (correctAnswers.length === 0) { for (const key in localQuestionBank) { const cleanKey = key.replace(/\s+/g, ""); if (stem.includes(cleanKey) || cleanKey.includes(stem)) { correctAnswers = localQuestionBank[key]; addLog(`本地题库匹配成功`, "success"); break; } } } // 随机选择 let selected = []; if (correctAnswers.length > 0) { correctAnswers.forEach(answer => { const cleanAnswer = answer.replace(/\s+/g, ""); const target = optionList.find(opt => opt.text.includes(cleanAnswer) || cleanAnswer.includes(opt.text)); if (target) { target.el.click(); selected.push(target.text); } }); } else { addLog("无匹配答案,随机选择", "info"); const randomCount = isMulti ? Math.floor(Math.random() * optionList.length) + 1 : 1; const randomOptions = [...optionList].sort(() => 0.5 - Math.random()).slice(0, randomCount); randomOptions.forEach(opt => { opt.el.click(); selected.push(opt.text); }); } addLog(`已选择:${selected.join("、")}`); // 提交答案 await new Promise(r => setTimeout(r, 800 + Math.random() * 500)); const submitBtns = popup.querySelectorAll('.submit-btn, .confirm-btn, .el-button--primary, button:has-text("确定"), button:has-text("提交")'); submitBtns.forEach(btn => { if (btn.offsetParent !== null) btn.click(); }); addLog("已提交答案,关闭弹窗", "success"); setTimeout(() => { popup.dataset.processed = "false"; }, 2000); } // 言溪题库搜题 async function searchYanxiBank(title, options, type) { return new Promise((resolve) => { const queryParams = new URLSearchParams({ token: YANXI_BANK_CONFIG.token, title: title.trim(), options: options.join("\n"), type: type }); GM_xmlhttpRequest({ method: "GET", url: `${YANXI_BANK_CONFIG.url}?${queryParams.toString()}`, timeout: 5000, onload: (res) => { try { const result = JSON.parse(res.responseText); if (result.code === 0 && result.data?.answer) { let answer = result.data.answer; answer = Array.isArray(answer) ? answer : [answer.toString().trim()]; addLog(`【言溪题库】搜题成功`, "success"); resolve(answer); } else { resolve([]); } } catch (e) { resolve([]); } }, onerror: () => resolve([]), ontimeout: () => resolve([]) }); }); } // ===================== 工具函数 ===================== // 清空所有定时器 function clearAllTimer() { if (timer) clearInterval(timer); if (popupTimer) clearInterval(popupTimer); if (answerTimer) clearInterval(answerTimer); } // 更新UI function updateUI() { const statusText = document.getElementById("statusText"); const courseText = document.getElementById("courseText"); const timeText = document.getElementById("timeText"); const progressFill = document.getElementById("progressFill"); if (statusText) statusText.textContent = state.isRunning ? "运行中" : "已停止"; if (courseText) courseText.textContent = `${state.currentCourseIndex + 1}/${state.totalCourses}`; if (timeText) timeText.textContent = `${formatTime(state.currentTime)} / ${config.watchTime}分钟`; if (progressFill) progressFill.style.width = `${Math.min((state.currentTime / (config.watchTime * 60)) * 100, 100)}%`; } // 格式化时间 function formatTime(seconds) { const m = Math.floor(seconds / 60).toString().padStart(2, "0"); const s = (seconds % 60).toString().padStart(2, "0"); return `${m}:${s}`; } // 日志 function addLog(msg, type = "info") { console.log(`[智慧树助手] ${msg}`); } // 防检测真人模拟 function startHumanSimulate() { setInterval(() => { if (!state.isRunning) return; document.dispatchEvent(new MouseEvent("mousemove", { clientX: Math.random() * 1000 + 200, clientY: Math.random() * 600 + 100 })); window.scrollBy(0, Math.random() > 0.5 ? 10 : -10); }, 30000 + Math.random() * 20000); } // ===================== 事件绑定 ===================== function bindEvents() { // 刷新课程按钮 document.getElementById("refreshBtn").onclick = refreshCourseList; // 开始按钮 document.getElementById("startBtn").onclick = startWatch; // 暂停按钮 document.getElementById("stopBtn").onclick = stopWatch; } // ===================== 初始化启动 ===================== function init() { loadAllData(); createPanel(); bindEvents(); startPopupMonitor(); startAnswerMonitor(); startHumanSimulate(); // 延迟加载课程,等待页面渲染完成 setTimeout(() => { refreshCourseList(); }, 1500); } // 页面加载完成后启动 if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", init); } else { init(); } })();