// ==UserScript== // @name 山西焦煤学习-防重复+一键重置+已学统计(修复版) // @namespace http://tampermonkey.net/ // @version 1.0.0 // @description 修复了按钮重复问题,保留防重复刷课、一键清空记录、已学数量统计 // @match *://172.21.12.145/* // @grant none // @run-at document-idle // @noframes // ==/UserScript== (function() { 'use strict'; delete window.Tampermonkey; delete window.GM_info; const cfg = { checkTime: 2500, backDelay: 3500, nextDelay: 5000, reloadWait: 12000 }; // 本地已学记录管理 const STUDIED_KEY = "studied_course_keys"; function getStudiedSet() { try { let str = localStorage.getItem(STUDIED_KEY); return str ? new Set(JSON.parse(str)) : new Set(); } catch (e) { return new Set(); } } function addStudied(key) { if (!key) return; let s = getStudiedSet(); s.add(key); localStorage.setItem(STUDIED_KEY, JSON.stringify(Array.from(s))); } function isStudied(key) { if (!key) return false; return getStudiedSet().has(key); } // 一键清空已学记录 function clearStudied() { localStorage.removeItem(STUDIED_KEY); showLog("🔴 已清空所有已学记录,可换号重刷"); updateStat(); } // 悬浮日志容器 let logBox = document.createElement("div"); logBox.style.position = "fixed"; logBox.style.bottom = "15px"; logBox.style.right = "15px"; logBox.style.background = "rgba(0,0,0,0.8)"; logBox.style.color = "#00ff00"; logBox.style.padding = "12px"; logBox.style.borderRadius = "6px"; logBox.style.fontSize = "13px"; logBox.style.zIndex = "999999"; logBox.style.maxWidth = "320px"; logBox.style.lineHeight = "1.7"; // 统计显示行 let statLine = document.createElement("div"); statLine.style.marginBottom = "8px"; statLine.style.borderBottom = "1px solid #444"; statLine.style.paddingBottom = "6px"; // 重置按钮(只创建一次) let resetBtn = document.createElement("button"); resetBtn.innerText = "重置已学记录"; resetBtn.style.marginTop = "8px"; resetBtn.style.width = "100%"; resetBtn.style.padding = "5px 0"; resetBtn.style.border = "none"; resetBtn.style.borderRadius = "4px"; resetBtn.style.background = "#e53935"; resetBtn.style.color = "#fff"; resetBtn.style.cursor = "pointer"; resetBtn.style.fontSize = "12px"; resetBtn.onclick = clearStudied; logBox.appendChild(statLine); logBox.appendChild(resetBtn); document.body.appendChild(logBox); // 更新统计 function updateStat(){ let set = getStudiedSet(); let count = set.size; statLine.innerHTML = `📊 已学课程:${count} 节`; } function showLog(txt){ const time = new Date().toLocaleTimeString(); const line = `[${time}] ${txt}`; // 取出日志部分,避免每次都重新加按钮 let logs = logBox.querySelectorAll(".log-line"); let logsArr = Array.from(logs).map(el => el.innerHTML); logsArr.unshift(line); if(logsArr.length > 10) logsArr.length = 10; // 清空日志框,重新组装:统计行 + 日志 + 按钮 logBox.innerHTML = ""; logBox.appendChild(statLine); logsArr.forEach(log => { let lineEl = document.createElement("div"); lineEl.className = "log-line"; lineEl.innerHTML = log; logBox.appendChild(lineEl); }); logBox.appendChild(resetBtn); } function wait(ms){ return new Promise(res=>setTimeout(res,ms)); } function hasVideo(){ return !!document.querySelector("video"); } function videoIsOver(){ let v = document.querySelector("video"); if(!v || v.duration === 0) return false; return v.ended || v.currentTime >= v.duration - 2; } async function safePlayVideo(video) { if (!video) return; video.muted = true; video.playbackRate = 1; if(video.paused){ try{ await video.play(); showLog("视频自动播放成功"); }catch(e){ video.click(); showLog("播放拦截,模拟点击播放"); } } } async function backCourse(){ let findBack = false; document.querySelectorAll("a,button").forEach(el=>{ let t = el.innerText.trim(); if(t.includes("返回")){ el.click(); findBack = true; showLog("点击返回课程"); } }); if(!findBack){ history.back(); showLog("无返回按钮,浏览器后退"); } await wait(cfg.backDelay); showLog("已回到课程列表"); } function nextStudy(){ showLog("正在查找下一节可学习课程"); let list = document.querySelectorAll("div,tr"); let target = null; let studyKey = null; for(let item of list){ let txt = item.innerText; if(!txt.includes("%")) continue; let m = txt.match(/(\d+)%/); if(!m || Number(m[1]) >= 100) continue; let key = txt.slice(0, 100).replace(/\s+/g, ""); if (isStudied(key)) { showLog("跳过已学旧课程"); continue; } let btn = item.querySelector("a,button"); if(btn && (btn.innerText.includes("开始学习") || btn.innerText.includes("继续学习"))){ target = btn; studyKey = key; break; } } if(target){ window._currentStudyKey = studyKey; target.click(); showLog("进入新课程学习"); return true; } showLog("✅ 全部课程已刷完"); return false; } async function mainLoop(){ updateStat(); showLog("脚本启动成功,开始自动刷课"); let allDone = false; while(true){ await wait(cfg.checkTime); updateStat(); if(allDone){ await wait(10000); continue; } if(hasVideo()){ let v = document.querySelector("video"); await safePlayVideo(v); if(videoIsOver()){ showLog("本节视频学习完成"); if (window._currentStudyKey) { addStudied(window._currentStudyKey); updateStat(); showLog("已录入已学列表,不再重复"); } await backCourse(); await wait(cfg.nextDelay); showLog("刷新页面加载课程列表..."); window.location.reload(); await wait(cfg.reloadWait); let hasNext = nextStudy(); if(!hasNext){ allDone = true; } } continue; } let hasNext = nextStudy(); if(!hasNext){ allDone = true; } } } setTimeout(()=>{ mainLoop(); },5000); })();