// ==UserScript== // @name 极致网课助手 // @namespace http://workbuddy.local/ // @version 2.0.0 // @description 自动播放视频、自动答题。支持 Chrome / Edge / Firefox / 夸克 // @author WorkBuddy // @match *://*.chaoxing.com/* // @match *://*.edu.cn/* // @match *://*.xueyinonline.com/* // @match *://*.zhihuishu.com/* // @match *://*.icourse163.org/* // @match *://*.icve.com.cn/* // @match *://*.zjy2.icve.com.cn/* // @match *://*.mooc.icve.com.cn/* // @match *://*.course.icve.com.cn/* // @grant none // @run-at document-idle // ==UserScript== (function() { 'use strict'; const host = location.hostname; let platform = 'unknown'; if (/\\.chaoxing\\.com$/.test(host) || host.endsWith('.edu.cn') || host.endsWith('.xueyinonline.com')) platform = 'cx'; else if (/\\.zhihuishu\\.com$/.test(host)) platform = 'zhs'; else if (/\\.icourse163\\.org$/.test(host)) platform = 'icourse'; else if (/\\.zjy2\\.icve\\.com\\.cn$/.test(host)) platform = 'zjy'; else if (/\\.mooc\\.icve\\.com\\.cn$/.test(host)) platform = 'mooc'; else if (/\\.course\\.icve\\.com\\.cn$/.test(host)) platform = 'course'; const cfg = { autoPlay: true, playSpeed: 1.5, skipAds: true, autoNext: true, autoAnswer: true, token: '791e4efc2b2f43f2879aa39696937fc2', apiEndpoint: 'https://tk.enncy.cn/query' }; const log = (...a) => console.log('[极致网课助手]', ...a); const sleep = ms => new Promise(r => setTimeout(r, ms)); function qsd(sel, doc = document) { let el = doc.querySelector(sel); if (el) return el; for (const iframe of doc.querySelectorAll('iframe')) { try { const sub = iframe.contentDocument || iframe.contentWindow?.document; if (sub) { el = qsd(sel, sub); if (el) return el; } } catch(e) {} } return null; } function findVideo() { if (platform === 'icourse') return document.querySelector('.mooc-video-player video, video'); return qsd('video'); } function setSpeed(v, s) { if (!v) return; try { v.playbackRate = s; } catch(e) {} try { const p = window.videojs && window.videojs(v.id || v); if (p && typeof p.playbackRate === 'function') p.playbackRate(s); } catch(e) {} } async function answerQuestion() { if (!cfg.autoAnswer) return; const title = qsd('.question-text, .question, [class*="question"]')?.textContent?.trim(); if (!title) return; const opts = []; document.querySelectorAll('input[type="radio"], input[type="checkbox"], .option').forEach(el => { const t = (el.textContent || el.value || '').trim(); if (t) opts.push(t); }); log('题目:', title.substring(0, 50)); try { const res = await new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('POST', cfg.apiEndpoint); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(e); } }; xhr.onerror = () => reject(new Error('请求失败')); xhr.send(JSON.stringify({token: cfg.token, title, options: opts.join('\\n'), type: 'single', v: '2'})); }); if (res.code === 0) { log('搜不到答案'); return; } const answer = res.data?.answer; if (!answer) return; log('答案:', answer); await sleep(500); const answers = answer.split('#'); const optionEls = document.querySelectorAll('input[type="radio"], input[type="checkbox"], .option'); for (const ans of answers) { const target = ans.trim().toUpperCase(); for (const el of optionEls) { const text = (el.textContent || el.value || '').trim().toUpperCase(); if (text.startsWith(target) || text.includes(target)) { if (el.tagName === 'INPUT') { el.checked = true; el.dispatchEvent(new Event('change', {bubbles: true})); } else el.click(); log('已选:', target); break; } } } await sleep(500); const submitBtn = qsd('.submit, .submit-btn, #submit, [class*="submit"]'); if (submitBtn) { submitBtn.click(); log('已提交'); } } catch(e) { log('答题失败:', e); } } function nextChapter() { switch(platform) { case 'cx': { const chs = document.querySelectorAll('.posCatalog_level .posCatalog_select'); if (!chs.length) return false; let idx = -1; chs.forEach((c,i) => { if (c.classList.contains('posCatalog_active')) idx = i; }); if (idx < 0 || idx >= chs.length - 1) return false; const next = chs[idx+1]; (next.querySelector('.posCatalog_name') || next).click(); return true; } case 'zhs': { const list = document.querySelectorAll('#chapterList li'); if (!list.length) return false; let idx = -1; list.forEach((li,i) => { if (li.classList.contains('active')) idx = i; }); if (idx < 0 || idx >= list.length - 1) return false; list[idx+1].click(); return true; } case 'icourse': { const pts = document.querySelectorAll('.sourceList .f-icon.lsicon[data-cid]'); if (!pts.length) return false; let idx = -1; pts.forEach((p,i) => { if (p.classList.contains('learned')) idx = i; }); if (idx < 0 || idx >= pts.length - 1) return false; pts[idx+1].click(); return true; } case 'zjy': case 'mooc': case 'course': { const cur = qsd('.s_pointerct'); if (!cur) return false; let next = cur.nextElementSibling; if (!next) { const p = cur.parentElement; if (p) { next = p.nextElementSibling; if (next) next = next.querySelector('.s_pointerct') || next; } } if (next) { next.click(); return true; } return false; } default: return false; } } async function main() { log('平台识别中...'); const video = findVideo(); if (video) { log('找到视频,设置速度', cfg.playSpeed); setSpeed(video, cfg.playSpeed); if (cfg.autoPlay) video.play().catch(e => log('播放失败:', e)); video.addEventListener('ended', async () => { log('视频完毕,切换下一节'); await sleep(2000); nextChapter(); }); if (cfg.skipAds) { video.addEventListener('timeupdate', () => { if (video.currentTime < 5) video.playbackRate = cfg.playSpeed; }); } } await answerQuestion(); let lastUrl = location.href; const obs = new MutationObserver(() => { if (location.href !== lastUrl) { lastUrl = location.href; log('页面变化,重新答题'); setTimeout(() => answerQuestion(), 1500); } }); obs.observe(document.body, { childList: true, subtree: true }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', main); } else { main(); } })();