// ==UserScript== // @name 中国宝武学习系统-全能版本学习助手【多账户、多模式】 // @namespace https://learn.baowugroup.com/ // @version 7.8.0 // @description 宝武学习平台全自动挂机:多账号托管、API扫描四专区、自动播放静音倍速、算术验证码重试刷新、未及格重播、自动点播放按钮 // @author 令狐不太冲 // @icon https://learn.baowugroup.com/favicon.png // @license MIT // @match https://learn.baowugroup.com/* // @grant GM_setValue // @grant GM_getValue // @require https://cdn.jsdelivr.net/npm/tesseract.js@5.0.5/dist/tesseract.min.js // @run-at document-idle // ==/UserScript== (function () { 'use strict'; const STORE = { get: (k,d)=>GM_getValue(k,d), set: (k,v)=>GM_setValue(k,v) }; const S = { running: STORE.get('bw_running', false), queue: STORE.get('bw_queue', []), queueIdx: STORE.get('bw_queueIdx', 0), accounts: STORE.get('bw_accounts', []), currentAccIdx: STORE.get('bw_currentAccIdx', 0), zones: {}, tab: 'zone', speed: STORE.get('bw_speed', 1.0), scoreInterval: STORE.get('bw_scoreInterval', 30), logEl: null, studyTimer: null, chapterTimer: null, lastScoreCheck: 0, retryCount: 0, doneToday: 0, totalToday: 0, ocr: null, }; const $ = (s,r=document)=>r.querySelector(s); const $$ = (s,r=document)=>Array.from(r.querySelectorAll(s)); const sleep = ms=>new Promise(r=>setTimeout(r,ms)); const now = ()=>{const d=new Date();return `[${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}:${String(d.getSeconds()).padStart(2,'0')}]`;}; function log(msg, type='info') { const c = {info:'#4fc3f7',ok:'#81c784',warn:'#ffb74d',err:'#e57373',run:'#ce93d8'}; const line = document.createElement('div'); line.style.cssText = `color:${c[type]||'#ccc'};font-size:12px;line-height:1.6;`; line.textContent = `${now()} ${msg}`; if (S.logEl) { S.logEl.appendChild(line); S.logEl.scrollTop = S.logEl.scrollHeight; while (S.logEl.children.length>300) S.logEl.removeChild(S.logEl.firstChild); } console.log(`[BW] ${msg}`); } function route() { const h = location.hash; if (h.includes('/auth/login')) return 'login'; if (h.includes('/zoneMore')) return 'zone'; if (h.includes('/courseStudy')) return 'study'; return 'home'; } function getToken() { try { const k = Object.keys(localStorage).find(x=>x.includes('core-access')); if (k) return JSON.parse(localStorage.getItem(k)).accessToken; } catch(e) {} return null; } async function api(url, body) { const token = getToken(); if (!token) throw new Error('未登录'); const r = await fetch('/learn-gateway'+url, {method:'POST', headers:{'Content-Type':'application/json','Authorization':'Bearer '+token,'token':token}, body: JSON.stringify(body||{})}); return await r.json(); } async function fetchZones(classType) { if (classType === 'OCE') { const all = []; for (let p=1; p<=10; p++) { const j = await api('/service/tms/ols/student/queryPageOpenClass', { current: p, size: 50, data: {learnStatus:"", searchInfo:"", searchType:"1", sortClass:"2", sortType:"desc"} }); if (!j.isSuccess || !j.data?.records?.length) break; j.data.records.forEach(r => all.push({ name: r.courseName, guid: r.courseGuid, courseNo: r.courseNo, centerCode: r.centerCode, olClassType: 'OCE', learnStatus: (r.courseSum && r.courseSum > 0) ? '2' : '0', isOpenCourse: true })); if (all.length >= j.data.total) break; } return all; } else { const all = []; for (let p=1; p<=10; p++) { const j = await api('/service/tms/ols/student/myClassPage', { current: p, size: 50, data: {classType, isLearnNum:"1", keyWord:"", lastLearnTime:"1", learnStatus:"", sortClass:"2", sortType:"desc", status:""} }); if (!j.isSuccess || !j.data?.records?.length) break; j.data.records.forEach(r => all.push({ name: r.olClassName, guid: r.olClassNo, olClassNo: r.olClassNo, centerCode: r.centerCode, olClassType: r.olClassType || classType, courseNum: r.courseNum, learnNum: r.learnNum, isOpenCourse: false })); if (all.length >= j.data.total) break; } return all; } } async function fetchZoneCourses(zone) { const all = []; const olClassNo = zone.olClassNo || zone.guid; const olClassType = zone.olClassType || 'ZE0'; const centerCode = zone.centerCode || 'C001'; for (let p=1; p<=20; p++) { const j = await api('/service/tms/ols/onlineClassCourse/getOnlineClassCourseSortPage', { current: p, size: 100, data: {centerCode, courseName:'', courseTypeCode:'', isMine:'1', isRecursiveCourse:'1', olClassNo, olClassType} }); if (!j.isSuccess || !j.data?.records?.length) break; j.data.records.forEach(r => all.push({ guid: r.guid, courseNo: r.courseNo, courseName: r.courseName, learnStatus: r.learnStatus, isMustTeach: r.isMustTeach, centerCode: r.centerCode, olClassNo: r.olClassNo, olClassType: r.olClassType })); if (all.length >= j.data.total) break; } return all; } function createPanel() { if ($('#bw-helper')) return; const p = document.createElement('div'); p.id = 'bw-helper'; p.innerHTML = `
宝武学习助手 v7.8 待机
今日进度: 0 / 0
公开课
学习专区
网络专题班
培训班
倍速: x 检验合格:
账号托管
`; document.body.appendChild(p); S.logEl = $('#bw-log'); $('#bw-mn').onclick = () => p.classList.toggle('off'); $('#bw-hdr').addEventListener('mousedown', e => { let dx=e.clientX-p.offsetLeft, dy=e.clientY-p.offsetTop, dr=true; const mv=ev=>{if(dr){p.style.left=(ev.clientX-dx)+'px';p.style.top=(ev.clientY-dy)+'px';p.style.right='auto';p.style.bottom='auto';}}; const up=()=>{dr=false;document.removeEventListener('mousemove',mv);document.removeEventListener('mouseup',up);}; document.addEventListener('mousemove',mv);document.addEventListener('mouseup',up); }); $$('.tab').forEach(t=>t.onclick=()=>{$$('.tab').forEach(x=>x.classList.remove('on'));t.classList.add('on');S.tab=t.dataset.t;renderList();}); $('#bw-refresh').onclick = ()=>{ loadAllZones(true); }; $('#bw-all').onclick = ()=>$$('#bw-cl input[type=checkbox]').forEach(c=>{if(!c.closest('.ci').querySelector('.cs').classList.contains('done'))c.checked=true;}); $('#bw-go').onclick = startLearning; $('#bw-stop').onclick = stopLearning; $('#bw-speed').onchange = ()=>{S.speed=parseFloat($('#bw-speed').value)||1.0;STORE.set('bw_speed',S.speed);log('倍速'+S.speed+'x');}; $('#bw-score-interval').onchange = ()=>{S.scoreInterval=parseInt($('#bw-score-interval').value)||30;STORE.set('bw_scoreInterval',S.scoreInterval);log('检验间隔'+S.scoreInterval+'秒');}; $('#bw-add-acc').onclick = addAccount; $('#bw-host').onclick = startHosting; renderAccList(); } function setStatus(t,c){const e=$('#bw-st');if(e){e.textContent=t;e.style.color=c||'#f9e2af';}} function setProg(p){const b=$('#bw-pb');if(b)b.style.width=Math.min(100,Math.max(0,p))+'%';} function setProgressText(done,total){const e=$('#bw-prog-text');if(e)e.textContent=`今日进度: ${done} / ${total}`;} function addAccount() { const acc = $('#bw-acc').value.trim(); const pwd = $('#bw-pwd').value; if (!acc || !pwd) { log('请输入工号和密码','warn'); return; } const zones = $$('#bw-zones-check input:checked').map(c=>c.value); S.accounts.push({acc, pwd, zones}); STORE.set('bw_accounts', S.accounts); $('#bw-acc').value=''; $('#bw-pwd').value=''; renderAccList(); log(`账号${acc}已添加 (${zones.join(',')})`,'ok'); } function renderAccList() { const el = $('#bw-acc-list'); if (!el) return; el.innerHTML = ''; S.accounts.forEach((a, i) => { const d = document.createElement('div'); d.className = 'acc-item'; d.innerHTML = `${i+1}. ${a.acc}${a.zones.join('/')}删除`; d.querySelector('.del').onclick = ()=>{ S.accounts.splice(i,1); STORE.set('bw_accounts',S.accounts); renderAccList(); }; el.appendChild(d); }); } const TAB_CLASS = {open:'OCE', zone:'ZE0', online:'ZE1', train:'TCE'}; const TAB_NAME = {open:'公开课', zone:'学习专区', online:'网络专题班', train:'培训班'}; async function loadAllZones(force) { if (route() !== 'home') { location.hash='#/userCenter'; await sleep(5000); } log('后台API读取各专区...','run'); setStatus('读取中','#89b4fa'); for (const [key, ct] of Object.entries(TAB_CLASS)) { if (!force && S.zones[key]?.length) continue; try { const list = await fetchZones(ct); S.zones[key] = list; log(`${TAB_NAME[key]}: ${list.length}个`); } catch(e) { log(`${TAB_NAME[key]}读取失败: ${e.message}`,'err'); } } renderList(); setStatus('待机','#f9e2af'); log('读取完成','ok'); } function renderList() { const list = S.zones[S.tab] || []; const cl = $('#bw-cl'); cl.innerHTML = ''; if (!list.length) { cl.innerHTML = '
暂无数据
'; return; } list.forEach((item, i) => { let st='', cls='todo'; if (item.isOpenCourse) { st = item.learnStatus==='2'?'已完成':'未学习'; cls = item.learnStatus==='2'?'done':'todo'; } else { const total=parseInt(item.courseNum)||0, done=parseInt(item.learnNum)||0; if(total>0&&done>=total){st='已完成';cls='done';} else if(done>0){st=`学习中 ${done}/${total}`;cls='doing';} else{st='未学习';cls='todo';} } const d=document.createElement('div'); d.className='ci'; d.innerHTML=`${item.name}${st}`; cl.appendChild(d); }); } async function startHosting() { if (!S.accounts.length) { log('请先添加账号','warn'); return; } S.running = true; STORE.set('bw_running', true); S.currentAccIdx = 0; setStatus('托管中','#a6e3a1'); log(`托管开始,共${S.accounts.length}个账号`,'run'); await hostNextAccount(); } async function getCurrentLoginAcc() { try { if (!getToken()) return null; const allText = document.body.innerText || ''; const m = allText.match(/(\d{6,10})/); return m ? m[1] : null; } catch(e) { return null; } } async function hostNextAccount() { if (!S.running) return; if (S.currentAccIdx >= S.accounts.length) { setStatus('托管完成','#a6e3a1'); log('所有账号托管完成!','ok'); STORE.set('bw_running', false); return; } const acc = S.accounts[S.currentAccIdx]; log(`=== 账号 ${S.currentAccIdx+1}/${S.accounts.length}: ${acc.acc} ===`, 'run'); const curRoute = route(); if (curRoute === 'login') { log('当前已在登录页,直接登录'); await doLoginWithAccount(acc); } else { const currentAcc = await getCurrentLoginAcc(); if (currentAcc && String(currentAcc) === String(acc.acc)) { log(`当前已是账号${acc.acc},跳过退出登录`,'ok'); await sleep(3000); } else { log(`当前账号${currentAcc||'未知'} ≠ ${acc.acc},需要切换`,'warn'); await logoutAndLogin(acc); await sleep(3000); } } S.zones = {}; await loadAllZones(true); const allCourses = []; for (const zk of acc.zones) { const list = S.zones[zk] || []; for (const item of list) { if (item.isOpenCourse) { if (item.learnStatus !== '2') allCourses.push(item); } else { log(`扫描: ${item.name}...`); try { const courses = await fetchZoneCourses(item); courses.forEach(c=>{if(c.learnStatus!=='2')allCourses.push({...c,zoneName:item.name});}); } catch(e) { log(`扫描${item.name}失败`,'err'); } } } } S.queue = allCourses; S.queueIdx = 0; S.doneToday = 0; S.totalToday = allCourses.length; STORE.set('bw_queue', S.queue); STORE.set('bw_queueIdx', 0); setProgressText(0, allCourses.length); log(`共${allCourses.length}门待学课程`,'ok'); if (allCourses.length === 0) { log('该账号无待学课程,切换下一账号','warn'); S.currentAccIdx++; STORE.set('bw_currentAccIdx', S.currentAccIdx); await sleep(2000); await hostNextAccount(); } else { await learnNext(); } } async function logoutAndLogin(acc) { log('退出当前账号...','run'); const avatarBtn = document.querySelector('button.outline-none'); if (avatarBtn) { avatarBtn.click(); await sleep(2000); } const items = document.querySelectorAll('.el-dropdown-menu__item, [role="menuitem"], .el-dropdown-menu li'); for (const item of items) { if (item.textContent.includes('退出登录') && item.offsetParent !== null) { item.click(); log('点了退出登录'); break; } } await sleep(2000); const confirmBtns = document.querySelectorAll('.el-message-box button, .el-dialog__footer button, .el-button--primary'); for (const btn of confirmBtns) { if (btn.offsetParent !== null && btn.textContent.trim().match(/确定|确认|退出/)) { btn.click(); log('确认退出弹窗: 点确定'); break; } } await sleep(4000); location.hash = '#/auth/login'; await sleep(5000); await doLoginWithAccount(acc); } function solveMath(str) { // 只取=前面的表达式,=后面是?(OCR会误识别成7) let expr = str.split(/[==??]/)[0]; // OCR把+识别成¥,替换回来 expr = expr.replace(/[¥¥]/g, '+'); // 去掉所有非数字非运算符字符([]等噪声) expr = expr.replace(/[^\d+\-×xX*÷/]/g, ''); const m = expr.match(/(\d+)\s*([+\-×xX*÷/])\s*(\d+)/); if (!m) return null; const a=parseInt(m[1]),op=m[2],b=parseInt(m[3]); if(op==='+')return a+b; if(op==='-')return a-b; if(op==='×'||op==='x'||op==='X'||op==='*')return a*b; if(op==='÷'||op==='/')return b?Math.round(a/b):null; return null; } async function ocrCaptcha() { const img = document.querySelector('img.captcha-image'); if (!img || !img.src) return null; const canvas = document.createElement('canvas'); canvas.width = img.naturalWidth * 5; canvas.height = img.naturalHeight * 5; const ctx = canvas.getContext('2d'); ctx.fillStyle = 'white'; ctx.fillRect(0,0,canvas.width,canvas.height); ctx.drawImage(img, 0, 0, canvas.width, canvas.height); const d = ctx.getImageData(0,0,canvas.width,canvas.height); for (let i=0; i{}}); } await S.ocr.setParameters({tessedit_pageseg_mode:'7'}); const {data} = await S.ocr.recognize(canvas.toDataURL()); return (data.text||'').trim(); } async function doLoginWithAccount(acc) { log('登录: ' + acc.acc, 'run'); let inputs = []; for (let i=0; i<10; i++) { inputs = $$('input.el-input__inner'); if (inputs.length >= 3) break; await sleep(1000); } if (inputs.length < 3) { log('登录页输入框未加载','err'); return; } inputs[0].focus(); inputs[0].value = acc.acc; inputs[0].dispatchEvent(new Event('input',{bubbles:true})); inputs[0].dispatchEvent(new Event('change',{bubbles:true})); await sleep(300); inputs[1].focus(); inputs[1].value = acc.pwd; inputs[1].dispatchEvent(new Event('input',{bubbles:true})); inputs[1].dispatchEvent(new Event('change',{bubbles:true})); await sleep(300); for (let attempt = 1; attempt <= 15; attempt++) { await sleep(1000); let text = ''; try { text = await ocrCaptcha(); } catch(e) { log('OCR异常: '+e.message,'err'); } log(`验证码OCR(第${attempt}次): "${text}"`); const ans = solveMath(text); if (ans !== null) { const capInput = $$('input.el-input__inner')[2]; if (capInput) { capInput.focus(); capInput.value = String(ans); capInput.dispatchEvent(new Event('input',{bubbles:true})); capInput.dispatchEvent(new Event('change',{bubbles:true})); log('填入验证码: ' + ans); } await sleep(500); const btns = Array.from(document.querySelectorAll('button')); for (const b of btns) { if (b.textContent.trim() === '登录' && b.offsetParent !== null) { b.click(); break; } } log('点了登录,等待响应...'); await sleep(4000); if (!location.hash.includes('/auth/login')) { log('登录成功!','ok'); return; } log('验证码错误,自动重试...','warn'); await sleep(1000); } else { log('验证码识别失败,点击刷新','warn'); const img = document.querySelector('img.captcha-image'); if (img) img.click(); await sleep(1500); } } log('验证码重试15次仍未成功','err'); } async function startLearning() { const checked = $$('#bw-cl input[type=checkbox]:checked'); if (!checked.length) { log('请先勾选','warn'); return; } setStatus('加载中','#89b4fa'); const allCourses = []; for (const cb of checked) { const i = parseInt(cb.dataset.i); const item = (S.zones[S.tab]||[])[i]; if (!item) continue; if (item.isOpenCourse) { if(item.learnStatus!=='2')allCourses.push(item); } else { log(`扫描: ${item.name}...`); try { const courses = await fetchZoneCourses(item); courses.forEach(c=>{if(c.learnStatus!=='2')allCourses.push({...c,zoneName:item.name});}); } catch(e) { log(`扫描失败: ${e.message}`,'err'); } } } if (!allCourses.length) { log('无未完成课程','warn'); setStatus('待机'); return; } allCourses.sort((a,b)=> (a.learnStatus==='1'?0:1)-(b.learnStatus==='1'?0:1)); S.queue = allCourses; S.queueIdx = 0; S.running = true; STORE.set('bw_running',true); STORE.set('bw_queue',S.queue); STORE.set('bw_queueIdx',0); S.doneToday = 0; S.totalToday = allCourses.length; setProgressText(0, allCourses.length); setStatus('学习中','#a6e3a1'); log(`开始学习${allCourses.length}门课程`,'run'); learnNext(); } function stopLearning() { S.running=false; STORE.set('bw_running',false); if(S.studyTimer)clearInterval(S.studyTimer); if(S.chapterTimer)clearInterval(S.chapterTimer); setStatus('已停止','#f9e2af'); log('已停止','warn'); } async function learnNext() { if (!S.running) return; if (S.queueIdx >= S.queue.length) { log(`本账号完成: ${S.doneToday}门`,'ok'); if (S.accounts.length > 1 && S.currentAccIdx < S.accounts.length) { S.currentAccIdx++; STORE.set('bw_currentAccIdx',S.currentAccIdx); await sleep(2000); await hostNextAccount(); } else { setStatus('全部完成','#a6e3a1'); log('全部学习完毕!','ok'); STORE.set('bw_running',false); } return; } const c = S.queue[S.queueIdx]; S.retryCount = 0; log(`=== ${S.queueIdx+1}/${S.queue.length}: ${c.courseName} ===`, 'run'); location.assign(`https://learn.baowugroup.com/#/courseStudy?centerCode=${c.centerCode}&guid=${c.guid}`); } function playCurrentVideo(video) { if (!video || video.ended || !video.paused) return false; const strategies = [ { sel: '.xgplayer-start', name: '中央播放按钮' }, { sel: '.xgplayer-play .xgplayer-icon', name: '控制栏播放图标' }, { sel: '#xg-video, .xgplayer', name: '视频画面区域' }, ]; for (const { sel, name } of strategies) { const el = document.querySelector(sel); if (el && el.offsetParent) { el.click(); log(`点击${name}`); return true; } } video.play().catch(()=>{}); log('兜底video.play()'); return true; } function cleanupVideoOverlays() { const start = document.querySelector('.xgplayer-start'); if (start) start.classList.add('hide'); const overlay = document.querySelector('.video-overlay'); if (overlay) overlay.style.display = 'none'; } async function studyCourse() { let v = null; for (let i=0;i<30;i++){ v=$('#xg-video video')||$('video'); if(v&&v.readyState>=1&&v.duration>0)break; await sleep(1000); } if (!v||!v.duration) { log('视频加载失败,重试','err'); setTimeout(studyCourse, 5000); return; } v.muted = true; v.playbackRate = S.speed; v.play().catch(()=>{}); await sleep(1500); playCurrentVideo(v); await sleep(2000); const score = await checkScore(); if (score && score.score>=score.passLine) { log(`已及格:${score.score}分`,'ok'); S.queueIdx++; S.doneToday++; setProgressText(S.doneToday,S.totalToday); STORE.set('bw_queueIdx',S.queueIdx); await sleep(2000); await learnNext(); return; } log(`成绩:${score?score.score:'?'}分 及格线${score?score.passLine:60}`); await playChapter(0); S.studyTimer = setInterval(async ()=>{ if(!S.running){clearInterval(S.studyTimer);return;} handlePopups(); watchdog(); const vv=$('#xg-video video')||$('video'); if(vv&&vv.duration) setProg(vv.currentTime/vv.duration*100); const now=Date.now(); if(now-S.lastScoreCheck>S.scoreInterval*1000){ S.lastScoreCheck=now; const s=await checkScore(); if(s&&s.score>=s.passLine){ log(`及格!${s.score}分`,'ok'); clearInterval(S.studyTimer); S.queueIdx++; S.doneToday++; setProgressText(S.doneToday,S.totalToday); STORE.set('bw_queueIdx',S.queueIdx); await sleep(2000); await learnNext(); } else if(s) { log(`当前${s.score}分/${s.passLine}分`,'info'); } } }, 3000); } async function playChapter(idx) { if (!S.running) return; const leaves = $$('.course-chapter').filter(el=>!el.classList.contains('chapter-parent')); if (!leaves.length) { log('无章节,直接播放'); const v=$('#xg-video video')||$('video'); if(v){v.muted=true;v.playbackRate=S.speed;v.play().catch(()=>{});} if(S.chapterTimer)clearInterval(S.chapterTimer); S.chapterTimer=setInterval(()=>{ const vv=$('#xg-video video')||$('video'); if(!vv)return; if(vv.muted!==true)vv.muted=true; if(Math.abs(vv.playbackRate-S.speed)>0.1)vv.playbackRate=S.speed; if(vv.paused&&!vv.ended){playCurrentVideo(vv);} if(vv.ended||(vv.duration>0&&vv.currentTime>=vv.duration-0.5)){ clearInterval(S.chapterTimer); onVideoEnded(); } },2000); return; } if (idx >= leaves.length) { onVideoEnded(); return; } const ch = leaves[idx]; ch.scrollIntoView({block:'center'}); ch.click(); log(`小节${idx+1}/${leaves.length}: ${ch.textContent.trim().slice(0,40)}`); await sleep(3000); const v=$('#xg-video video')||$('video'); if(v){v.muted=true;v.playbackRate=S.speed;playCurrentVideo(v);} if(S.chapterTimer)clearInterval(S.chapterTimer); S.chapterTimer=setInterval(()=>{ const vv=$('#xg-video video')||$('video'); if(!vv)return; if(vv.muted!==true)vv.muted=true; if(Math.abs(vv.playbackRate-S.speed)>0.1)vv.playbackRate=S.speed; if(vv.paused&&!vv.ended){playCurrentVideo(vv);} if(vv.ended||(vv.duration>0&&vv.currentTime>=vv.duration-0.5)){ clearInterval(S.chapterTimer); log(`小节${idx+1}完毕`); setTimeout(()=>playChapter(idx+1),2000); } },2000); } async function onVideoEnded() { log('全部小节播完,检查成绩...','run'); await sleep(2000); const s = await checkScore(); if (s && s.score >= s.passLine) { log(`及格!${s.score}分`,'ok'); S.queueIdx++; S.doneToday++; setProgressText(S.doneToday,S.totalToday); STORE.set('bw_queueIdx',S.queueIdx); await sleep(2000); await learnNext(); } else { S.retryCount++; log(`未及格(${s?s.score:'?'}分/${s?s.passLine:60}), 第${S.retryCount}次重播`,'warn'); if (S.retryCount >= 5) { log('重播5次仍未及格,跳下一门','err'); S.queueIdx++; STORE.set('bw_queueIdx',S.queueIdx); await sleep(2000); await learnNext(); } else { setTimeout(()=>playChapter(0), 3000); } } } function watchdog(){ const v=$('#xg-video video')||$('video'); if(!v)return; if(v.muted!==true)v.muted=true; if(Math.abs(v.playbackRate-S.speed)>0.1)v.playbackRate=S.speed; if(v.paused&&!v.ended){ playCurrentVideo(v); } if(!v.paused) cleanupVideoOverlays(); } async function checkScore(){ const btn=Array.from($$('button')).find(b=>b.textContent.trim()==='完成情况'); if(!btn)return null; btn.click(); await sleep(5000); const dlg=$('.el-dialog'); if(!dlg)return null; const t=dlg.textContent; const sm=t.match(/课程成绩[::]\s*([\d.]+)\s*分/); const pm=t.match(/合格分数线[::]\s*([\d.]+)/); const r={score:sm?parseFloat(sm[1]):null, passLine:pm?parseFloat(pm[1]):60}; const close=dlg.querySelector('.el-dialog__close, [aria-label="Close"]'); if(close)close.click();else document.body.click(); await sleep(500); return r; } function handlePopups(){ $$('.el-message-box, .el-dialog, [class*="popup"], [class*="modal"]').forEach(el=>{ const r=el.getBoundingClientRect(); if(r.width>100&&el.offsetParent!==null){ const txt=el.textContent; if(txt.includes('休息')||txt.includes('专注学习')){ log('休息弹窗,点继续','warn'); const btns=el.querySelectorAll('button'); for(const b of btns){ const bt=b.textContent.trim(); if(bt==='继续'||bt==='确定'||bt==='知道了'||bt==='确认'){b.click();break;} } } } }); const nextBtn = document.querySelector('.play-chapter button.el-button--primary'); if (nextBtn && nextBtn.offsetParent && !nextBtn.disabled && nextBtn.textContent.includes('播放下一节')) { log('自动点了: 播放下一节'); nextBtn.click(); return; } } async function boot(){ createPanel(); log('宝武学习助手 v7.8 启动','ok'); const r=route(); if(r==='login'){setStatus('登录中','#f38ba8');} else if(r==='home'){setStatus('待机','#f9e2af');loadAllZones(false);} else if(r==='zone'){setStatus('专区页','#89b4fa');} else if(r==='study'){setStatus('学习页','#a6e3a1');if(S.running)await studyCourse();} setInterval(()=>{handlePopups();if(S.running&&route()==='study')watchdog();},2000); window.addEventListener('hashchange',()=>{ setTimeout(async()=>{ if(S.running&&route()==='study')await studyCourse(); if(S.running&&route()==='home'&&S.queue[S.queueIdx]){ const c=S.queue[S.queueIdx]; location.assign(`https://learn.baowugroup.com/#/courseStudy?centerCode=${c.centerCode}&guid=${c.guid}`); } },3000); }); } if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',boot); else boot(); })();