// ==UserScript== // @name 自动刷课脚本 // @namespace https://github.com/ // @version 1.0 // @description 自动播放视频、切换课程、处理弹窗,最高支持16倍速 // @author User // @match *://*.chaoxing.com/* // @grant none // ==/UserScript== (function() { 'use strict'; const userSettings = { playbackRate: 16, autoAnswer: false }; const config = { checkInterval: 2000, videoSelectors: [ 'video', 'video.player', '.video-container video', '.play-video video', '#video', '.video-player video' ], nextButtonSelectors: [ '.next-btn', '.next-video', '.next-lesson', 'button.next', 'a.next', '.next', '.continue', '.go-next', '[data-next]', 'button:contains("下一节")', 'button:contains("下一课")', 'button:contains("下一章")', 'button:contains("继续学习")', 'a:contains("下一节")', 'a:contains("下一课")', 'a:contains("下一章")', 'a:contains("继续学习")', '.btn-next', '#btn-next' ], popupSelectors: [ '.modal', '.popup', '.dialog', '.alert', '.confirm', '.modal-dialog', '.popup-container' ], closeButtonSelectors: [ '.close', '.btn-close', '.modal-close', '.popup-close', 'button.close', '.confirm-btn', '.ok-btn', '.btn-confirm', '.btn-ok', 'button:contains("确定")', 'button:contains("确认")', 'button:contains("关闭")', 'button:contains("继续")', 'button:contains("知道了")', 'button:contains("我知道了")', 'span.close', '.close-icon' ] }; function waitForPageLoad() { return new Promise((resolve) => { if (document.readyState === 'complete') { resolve(); } else { window.addEventListener('load', resolve); } }); } function findElement(selectors) { for (const selector of selectors) { let element; try { if (selector.includes(':contains(')) { const text = selector.match(/:contains\("(.*?)"\)/)[1]; const baseSelector = selector.replace(/:contains\(.*?\)/, ''); const elements = document.querySelectorAll(baseSelector || '*'); element = Array.from(elements).find(el => el.textContent.includes(text)); } else { element = document.querySelector(selector); } } catch (e) { element = document.querySelector(selector); } if (element) return element; } return null; } function handleVideo() { const video = findElement(config.videoSelectors); if (!video) return; if (video.playbackRate !== userSettings.playbackRate) { video.playbackRate = userSettings.playbackRate; } if (!video.muted) { video.muted = true; } if (video.paused && !video.ended) { video.play().catch(() => {}); } if (video.ended) { handleNextLesson(); } } function handleQuiz() { if (!userSettings.autoAnswer) return; const options = document.querySelectorAll('input[type="radio"], input[type="checkbox"], .answer-option, .quiz-option'); options.forEach((option, index) => { if (index === 0) { option.click(); } }); const submitBtn = findElement(['.submit-btn', 'button:contains("提交")', 'button:contains("确定")', '.quiz-submit']); if (submitBtn) { submitBtn.click(); } } function handleNextLesson() { const nextBtn = findElement(config.nextButtonSelectors); if (nextBtn) { nextBtn.click(); } } function handlePopups() { const popups = document.querySelectorAll(config.popupSelectors.join(',')); popups.forEach(popup => { if (getComputedStyle(popup).display !== 'none') { const closeBtn = findElement(config.closeButtonSelectors); if (closeBtn) { closeBtn.click(); } } }); } function runLoop() { handleVideo(); handlePopups(); handleQuiz(); } function showControlPanel() { const modalStyle = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: #ffffff; border-radius: 16px; box-shadow: 0 25px 80px rgba(0, 0, 0, 0.18); z-index: 99999; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; min-width: 340px; max-width: 420px; animation: fadeInScale 0.3s ease-out; overflow: hidden; `; const headerStyle = ` background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px 24px; text-align: center; `; const titleStyle = ` font-size: 18px; font-weight: 600; margin: 0; `; const bodyStyle = ` padding: 24px; `; const sectionStyle = ` margin-bottom: 20px; `; const labelStyle = ` display: block; font-size: 14px; font-weight: 500; color: #333; margin-bottom: 10px; `; const selectStyle = ` width: 100%; padding: 10px 14px; border: 1px solid #e0e0e0; border-radius: 8px; font-size: 14px; background: white; cursor: pointer; outline: none; transition: border-color 0.2s; `; const toggleContainerStyle = ` display: flex; align-items: center; justify-content: space-between; `; const toggleStyle = ` width: 48px; height: 26px; border-radius: 13px; background: #e0e0e0; cursor: pointer; position: relative; transition: background 0.2s; `; const toggleKnobStyle = ` width: 22px; height: 22px; border-radius: 50%; background: white; position: absolute; top: 2px; left: 2px; transition: transform 0.2s; box-shadow: 0 2px 4px rgba(0,0,0,0.1); `; const buttonContainerStyle = ` display: flex; gap: 10px; margin-top: 24px; `; const buttonStyle = ` flex: 1; padding: 12px; border: none; border-radius: 8px; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.2s; `; const overlayStyle = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); z-index: 99998; animation: fadeIn 0.3s ease-out; `; const cssAnimation = ` `; const overlay = document.createElement('div'); overlay.setAttribute('style', overlayStyle); const modal = document.createElement('div'); modal.setAttribute('style', modalStyle); const header = document.createElement('div'); header.setAttribute('style', headerStyle); const title = document.createElement('h2'); title.setAttribute('style', titleStyle); title.textContent = '🚀 自动刷课控制面板'; header.appendChild(title); const body = document.createElement('div'); body.setAttribute('style', bodyStyle); const speedSection = document.createElement('div'); speedSection.setAttribute('style', sectionStyle); const speedLabel = document.createElement('label'); speedLabel.setAttribute('style', labelStyle); speedLabel.textContent = '播放倍速'; speedSection.appendChild(speedLabel); const speedSelect = document.createElement('select'); speedSelect.setAttribute('style', selectStyle); speedSelect.className = 'control-panel-select'; const speeds = [0.5, 1, 1.25, 1.5, 2, 4, 8, 16]; speeds.forEach(speed => { const option = document.createElement('option'); option.value = speed; option.textContent = `${speed}x`; if (speed === userSettings.playbackRate) { option.selected = true; } speedSelect.appendChild(option); }); speedSection.appendChild(speedSelect); body.appendChild(speedSection); const quizSection = document.createElement('div'); quizSection.setAttribute('style', sectionStyle); const quizLabel = document.createElement('label'); quizLabel.setAttribute('style', toggleContainerStyle); quizLabel.innerHTML = '自动答题'; const toggle = document.createElement('div'); toggle.setAttribute('style', toggleStyle); toggle.className = `control-panel-toggle ${userSettings.autoAnswer ? 'active' : ''}`; const knob = document.createElement('div'); knob.setAttribute('style', toggleKnobStyle); knob.className = 'control-panel-knob'; toggle.appendChild(knob); toggle.addEventListener('click', () => { toggle.classList.toggle('active'); }); quizLabel.appendChild(toggle); quizSection.appendChild(quizLabel); body.appendChild(quizSection); const buttonContainer = document.createElement('div'); buttonContainer.setAttribute('style', buttonContainerStyle); const saveBtn = document.createElement('button'); saveBtn.setAttribute('style', buttonStyle); saveBtn.className = 'control-panel-btn-primary'; saveBtn.textContent = '保存设置'; const closeBtn = document.createElement('button'); closeBtn.setAttribute('style', buttonStyle); closeBtn.className = 'control-panel-btn-secondary'; closeBtn.textContent = '关闭'; buttonContainer.appendChild(saveBtn); buttonContainer.appendChild(closeBtn); body.appendChild(buttonContainer); modal.appendChild(header); modal.appendChild(body); saveBtn.addEventListener('click', () => { userSettings.playbackRate = parseFloat(speedSelect.value); userSettings.autoAnswer = toggle.classList.contains('active'); document.body.removeChild(overlay); document.body.removeChild(modal); }); closeBtn.addEventListener('click', () => { document.body.removeChild(overlay); document.body.removeChild(modal); }); document.head.insertAdjacentHTML('beforeend', cssAnimation); document.body.appendChild(overlay); document.body.appendChild(modal); } async function init() { await waitForPageLoad(); showControlPanel(); setInterval(runLoop, config.checkInterval); runLoop(); } init(); })();