青书学堂自动刷课+自动答题,所有页面控制区域固定在左侧
// ==UserScript== // @name 青书学堂自动刷课与答题增强版 // @version 0.0.2 // @description 青书学堂自动刷课+自动答题,所有页面控制区域固定在左侧 // @author 综合优化 // @match https://*.qingshuxuetang.com/*/CourseShow* // @match https://*.qingshuxuetang.com/*/CourseStudy* // @match https://*.qingshuxuetang.com/*/CourseList* // @match https://*.qingshuxuetang.com/*/Student/Home* // @match https://*.qingshuxuetang.com/*/ExercisePaper* // @match https://*.qingshuxuetang.com/*/ExamPaper* // @match https://qingshuxuetang.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=degree.qingshuxuetang.com // @require https://unpkg.com/[email protected]/dist/web/pxmu.min.js // @require https://lib.baomitu.com/lodash.js/latest/lodash.min.js // @run-at document-end // @grant none // @license MIT // @namespace https://greasyfork.org/users/1473469 // ==/UserScript== ;(function () { 'use strict'; // 检查是否已存在控制区域,避免重复创建 if (document.getElementById('qingshu-script-control-panel')) { return; } // 检查功能是否启用(默认都启用) let autoPlayEnabled = localStorage.getItem('qingshuAutoPlayEnabled') !== 'false'; let autoAnswerEnabled = localStorage.getItem('qingshuAutoAnswerEnabled') !== 'false'; // 如果是从禁用状态切换到启用状态,刷新页面 if ((autoPlayEnabled && localStorage.getItem('qingshuAutoPlayEnabled') === 'false') || (autoAnswerEnabled && localStorage.getItem('qingshuAutoAnswerEnabled') === 'false')) { localStorage.setItem('qingshuAutoPlayEnabled', autoPlayEnabled); localStorage.setItem('qingshuAutoAnswerEnabled', autoAnswerEnabled); location.reload(); return; } // 先处理答题相关页面(仅当自动答题启用时) if (autoAnswerEnabled) { handleAnswerPages(); } // 如果是答题页面且已处理,不执行后续刷课逻辑 if ((location.href.indexOf('ExercisePaper') !== -1 || location.href.indexOf('ExamPaper') !== -1) && autoAnswerEnabled) { return; } // 变量初始化(刷课相关) let video = null; let nextNode = null; let playCheckInterval = null; let findCoursesTimer = null; let findVideoTimer = null; let currentVideoTime = null; const urlParams = new URLSearchParams(window.location.search); const currentNodeId = urlParams.get('nodeId'); const domain = 'https://degree.qingshuxuetang.com/'; const url = location.href; const host = location.host; const isDegree = host.indexOf('degree') > -1; let symbol = null; const PLAYBACK_RATE = 1; // 播放速度 // 创建样式表 - 统一左侧定位 const styleSheet = document.createElement('style'); styleSheet.textContent = ` /* 基础控制面板样式 - 所有页面通用,固定在左侧 */ #qingshu-script-control-panel { display: flex; flex-direction: column; padding: 15px; border-radius: 8px; background: #ffffff; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); transition: all 0.3s ease; z-index: 9999; width: 220px; box-sizing: border-box; border: 1px solid #f0f0f0; position: fixed; /* 固定定位 */ left: 15px; /* 左侧距离 */ top: 50%; /* 垂直居中 */ transform: translateY(-50%); /* 垂直居中调整 */ } #qingshu-script-control-panel:hover { box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15); } .qingshu-status { padding: 8px 12px; border-radius: 4px; color: white; font-weight: 500; font-size: 14px; transition: background 0.3s ease; width: 100%; text-align: center; box-sizing: border-box; margin: 5px 0; } .qingshu-toggle-container { display: flex; align-items: center; gap: 8px; width: 100%; justify-content: space-between; margin: 5px 0; } .qingshu-toggle-label { color: #333; font-weight: 500; font-size: 14px; } /* 开关按钮样式 */ .qingshu-toggle { position: relative; display: inline-block; width: 42px; height: 24px; } .qingshu-toggle input { opacity: 0; width: 0; height: 0; } .qingshu-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #e0e0e0; transition: .3s; border-radius: 24px; } .qingshu-slider:before { position: absolute; content: ""; height: 18px; width: 18px; left: 3px; bottom: 3px; background-color: white; transition: .3s; border-radius: 50%; } input:checked + .qingshu-slider { background-color: #42b983; } input:checked + .qingshu-slider:before { transform: translateX(18px); } /* 答题控制按钮样式 */ .answer-action-buttons { display: flex; flex-direction: column; gap: 10px; width: 100%; margin: 10px 0; } .answer-btn { padding: 8px 12px; border-radius: 4px; border: none; cursor: pointer; font-weight: 500; font-size: 14px; transition: all 0.2s ease; width: 100%; } .retry-btn { background-color: #4285f4; color: white; } .retry-btn:hover { background-color: #3367d6; } /* 所有页面主内容区域都添加左边距,避免被左侧控制面板遮挡 */ .main-content-offset { margin-left: 250px !important; transition: margin-left 0.3s ease; } /* 针对不同页面的主内容容器选择器 */ .container.main-content-offset, .main.main-content-offset, .paper-content.main-content-offset, .course-content.main-content-offset { margin-left: 250px !important; } `; document.head.appendChild(styleSheet); // 创建控制区域容器 - 使用唯一ID确保不会重复 const controlContainer = document.createElement('div'); controlContainer.id = 'qingshu-script-control-panel'; // 唯一ID,防止重复创建 // 放置控制区域 - 统一固定在左侧 function placeControlPanel() { // 所有页面都添加到body document.body.appendChild(controlContainer); // 所有页面主内容区域都添加左边距,避免被遮挡 const mainContentSelectors = [ '.container', '.main', '.paper-content', '.course-content', '#main-content' ]; // 尝试找到主内容容器并添加偏移 mainContentSelectors.some(selector => { const mainContent = document.querySelector(selector); if (mainContent) { mainContent.classList.add('main-content-offset'); return true; } return false; }); } // 创建自动刷课开关 const createToggle = (labelText, isChecked, storageKey) => { const toggleContainer = document.createElement('div'); toggleContainer.className = 'qingshu-toggle-container'; const toggleLabel = document.createElement('span'); toggleLabel.className = 'qingshu-toggle-label'; toggleLabel.textContent = labelText; const toggleWrapper = document.createElement('label'); toggleWrapper.className = 'qingshu-toggle'; const toggleSwitch = document.createElement('input'); toggleSwitch.type = 'checkbox'; toggleSwitch.checked = isChecked; toggleSwitch.onchange = function() { const newState = this.checked; localStorage.setItem(storageKey, newState); if ((storageKey === 'qingshuAutoPlayEnabled' && newState !== autoPlayEnabled) || (storageKey === 'qingshuAutoAnswerEnabled' && newState !== autoAnswerEnabled)) { location.reload(); } }; const slider = document.createElement('span'); slider.className = 'qingshu-slider'; toggleWrapper.appendChild(toggleSwitch); toggleWrapper.appendChild(slider); toggleContainer.appendChild(toggleLabel); toggleContainer.appendChild(toggleWrapper); return toggleContainer; }; // 添加自动刷课开关 controlContainer.appendChild(createToggle('自动刷课', autoPlayEnabled, 'qingshuAutoPlayEnabled')); // 添加自动答题开关 controlContainer.appendChild(createToggle('自动答题', autoAnswerEnabled, 'qingshuAutoAnswerEnabled')); // 创建状态提示元素 const statusDiv = document.createElement('div'); statusDiv.className = 'qingshu-status'; statusDiv.textContent = '初始化中...'; statusDiv.style.background = '#4285f4'; // 谷歌蓝 - 初始状态 controlContainer.appendChild(statusDiv); // 放置控制面板 placeControlPanel(); // 如果两个功能都被禁用,显示状态并退出 if (!autoPlayEnabled && !autoAnswerEnabled) { updateStatus('所有功能已禁用', '#f44336'); // 红色 - 禁用状态 return; } // 获取学校标识 if (location.pathname.split('/').length >= 2) { symbol = location.pathname.split('/')[1]; } // 页面路由处理(仅当自动刷课启用时) if (autoPlayEnabled) { if (!isDegree && url.indexOf('MyQingShu') > -1) { updateStatus('开始执行,请稍后...', '#4285f4'); setTimeout(function() { if (window.viewMap && window.viewMap.student && window.viewMap.student.data) { window.location.href = window.viewMap.student.data[0].entrance; return; } updateStatus('未找到学校信息', '#ff9800'); // 橙色 - 警告 }, 3000); return; } if (!isDegree) { updateStatus('仅支持degree页面', '#f44336'); return; } if (symbol == null) { updateStatus('未找到学校标识', '#f44336'); return; } // 首页处理:跳转到课程列表 if (url.indexOf('Student/Home') > -1) { updateStatus('跳转到课程列表...', '#4285f4'); setTimeout(() => { window.location.href = `${domain}${symbol}/Student/Course/CourseList`; }, 1000); return; } // 课程列表页处理:选择未完成课程 if (url.indexOf('Course/CourseList') > -1) { updateStatus('获取课程列表...', '#4285f4'); setTimeout(function() { if (window.currentCourse) { sessionStorage.setItem('courses', JSON.stringify(window.currentCourse)); let course = getFitCourse(); if (course) { updateStatus(`开始学习课程: ${course.courseName}`, '#42b983'); // 绿色 - 正常状态 setTimeout(() => { window.location.href = `${domain}${symbol}/Student/Course/CourseStudy?courseId=${course.courseId}&teachPlanId=${course.teachPlanId}&periodId=${course.periodId}`; }, 1000); return; } updateStatus('所有课程已完成', '#ff9800'); } else { updateStatus('未获取到课程数据', '#f44336'); } }, 3000); return; } // 课程学习页处理:选择下一个视频 if (url.indexOf('Course/CourseStudy') > -1) { updateStatus('获取视频列表...', '#4285f4'); setTimeout(function() { if (window.coursewareMedias) { var videos = []; getVideoNode(window.coursewareMedias, videos); console.log('找到视频:', videos.length); if (videos.length > 0) { let courseId = getQueryString('courseId'); let teachPlanId = getQueryString('teachPlanId'); let periodId = getQueryString('periodId'); let videoMaps = {}; videoMaps[courseId] = videos; sessionStorage.setItem('videos', JSON.stringify(videoMaps)); updateStatus(`开始播放视频: ${videos[0].title}`, '#42b983'); setTimeout(() => { window.location.href = `${domain}${symbol}/Student/Course/CourseShow?teachPlanId=${teachPlanId}&periodId=${periodId}&courseId=${courseId}&nodeId=${videos[0].id}`; }, 1000); return; } updateStatus('该课程没有视频', '#ff9800'); } else { updateStatus('未获取到视频数据', '#f44336'); } }, 3000); return; } // 课程播放页处理 - 核心功能区 if (url.indexOf('Course/CourseShow') > -1) { // 非视频课程检测 if (currentNodeId && currentNodeId.includes('jbxx')) { updateStatus('⚠ 仅支持视频课程', '#ff9800'); return; } // 获取下一节课程信息 const findCourses = () => { const list = document.querySelectorAll('#lessonMenu li a[id]'); if (list?.length) { clearInterval(findCoursesTimer); const nodeArray = Array.from(list) .filter(item => !item.id.includes('jbxx')) .map(item => ({ id: item.id.split('-')[1], title: item.text.trim() })); const currentIndex = nodeArray.findIndex(o => o.id === currentNodeId); if (currentIndex !== -1) { nextNode = nodeArray[currentIndex + 1]; updateStatus(nextNode ? `准备中,下一节:${nextNode.title}` : '准备中,当前是最后一节', '#4285f4'); } } }; findCoursesTimer = setInterval(findCourses, 1000); // 初始化自动播放 - 增强后台播放能力 function initAutoPlay() { // 强制静音播放,确保后台运行不打扰用户 video.muted = true; video.volume = 0; // 设置播放速度 video.playbackRate = PLAYBACK_RATE; // 尝试播放视频 function playVideo() { video.play().then(() => { updateStatus(nextNode ? `播放中,下一节:${nextNode.title}` : `播放中,最后一节`, '#42b983'); }).catch((error) => { updateStatus('尝试恢复播放...', '#ff9800'); console.log('播放失败,尝试恢复:', error); setTimeout(playVideo, 1000); }); } // 初始播放 playVideo(); // 页面可见性变化监听 document.addEventListener("visibilitychange", () => { if (document.hidden) { if (video.paused) playVideo(); } else { if (video.paused) playVideo(); } }); // 播放状态检查 playCheckInterval = setInterval(() => { if (video.paused) playVideo(); // 显示播放进度 const playedTime = formatTime(video.currentTime); const totalTime = formatTime(video.duration || 0); const duration = video.duration || 0; const progress = duration ? Math.round((video.currentTime / duration) * 100) : 0; updateStatus(nextNode ? `播放中 (${progress}%) ${playedTime}/${totalTime},下一节:${nextNode.title}` : `播放中 (${progress}%) ${playedTime}/${totalTime},最后一节`, '#42b983'); }, 2000); // 视频卡住检测与恢复 setInterval(() => { if (!video) return; const currentTime = video.currentTime.toFixed(1); if (currentTime === currentVideoTime && video.currentTime > 0 && !video.paused) { updateStatus('视频卡住,尝试恢复...', '#ff9800'); video.play().catch(() => location.reload()); } currentVideoTime = currentTime; }, 5000); // 视频结束处理 video.addEventListener("ended", () => { clearInterval(playCheckInterval); const courseId = getQueryString('courseId'); const teachPlanId = getQueryString('teachPlanId'); const periodId = getQueryString('periodId'); if (nextNode) { updateStatus(`跳转至下一节: ${nextNode.title}`, '#4285f4'); urlParams.set('nodeId', nextNode.id); setTimeout(() => { location.replace(`${window.location.pathname}?${urlParams}`); }, 1000); } else { updateStatus('当前课程章节已完成,寻找下一个课程...', '#4285f4'); setTimeout(() => { let courses = JSON.parse(sessionStorage.getItem('courses') || '[]'); let nextCourse = getNextCourse(courseId, courses); if (nextCourse) { updateStatus(`开始学习下一个课程: ${nextCourse.courseName}`, '#42b983'); setTimeout(() => { window.location.href = `${domain}${symbol}/Student/Course/CourseStudy?courseId=${nextCourse.courseId}&teachPlanId=${nextCourse.teachPlanId}&periodId=${nextCourse.periodId}`; }, 1000); } else { updateStatus('所有课程已学习完毕', '#ff9800'); } }, 2000); } }); // 错误监听 video.addEventListener('error', (e) => { console.error('视频错误:', e); updateStatus('视频错误,尝试恢复...', '#f44336'); setTimeout(() => location.reload(), 3000); }); } // 查找视频播放器 const findVideo = () => { video = document.getElementById("vjs_video_3_html5_api") || document.querySelector('video') || (window.CoursewarePlayer?.videoPlayer?.player?.el_?.querySelector('video')); if (video) { clearInterval(findVideoTimer); initAutoPlay(); } else { updateStatus('寻找视频...', '#4285f4'); } }; findVideoTimer = setInterval(findVideo, 1000); // 超时处理 setTimeout(() => { if (!video) { clearInterval(findVideoTimer); updateStatus('未找到视频,请刷新', '#f44336'); } }, 15000); } } else { // 如果自动刷课功能被禁用 updateStatus('自动刷课已禁用', '#f44336'); } // 工具函数:更新状态显示 function updateStatus(text, color) { statusDiv.textContent = text; statusDiv.style.background = color; } // 工具函数:格式化时间 function formatTime(seconds) { const hours = Math.floor(seconds / 3600); const minutes = Math.floor((seconds % 3600) / 60); const secs = Math.floor(seconds % 60); const pad = (num) => num.toString().padStart(2, '0'); return hours > 0 ? `${pad(hours)}:${pad(minutes)}:${pad(secs)}` : `${pad(minutes)}:${pad(secs)}`; } // 工具函数:获取未完成的课程 function getFitCourse() { if (!window.currentCourse) return null; for (let inx in window.currentCourse) { if (window.currentCourse[inx].score < 100) { return window.currentCourse[inx]; } } return null; } // 工具函数:获取下一个课程 function getNextCourse(currentId, courses) { let next = null; Array.prototype.forEach.call(courses, function (value, index) { if (value.courseId == currentId && courses.length - 1 > index) { next = courses[index + 1]; return false; } }); return next; } // 工具函数:递归获取所有视频节点 function getVideoNode(medias, videos) { Array.prototype.forEach.call(medias, function (value) { if (value.type === 'video') { videos.push(value); } if (value.nodes != null) { getVideoNode(value.nodes, videos); } }); } // 工具函数:获取URL参数 function getQueryString(name) { const reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); const r = window.location.search.substr(1).match(reg); if (r != null) { return unescape(r[2]); } return null; } // 清理资源 window.addEventListener('beforeunload', () => { clearInterval(playCheckInterval); clearInterval(findCoursesTimer); clearInterval(findVideoTimer); }); // 答题相关函数 - 避免修改页面原有提交功能 function handleAnswerPages() { // 考试页面解除复制限制(不影响提交功能) if (location.href.indexOf('ExamPaper') !== -1) { $('*').unbind('copy'); showAnswerNotification('考试页面已解除复制限制'); return; } // 作业页面自动答题 if (location.href.indexOf('ExercisePaper') !== -1) { listenSource([ { fn: () => $('.question-detail-options .question-detail-option').length, callback: autoFillAnswer }, ]); } } // url参数转换为对象 function UrlSearch() { const url = new URL(location.href); const params = {}; // 处理查询字符串参数 const search = url.search.replace('?', ''); const searchParams = search.split('&'); searchParams.forEach(item => { const [key, value] = item.split('='); if (key) params[key] = value; }); // 处理路径参数 const pathParts = url.pathname.split('/').filter(part => part !== ''); params.pathParts = pathParts; return params; } function listenSource(listen = []) { function setup() { listen.forEach((item, index) => { const {fn, callback} = item; if (fn()) { callback(); listen.splice(index, 1); } }); if (listen.length) { requestAnimationFrame(setup); } } if (listen.length) { requestAnimationFrame(setup); } } // 清除已选答案 - 只操作答案选项,不影响提交按钮 function clearAnswers() { // 清除单选题和多选题的选中状态 $('.question-detail-option.active').removeClass('active'); $('input[type="radio"]:checked, input[type="checkbox"]:checked').prop('checked', false); showAnswerNotification('已清除所有答案', true); } // 答案自动填入 - 仅填充答案,不触碰提交相关元素 function autoFillAnswer() { const urlSearch = UrlSearch(); if (!urlSearch.quizId || !urlSearch.pathParts || !urlSearch.pathParts[0]) { showAnswerNotification('无法获取答题信息', false); return; } fetch(`https://degree.qingshuxuetang.com/${urlSearch.pathParts[0]}/Student/DetailData?_t=${new Date().getMilliseconds()}&quizId=${urlSearch.quizId}`, { method: 'GET', headers: { Host: 'degree.qingshuxuetang.com', Cookie: Object.entries(Cookies.get()).map(([key, value]) => `${key}=${value}`).join('; '), Referer: location.href, 'sec-ch-ua': '"Not?A_Brand";v="8", "Chromium";v="108", "Google Chrome";v="108"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': 'macOS', 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors', 'Sec-Fetch-Site': 'same-origin', 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36', 'X-Requested-With': 'XMLHttpRequest', }, }) .then(res => res.json()) .then(res => { if (!res.data || !res.data.paperDetail || !res.data.paperDetail.questions) { showAnswerNotification('未获取到答案数据', false); return; } const questions = res.data.paperDetail.questions; Object.values(questions).forEach(item => { // 处理单选/多选答案填入 if (item.solution && item.questionId) { for (let i = 0; i < item.solution.length; i++) { $(`#${item.questionId}_${item.solution.charAt(i)}`).click(); } } }); // 显示完成信息并提供重新答题选项 showAnswerNotification('答案已自动填入', true, true); }) .catch(err => { showAnswerNotification('答案填入失败', false); console.error(err); }); } // 显示答题相关通知 - 左侧显示 function showAnswerNotification(msg, isSuccess = true, showActions = false) { // 使用脚本专用的控制容器,不干扰页面原有元素 let statusContainer = document.getElementById('qingshu-script-control-panel'); if (!statusContainer) { statusContainer = document.createElement('div'); statusContainer.id = 'qingshu-script-control-panel'; document.body.appendChild(statusContainer); } let answerStatus = document.querySelector('.answer-status'); if (!answerStatus) { answerStatus = document.createElement('div'); answerStatus.className = 'qingshu-status answer-status'; statusContainer.appendChild(answerStatus); } answerStatus.textContent = `[自动答题] ${msg}`; answerStatus.style.background = isSuccess ? '#42b983' : '#f44336'; // 创建操作按钮容器 if (showActions) { let actionContainer = document.querySelector('.answer-action-buttons'); if (!actionContainer) { actionContainer = document.createElement('div'); actionContainer.className = 'answer-action-buttons'; statusContainer.appendChild(actionContainer); } else { // 清空现有按钮,避免重复创建 actionContainer.innerHTML = ''; } // 添加重新答题按钮 const retryBtn = document.createElement('button'); retryBtn.className = 'answer-btn retry-btn'; retryBtn.textContent = '重新答题'; retryBtn.onclick = function() { clearAnswers(); // 2秒后重新填充答案 setTimeout(autoFillAnswer, 2000); }; actionContainer.appendChild(retryBtn); } // 同时显示pxmu通知 if (isSuccess) { pxmu.success({ msg: `[自动答题] ${msg}`, bg: '#42b983' }); } else { pxmu.fail({ msg: `[自动答题] ${msg}`, bg: '#f44336' }); } } })();