// ==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();
})();