// ==UserScript== // @name 杏林学堂刷课 // @namespace http://tampermonkey.net/ // @version 2.1 // @description 自动获取任务列表及课程列表,完成视频播放,支持自动登录 // @author You // @match https://mbeta.xlxt.net/* // @grant GM_xmlhttpRequest // ==/UserScript== /* global GM_xmlhttpRequest */ (function () { 'use strict'; // 配置常量 const config = { taskUrl: 'https://wwwbeta.xlxt.net//Task/GetMemberTask?pageindex=1&pagesize=10&StudyStatus=0', loginUrl: 'https://mbeta.xlxt.net/#/login?from=home', defaultSettings: { playButtonDelay: 2500, videoDetectionDelay: 2000, videoAdjustWait: 500, maxRetries: 2, maxPlayButtonRetries: 2, maxLoopRetries: 3, phone: '未设置', password: '未设置' } }; // 状态变量 let state = { isBrushing: false, CoursesNum: 0, curCourseQueue: [], taskQueue: [], currentTaskIndex: -1, retryCount: 0, settings: loadSettings(), cookieString: document.cookie, // 添加全局cookieString变量 ui: { infoBox: null, floatWindow: null, settingsWindow: null, startBtn: null, processCurrentCourseBtn: null } }; // 初始化 function init() { initUI(); // 事件监听 document.addEventListener('DOMContentLoaded', checkIfInCoursePage); window.addEventListener('load', checkIfInCoursePage); // 监听URL变化 - 改进版 let lastUrl = window.location.href; // 监听popstate事件,当浏览器地址栏发生变化时触发。 window.addEventListener('popstate', function () { if (window.location.href !== lastUrl) { lastUrl = window.location.href; checkIfInCoursePage(); } }); } //加载完页面之后的操作。程序的入口 if (document.readyState === 'complete') { init(); } else { window.addEventListener('load', init); } // 初始化UI function initUI() { state.ui.infoBox = createInfoBox(); state.ui.floatWindow = createFloatWindow(); showMessage('欢迎使用自动刷课脚本!', 'info'); showMessage('点击"开始刷课"按钮开始执行任务', 'info'); showMessage('如果需要登录,请点击"前往登录"按钮', 'info'); } // 创建信息显示框 function createInfoBox() { const box = document.createElement('div'); box.id = 'auto-course-info-box'; box.style.cssText = ` position: fixed; bottom: 20px; right: 20px; width: 300px; padding: 15px; background-color: #fff; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 9999; font-family: 'Microsoft YaHei', sans-serif; `; // 标题栏 const titleBar = document.createElement('div'); titleBar.style.cssText = 'display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;'; box.appendChild(titleBar); // 标题 const title = document.createElement('h3'); title.textContent = '自动刷课脚本'; title.style.cssText = 'margin: 0; color: #333; border-bottom: 1px solid #eee; padding-bottom: 8px; flex-grow: 1;'; titleBar.appendChild(title); // 设置按钮 const settingsBtn = document.createElement('button'); settingsBtn.textContent = '设置'; settingsBtn.style.cssText = 'background-color: #2196F3; color: white; border: none; border-radius: 4px; padding: 4px 8px; cursor: pointer; font-size: 12px;'; settingsBtn.addEventListener('click', openSettingsWindow); titleBar.appendChild(settingsBtn); // 内容区域 const content = document.createElement('div'); content.id = 'auto-course-info-content'; content.style.cssText = 'max-height: 200px; overflow-y: auto; margin: 10px 0; font-size: 14px;'; box.appendChild(content); // 按钮区域 const buttonArea = document.createElement('div'); buttonArea.style.cssText = 'display: flex; flex-wrap: wrap; justify-content: space-between; margin-top: 15px; gap: 5px;'; box.appendChild(buttonArea); // 开始刷课按钮 state.ui.startBtn = document.createElement('button'); state.ui.startBtn.textContent = '开始刷课'; state.ui.startBtn.style.cssText = 'background-color: #4CAF50; color: white; border: none; border-radius: 4px; padding: 8px 16px; cursor: pointer; flex: 1 1 100%; margin-bottom: 5px;'; state.ui.startBtn.addEventListener('click', toggleBrushing); buttonArea.appendChild(state.ui.startBtn); // 处理当前课程按钮 state.ui.processCurrentCourseBtn = document.createElement('button'); state.ui.processCurrentCourseBtn.textContent = '处理当前课程'; state.ui.processCurrentCourseBtn.style.cssText = 'background-color: #ff9800; color: white; border: none; border-radius: 4px; padding: 8px 16px; cursor: pointer; flex: 1 1 100%; margin-bottom: 5px;'; state.ui.processCurrentCourseBtn.addEventListener('click', processCurrentCourseBtn_Onclick); buttonArea.appendChild(state.ui.processCurrentCourseBtn); // 前往登录按钮 const loginBtn = document.createElement('button'); loginBtn.textContent = '前往登录'; loginBtn.style.cssText = 'background-color: #2196F3; color: white; border: none; border-radius: 4px; padding: 8px 16px; cursor: pointer; flex: 1 1 calc(50% - 5px);'; loginBtn.addEventListener('click', () => { showMessage('正在跳转到登录页面...', 'info'); window.location.href = config.loginUrl; }); buttonArea.appendChild(loginBtn); // 关闭按钮 const closeBtn = document.createElement('button'); closeBtn.textContent = '关闭'; closeBtn.style.cssText = 'background-color: #f44336; color: white; border: none; border-radius: 4px; padding: 8px 16px; cursor: pointer; flex: 1 1 calc(50% - 5px);'; closeBtn.addEventListener('click', () => { box.style.display = 'none'; state.ui.floatWindow.style.display = 'block'; }); buttonArea.appendChild(closeBtn); document.body.appendChild(box); return box; } // 创建悬浮窗 // 创建悬浮窗 function createFloatWindow() { const floatWin = document.createElement('div'); floatWin.id = 'auto-course-float-window'; floatWin.style.cssText = ` position: fixed; bottom: 20px; right: 20px; width: 50px; height: 50px; background-color: #4CAF50; color: white; border-radius: 50%; display: flex; justify-content: center; align-items: center; box-shadow: 0 4px 8px rgba(0,0,0,0.2); cursor: pointer; z-index: 9998; display: none; text-align: center; line-height: 50px; /* 添加这两行使文字居中 */ `; floatWin.textContent = '刷课'; floatWin.addEventListener('click', () => { floatWin.style.display = 'none'; state.ui.infoBox.style.display = 'block'; }); document.body.appendChild(floatWin); return floatWin; } // 打开设置窗口 function openSettingsWindow() { if (state.ui.settingsWindow) { state.ui.settingsWindow.style.display = 'block'; return; } // 创建设置窗口 state.ui.settingsWindow = document.createElement('div'); state.ui.settingsWindow.id = 'auto-course-settings-window'; state.ui.settingsWindow.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 350px; padding: 20px; background-color: #fff; border-radius: 8px; box-shadow: 0 4px 16px rgba(0,0,0,0.2); z-index: 10000; font-family: 'Microsoft YaHei', sans-serif; font-size: 12px; /* 减小整体字体大小 */ `; // 标题 const title = document.createElement('h3'); title.textContent = '脚本设置'; title.style.cssText = 'margin-top: 0; color: #333; border-bottom: 1px solid #eee; padding-bottom: 10px; margin-bottom: 15px; font-size: 16px;'; state.ui.settingsWindow.appendChild(title); // 表单 const form = document.createElement('form'); form.style.cssText = 'font-size: 12px;'; // 设置表单内字体大小 state.ui.settingsWindow.appendChild(form); // 添加设置字段 addSettingField(form, 'playButtonDelay', '播放按钮查找延迟(毫秒)', state.settings.playButtonDelay); addSettingField(form, 'videoDetectionDelay', '视频元素检测延迟(毫秒)', state.settings.videoDetectionDelay); addSettingField(form, 'videoAdjustWait', '视频调整后等待时间(毫秒)', state.settings.videoAdjustWait); addSettingField(form, 'maxRetries', '单个循环内视频元素最大重试次数', state.settings.maxRetries); addSettingField(form, 'maxPlayButtonRetries', '单个循环内播放按钮最大重试次数', state.settings.maxPlayButtonRetries); addSettingField(form, 'maxLoopRetries', '最大循环重试次数', state.settings.maxLoopRetries); addSettingField(form, 'phone', '手机号', state.settings.phone, 'text'); addSettingField(form, 'password', '密码', state.settings.password, 'text'); // 保存按钮 const buttonArea = document.createElement('div'); buttonArea.style.cssText = 'margin-top: 20px; display: flex; justify-content: flex-end;'; state.ui.settingsWindow.appendChild(buttonArea); const saveBtn = document.createElement('button'); saveBtn.type = 'button'; saveBtn.textContent = '保存并关闭'; saveBtn.style.cssText = 'background-color: #4CAF50; color: white; border: none; border-radius: 4px; padding: 8px 16px; cursor: pointer; font-size: 12px;'; saveBtn.addEventListener('click', saveSettings); buttonArea.appendChild(saveBtn); // 遮罩层 const overlay = document.createElement('div'); overlay.id = 'auto-course-settings-overlay'; overlay.style.cssText = 'position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); z-index: 9999;'; overlay.addEventListener('click', closeSettingsWindow); document.body.appendChild(overlay); document.body.appendChild(state.ui.settingsWindow); } // 添加设置字段 function addSettingField(form, id, labelText, value, type = 'number') { const fieldContainer = document.createElement('div'); fieldContainer.style.cssText = 'margin-bottom: 15px; display: flex; align-items: center;'; const label = document.createElement('label'); label.textContent = labelText; label.htmlFor = id; label.style.cssText = 'font-weight: bold; width: 200px; margin-right: 10px; font-size: 12px;'; // 调整标签字体大小 const input = document.createElement('input'); input.type = type; input.id = id; input.name = id; input.value = value; if (type === 'number') { input.min = '0'; } input.style.cssText = 'flex: 1; padding: 8px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box; font-size: 12px;'; // 调整输入框字体大小 fieldContainer.appendChild(label); fieldContainer.appendChild(input); form.appendChild(fieldContainer); } // 保存设置 function saveSettings() { const newSettings = { playButtonDelay: parseInt(document.getElementById('playButtonDelay').value, 10) || config.defaultSettings.playButtonDelay, videoDetectionDelay: parseInt(document.getElementById('videoDetectionDelay').value, 10) || config.defaultSettings.videoDetectionDelay, videoAdjustWait: parseInt(document.getElementById('videoAdjustWait').value, 10) || config.defaultSettings.videoAdjustWait, maxRetries: parseInt(document.getElementById('maxRetries').value, 10) || config.defaultSettings.maxRetries, maxPlayButtonRetries: parseInt(document.getElementById('maxPlayButtonRetries').value, 10) || config.defaultSettings.maxPlayButtonRetries, maxLoopRetries: parseInt(document.getElementById('maxLoopRetries').value, 10) || config.defaultSettings.maxLoopRetries, phone: document.getElementById('phone').value || config.defaultSettings.phone, password: document.getElementById('password').value || config.defaultSettings.password }; localStorage.setItem('autoCourseSettings', JSON.stringify(newSettings)); state.settings = newSettings; showMessage('设置已保存', 'success'); closeSettingsWindow(); } // 关闭设置窗口 function closeSettingsWindow() { if (state.ui.settingsWindow) state.ui.settingsWindow.style.display = 'none'; const overlay = document.getElementById('auto-course-settings-overlay'); if (overlay) overlay.style.display = 'none'; } // 加载设置 function loadSettings() { try { const savedSettings = localStorage.getItem('autoCourseSettings'); if (savedSettings) { const settings = JSON.parse(savedSettings); return { ...config.defaultSettings, ...settings }; } } catch (e) { console.error('加载设置失败:', e); } return config.defaultSettings; } // 显示消息 function showMessage(message, type = 'log') { const content = document.getElementById('auto-course-info-content'); if (!content) return; const messageElement = document.createElement('p'); messageElement.textContent = `[${new Date().toLocaleTimeString()}] ${message}`; messageElement.style.margin = '5px 0'; messageElement.style.padding = '3px 5px'; messageElement.style.borderRadius = '3px'; // 设置消息样式 switch (type) { case 'error': messageElement.style.color = '#f44336'; messageElement.style.backgroundColor = '#ffebee'; console.error(`[${new Date().toLocaleTimeString()}] ${message}`); break; case 'warning': messageElement.style.color = '#ff9800'; messageElement.style.backgroundColor = '#fff3e0'; console.warn(`[${new Date().toLocaleTimeString()}] ${message}`); break; case 'success': messageElement.style.color = '#4caf50'; messageElement.style.backgroundColor = '#e8f5e9'; console.log(`[${new Date().toLocaleTimeString()}] ${message}`); break; case 'info': messageElement.style.color = '#2196f3'; messageElement.style.backgroundColor = '#e3f2fd'; console.log(`[${new Date().toLocaleTimeString()}] ${message}`); break; default: messageElement.style.color = '#666'; console.log(`[${new Date().toLocaleTimeString()}] ${message}`); } content.appendChild(messageElement); content.scrollTop = content.scrollHeight; // 保持最多显示10条消息 if (content.children.length > 10) content.removeChild(content.firstChild); } // 切换刷课状态 function toggleBrushing() { if (state.isBrushing) { stopBrushing(); } else { startBrushing(); } } // 开始刷课 function startBrushing() { state.isBrushing = true; state.ui.startBtn.textContent = '停止刷课'; state.ui.startBtn.style.backgroundColor = '#f44336'; showMessage('开始刷课,正在获取任务列表...', 'info'); reachTasks(); } // 停止刷课 function stopBrushing() { state.isBrushing = false; state.ui.startBtn.textContent = '开始刷课'; state.ui.startBtn.style.backgroundColor = '#4CAF50'; state.curCourseQueue = []; state.taskQueue = []; state.CoursesNum = 0; state.currentTaskIndex = -1; showMessage('已停止刷课', 'warning'); } // 处理当前课程 :按钮的响应函数 function processCurrentCourseBtn_Onclick() { if (window.location.href.includes('/course-info')) { showMessage('正在处理当前课程...', 'info'); const originalText = state.ui.processCurrentCourseBtn.textContent; const originalColor = state.ui.processCurrentCourseBtn.style.backgroundColor; state.ui.processCurrentCourseBtn.textContent = '处理中...'; state.ui.processCurrentCourseBtn.style.backgroundColor = '#cccccc'; state.ui.processCurrentCourseBtn.disabled = true; const dummyCourse = { jumpUrl: window.location.href }; handleVideoPlayback(dummyCourse, () => { state.ui.processCurrentCourseBtn.textContent = originalText; state.ui.processCurrentCourseBtn.style.backgroundColor = originalColor; state.ui.processCurrentCourseBtn.disabled = false; showMessage('当前课程处理完毕', 'success'); }); } else { showMessage('当前不在课程页面', 'error'); } } // 处理视频界面->被用于:按钮触发,流程触发 function handleVideoPlayback(course, callback = null) { if (!state.isBrushing && !callback) { showMessage('刷课已停止,无法继续处理课程', 'warning'); return; } showMessage('正在尝试处理视频...', 'info'); let loopRetryCount = 0; const MAX_LOOP_RETRIES = state.settings.maxLoopRetries; // 创建循环处理函数 function processVideoLoop() { if (!state.isBrushing && !callback) return; if (loopRetryCount >= MAX_LOOP_RETRIES) { showMessage(`超过最大循环重试次数(${MAX_LOOP_RETRIES}),跳过当前课程`, 'error'); if (callback) { callback(); } else { skipCurrentCourse(); } return; } loopRetryCount++; showMessage(`第 ${loopRetryCount}/${MAX_LOOP_RETRIES} 次尝试处理视频`, 'info'); // 先尝试点击播放按钮 clickPlayButton(course, () => { // 播放按钮点击后,检查视频元素 checkVideoElement(course, callback, () => { // 视频元素检查失败,再次尝试循环 setTimeout(processVideoLoop, state.settings.playButtonDelay + state.settings.videoDetectionDelay); }); }); } // 开始循环处理 processVideoLoop(); } // 自动点击播放按钮 function clickPlayButton(course, successCallback, failCallback) { if (!state.isBrushing && !successCallback && !failCallback) return; let playButtonRetryCount = 0; const MAX_PLAY_BUTTON_RETRIES = state.settings.maxPlayButtonRetries; setTimeout(() => findAndClickPlayButton(), state.settings.playButtonDelay); function findAndClickPlayButton() { if (!state.isBrushing && !successCallback && !failCallback) return; const playButton = document.querySelector('.prism-big-play-btn') || document.querySelector('#J_prismPlayer_component_F46C34E7-1A34-4EFB-AA07-68B666B6A1DD') || document.querySelector('.play-button') || document.querySelector('button[aria-label*="播放"]'); if (playButton) { try { playButton.click(); showMessage('已成功点击播放按钮', 'success'); if (successCallback) successCallback(); } catch (e) { showMessage('点击播放按钮时出错: ' + e.message, 'error'); if (failCallback) failCallback(); } } else { playButtonRetryCount++; if (playButtonRetryCount < MAX_PLAY_BUTTON_RETRIES) { showMessage(`未找到播放按钮,${playButtonRetryCount}秒后重试(${playButtonRetryCount}/${MAX_PLAY_BUTTON_RETRIES})`, 'warning'); setTimeout(() => findAndClickPlayButton(), 1000); } else { showMessage('超过最大重试次数,无法找到播放按钮', 'error'); if (failCallback) failCallback(); } } } } // 检查视频元素 function checkVideoElement(course, successCallback, failCallback) { if (!state.isBrushing && !successCallback && !failCallback) return; let localRetryCount = 0; const MAX_LOCAL_RETRIES = state.settings.maxRetries; function check() { localRetryCount++; let v = document.querySelector('video') || document.querySelector('video[src]') || document.querySelector('.video-player video') || document.querySelector('#video-player video'); if (v && typeof v.currentTime !== 'undefined' && typeof v.duration !== 'undefined' && !isNaN(v.duration) && v.duration > 0) { try { v.currentTime = v.duration; showMessage('视频进度已调整至最后', 'success'); if (successCallback) { setTimeout(successCallback, state.settings.videoAdjustWait); } else { state.curCourseQueue.shift(); state.CoursesNum--; showMessage(`当前课程处理完毕,还剩 ${state.curCourseQueue.length} 个课程待处理`, 'info'); setTimeout(() => { if (!state.isBrushing) return; if (state.curCourseQueue.length > 0) { showMessage(`正在跳转到下一个课程页面...`, 'info'); window.location.href = state.curCourseQueue[0].jumpUrl; } else { showMessage('当前任务的所有课程处理完毕', 'success'); state.CoursesNum = 0; resetStartButton(); } }, state.settings.videoAdjustWait); } } catch (error) { showMessage('调整视频进度时出错: ' + error.message, 'error'); if (localRetryCount < MAX_LOCAL_RETRIES) { setTimeout(check, state.settings.videoDetectionDelay); } else { showMessage('超过最大重试次数,无法调整视频进度', 'error'); handleFailedCourse(course, callback); } } } else { if (localRetryCount < MAX_LOCAL_RETRIES) { showMessage(`未检测到有效的视频元素(${localRetryCount}/${MAX_LOCAL_RETRIES})`, 'warning'); setTimeout(check, state.settings.videoDetectionDelay); } else { showMessage('超过最大重试次数,无法检测到视频元素,尝试重新点击播放按钮...', 'error'); clickPlayButton(course, callback); } } } check(); } // 处理失败的课程 function handleFailedCourse(course, callback) { if (callback) { callback(); } else { state.curCourseQueue.shift(); state.CoursesNum--; showMessage(`当前课程处理完毕,还剩 ${state.curCourseQueue.length} 个课程待处理`, 'info'); setTimeout(() => { if (!state.isBrushing) return; if (state.curCourseQueue.length > 0) { window.location.href = state.curCourseQueue[0].jumpUrl; } else { showMessage('当前任务的所有课程处理完毕', 'success'); state.CoursesNum = 0; resetStartButton(); } }, state.settings.videoDetectionDelay); } } // 重置开始按钮状态 function resetStartButton() { if (state.ui.startBtn && state.CoursesNum === 0 && state.taskQueue.length === 0) { state.ui.startBtn.disabled = false; state.ui.startBtn.style.backgroundColor = '#4CAF50'; state.ui.startBtn.textContent = '开始刷课'; state.isBrushing = false; showMessage('所有任务和课程处理完毕,您可以再次点击"开始刷课"按钮', 'success'); } else if (state.ui.startBtn && state.CoursesNum === 0 && state.taskQueue.length > 0) { state.currentTaskIndex++; if (state.currentTaskIndex < state.taskQueue.length) { const nextTask = state.taskQueue[state.currentTaskIndex]; showMessage(`开始处理下一个任务: ${nextTask.Name}`, 'info'); processCourseList(nextTask.TaskID, document.cookie); } } } // function reachTasks() { // 更新cookieString state.cookieString = document.cookie; showMessage('已获取Cookie,准备发送请求...', 'info'); let taskData = null; GM_xmlhttpRequest({ method: 'GET', url: config.taskUrl, headers: { 'Content-Type': 'application/json', 'Cookie': state.cookieString }, onload: function (response) { try { taskData = JSON.parse(response.responseText); showMessage('任务列表获取成功', 'success'); if (taskData.Code === 401) { showMessage('未授权访问,请先登录', 'error'); window.location.href = config.loginUrl; } processTasks(taskData); //进入课程 } catch (error) { showMessage('解析任务数据失败', 'error'); } }, onerror: function (error) { showMessage('请求任务列表失败', 'error'); } }); } // 处理任务数据 function processTasks(taskData) { if (!state.isBrushing) return; if (taskData.Data && taskData.Data.length > 0) { showMessage(`共获取到 ${taskData.Data.length} 个任务`, 'info'); state.taskQueue = taskData.Data; state.currentTaskIndex = 0; if (state.taskQueue.length > 0) { const firstTask = state.taskQueue[state.currentTaskIndex]; showMessage(`开始处理第一个任务: ${firstTask.Name}`, 'info'); processCourseList(firstTask.TaskID); } } else { showMessage('没有获取到任务数据', 'warning'); stopBrushing(); } } // 处理课程列表 function processCourseList(taskId) { if (!state.isBrushing) return; // 更新cookieString state.cookieString = document.cookie; const courseUrl = `https://wwwbeta.xlxt.net//Task/GetMemberCourseProgress?TaskID=${taskId}&pageindex=1&pagesize=30`; showMessage(`正在获取任务ID ${taskId} 的课程列表...`, 'info'); GM_xmlhttpRequest({ method: 'GET', url: courseUrl, headers: { 'Content-Type': 'application/json', 'Cookie': state.cookieString }, onload: function (response) { try { const courseData = JSON.parse(response.responseText); showMessage(`任务ID ${taskId} 的课程列表获取成功`, 'success'); if (courseData.Code === 401) { showMessage('未授权访问,请先登录', 'error'); stopBrushing(); window.location.href = config.loginUrl; return; } processCourseData(taskId, courseData); } catch (error) { showMessage(`解析任务ID ${taskId} 的课程数据失败`, 'error'); nextTask(); } }, onerror: function (error) { showMessage(`请求任务ID ${taskId} 的课程列表失败`, 'error'); nextTask(); } }); function nextTask() { state.currentTaskIndex++; if (state.currentTaskIndex < state.taskQueue.length) { const nextTask = state.taskQueue[state.currentTaskIndex]; showMessage(`跳过当前任务,开始处理下一个任务: ${nextTask.Name}`, 'warning'); // 更新cookieString state.cookieString = document.cookie; processCourseList(nextTask.TaskID); } else { showMessage('所有任务处理完毕', 'success'); stopBrushing(); } } } // 处理课程数据(组) function processCourseData(taskId, courseData) { if (!state.isBrushing) return; if (courseData.Data && Array.isArray(courseData.Data) && courseData.Data.length > 0) { showMessage(`任务ID ${taskId} 共获取到 ${courseData.Data.length} 个课程`, 'info'); state.CoursesNum = 0; state.curCourseQueue = []; courseData.Data.forEach((course, index) => { if (course.Progress == null || course.Progress.StudyStatus == 0) { //没有进度,或者进度为零 -> 要刷的 const courseId = course.CourseID; const jumpUrl = `https://mbeta.xlxt.net/#/course-info?courseID=${courseId}&TaskID=${taskId}`; state.curCourseQueue.push({ index, courseId, jumpUrl }); state.CoursesNum++; showMessage(`课程ID ${course.CourseID} 已添加到队列`, 'info'); } else { showMessage(`课程ID ${course.CourseID} 已完成,跳过`, 'info'); } }); showMessage(`预计要刷 ${state.CoursesNum} 个课程`, 'info'); if (state.curCourseQueue.length > 0) { showMessage(`准备跳转到第一个课程页面...`, 'info'); setTimeout(() => { if (state.isBrushing) window.location.href = state.curCourseQueue[0].jumpUrl; }, 1000); } else { showMessage('当前任务没有有效的课程可处理', 'warning'); nextTask(); } } else { showMessage(`任务ID ${taskId} 没有获取到课程数据`, 'warning'); nextTask(); } function nextTask() { state.currentTaskIndex++; if (state.currentTaskIndex < state.taskQueue.length) { const nextTask = state.taskQueue[state.currentTaskIndex]; showMessage(`开始处理下一个任务: ${nextTask.Name}`, 'info'); processCourseList(nextTask.TaskID, document.cookie); } else { showMessage('所有任务处理完毕', 'success'); stopBrushing(); } } } // 检查是否在课程页面 function checkIfInCoursePage() { const currentUrl = window.location.href; // 检测是否在课程页面 if (currentUrl.includes('/#/course-info')) { // console.log("刷课状态?:",state.isBrushing); if (state.isBrushing) { showMessage('检测到课程页面,开始处理...', 'info'); // 创建一个虚拟课程对象 const dummyCourse = { jumpUrl: currentUrl }; // 调用视频处理函数 handleVideoPlayback(dummyCourse); } return true; } // 检测是否在登录页面 else if (currentUrl.includes('/login')) { console.log('检测到登录页面,尝试自动登录...'); setTimeout(autoLogin, 1000); // 延迟1秒执行自动登录 return false; } return false; } // 自动登录函数 function autoLogin() { const phone = state.settings.phone || ''; const password = state.settings.password || ''; if (!phone || !password || phone === '未设置' || password === '未设置') { console.log('手机号或密码未设置,无法自动登录'); showMessage('请先在设置中填写手机号和密码', 'error'); return; } // 1. 先点击用户协议图片 const agreementImg = document.querySelector('img[data-v-32955c20][src="https://img.xlxt.net/photoManage/2021/12/14/163944958500030968.png"]'); if (agreementImg) { agreementImg.click(); console.log('已点击用户协议图片'); } else { console.log('未找到用户协议图片'); showMessage('未找到用户协议图片,请手动点击', 'warning'); return; // 如果未找到协议图片,不继续执行 } // 2. 延迟填写手机号和密码,确保协议已点击完成 setTimeout(() => { // 填写手机号 - 使用更精确的选择器 const phoneInput = document.querySelector('input.tel-input[data-v-32955c20]') || document.querySelector('input[data-v-32955c20][placeholder="请输入手机号/用户名"]'); if (phoneInput) { // 尝试不同的填入方法 try { // 方法1: 直接设置value并触发事件 phoneInput.value = phone; phoneInput.dispatchEvent(new Event('focus')); phoneInput.dispatchEvent(new Event('input', { bubbles: true })); phoneInput.dispatchEvent(new Event('change', { bubbles: true })); console.log('已填写手机号 (方法1)'); } catch (e1) { try { // 方法2: 使用setAttribute phoneInput.setAttribute('value', phone); phoneInput.dispatchEvent(new Event('input', { bubbles: true })); console.log('已填写手机号 (方法2)'); } catch (e2) { console.error('填写手机号时出错:', e1, e2); showMessage('填写手机号时出错,请手动填写', 'error'); } } } else { console.log('未找到手机号输入框'); showMessage('未找到手机号输入框,请手动填写', 'warning'); } // 填写密码 - 使用更精确的选择器 const passwordInput = document.querySelector('input[type="password"][data-v-32955c20]') || document.querySelector('input[data-v-32955c20][placeholder="请输入登录密码"]'); if (passwordInput) { // 尝试不同的填入方法 try { // 方法1: 直接设置value并触发事件 passwordInput.value = password; passwordInput.dispatchEvent(new Event('focus')); passwordInput.dispatchEvent(new Event('input', { bubbles: true })); passwordInput.dispatchEvent(new Event('change', { bubbles: true })); console.log('已填写密码 (方法1)'); } catch (e1) { try { // 方法2: 使用setAttribute passwordInput.setAttribute('value', password); passwordInput.dispatchEvent(new Event('input', { bubbles: true })); console.log('已填写密码 (方法2)'); } catch (e2) { console.error('填写密码时出错:', e1, e2); showMessage('填写密码时出错,请手动填写', 'error'); } } } else { console.log('未找到密码输入框'); showMessage('未找到密码输入框,请手动填写', 'warning'); } // 3. 延迟点击登录按钮,确保信息已填写完成 setTimeout(() => { const loginButton = document.querySelector('button.login-btn') || document.querySelector('button[type="button"]:not([class])'); if (loginButton) { loginButton.click(); console.log('已点击登录按钮'); showMessage('自动登录中...', 'info'); } else { console.log('未找到登录按钮'); showMessage('未找到登录按钮,请手动点击', 'warning'); } }, 1000); // 延迟1000毫秒点击登录按钮 }, 500); // 延迟500毫秒填写信息 } })();