// ==UserScript== // @name 江苏开放大学刷课脚本 // @namespace http://tampermonkey.net/ // @version 3.0 // @description 江苏开放大学刷课脚本-三改版,增加自动跳转功能 | 原作者:Pwn | 二创作者:thanks play | 三创作者:AI // @author Pwn (原作者) / thanks play (二创作者) / AI (三创作者) // @match http://xuexi.jsou.cn/jxpt-web/student/courseuser/courseVersionId=* // @match http://xuexi.jsou.cn/jxpt-web/student/activity/display?courseVersionId=* // @match http://xuexi.jsou.cn/jxpt-web/student/newHomework/showHomeworkByStatus?courseVersionId=* // @icon https://www.google.com/s2/favicons?sz=64&domain=jsou.cn // @grant unsafeWindow // @grant GM_setValue // @grant GM_getValue // @license MIT // ==/UserScript== /** * 江苏开放大学刷课脚本 v3.0 * * 总体功能说明: * ==================== * * 本工具提供两个主要功能模块,可以单独使用,也可以配合使用实现全自动刷课: * * 1. 手动选课功能(ManualSelectAuto) * - 功能:自动发送心跳包,模拟学习时长 * - 特点:只发送心跳包,不跳转页面 * - 使用:打开课程页面后点击"手动选课"按钮 * - 时长:随机8-10分钟 * * 2. 自动跳转功能(AllCourseAuto) * - 功能:自动遍历所有课程并跳转 * - 特点:只跳转页面,不发送心跳包 * - 使用:点击"开始自动跳转"按钮 * - 间隔:每个课程停留5秒后跳转 * * 全自动刷课模式: * ==================== * * 步骤1:打开任意课程页面 * 步骤2:点击"手动选课"按钮,开始发送心跳包 * 步骤3:点击"开始自动跳转"按钮,开始自动跳转 * 步骤4:脚本会自动完成所有课程的学习 * * 开发方法说明: * ==================== * * 1. 课程信息获取 * - 优先从全局变量 units 中获取课程列表 * - 如果 units 不存在,从 DOM 中的课程目录树(a.treenode_a)提取 * - 课程 URL 格式:http://xuexi.jsou.cn/jxpt-web/student/activity/display?courseVersionId=xxx&activityId=xxx * * 2. 心跳包发送 * - 使用 setInterval 定时器,每30秒发送一次心跳包 * - 心跳包通过 sendHeartBeatAjax() 函数发送 * - 随机生成8-10分钟的学习时长 * * 3. 页面跳转 * - 使用 window.location.href 进行页面跳转 * - 使用 setTimeout 延迟5秒后跳转 * - 使用 GM_setValue/GM_getValue 保存和恢复进度 * * 4. 状态管理 * - manualSelectRunning:手动选课运行状态 * - autoJumpRunning:自动跳转运行状态 * - 使用 GM_setValue 保存自动跳转进度,页面刷新后可继续 * * 注意事项: * ==================== * * 1. 两个功能可以同时开启,实现全自动刷课 * 2. 建议先开启"手动选课",再开启"自动跳转" * 3. 可以随时点击"停止"按钮停止运行 * 4. 页面刷新后会自动检测进度并继续 * 5. 请确保在正确的课程页面使用 */ var manualSelectMode = false; var manualMinMinutes = 8; var manualMaxMinutes = 10; var manualSelectRunning = false; var manualSelectButton = null; function StartRunCourse() { for(var i=0;i<300;i++) { sendHeartBeatAjax(); } } function AddTime(seconds) { var secondsPerHeartbeat = 30; var totalHeartbeats = Math.ceil(seconds / secondsPerHeartbeat); console.log('开始快速增加时长...'); console.log('需要发送心跳包: ' + totalHeartbeats + '次'); var heartbeatCount = 0; for(var i = 0; i < totalHeartbeats; i++) { sendHeartBeatAjax(); heartbeatCount++; console.log('已发送第 ' + heartbeatCount + '/' + totalHeartbeats + ' 次心跳包'); if(i < totalHeartbeats - 1) { var delay = 100; var start = Date.now(); while(Date.now() - start < delay) { } } } console.log('✓ 已发送 ' + totalHeartbeats + ' 次心跳包'); } function toggleSettingsPanel() { var panel = document.getElementById('settingsPanel'); if(panel.style.display === 'none') { panel.style.display = 'block'; } else { panel.style.display = 'none'; } } function showHelpModal() { var modal = document.createElement('div'); modal.id = 'helpModal'; modal.style.cssText = 'position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 10000; display: flex; align-items: center; justify-content: center;'; var modalContent = document.createElement('div'); modalContent.style.cssText = 'background: white; padding: 20px; border-radius: 8px; max-width: 500px; max-height: 80vh; overflow-y: auto; box-shadow: 0 4px 20px rgba(0,0,0,0.3);'; var modalTitle = document.createElement('h2'); modalTitle.textContent = '使用说明'; modalTitle.style.cssText = 'margin: 0 0 15px 0; color: #333; font-size: 18px;'; modalContent.appendChild(modalTitle); var modalText = document.createElement('div'); modalText.innerHTML = ` 手动刷课:
您可以直接点击上面的增加时间数来手动的刷课。

自动跳转:
点击自动跳转之后只会自动跳转,快速的,在延迟5秒后自动跳转课程。

全自动刷课:
如果想要全自动刷课,您需要先启动手动选课功能,然后再启动开始自动跳转。

手动选课:
您也可以只启动手动选课功能。启动手动选课功能之后,插件会在后台实时监听,前台一旦页面发生跳转,您选择其他课程之后会自动帮您注入时间。 `; modalText.style.cssText = 'line-height: 1.8; color: #333; font-size: 14px;'; modalContent.appendChild(modalText); var closeButton = document.createElement('button'); closeButton.textContent = '关闭'; closeButton.style.cssText = 'margin-top: 20px; padding: 10px 30px; background-color: #4CAF50; color: white; border: none; border-radius: 5px; cursor: pointer; font-size: 14px; width: 100%;'; closeButton.onclick = function() { document.body.removeChild(modal); }; modalContent.appendChild(closeButton); modal.appendChild(modalContent); document.body.appendChild(modal); modal.onclick = function(e) { if(e.target === modal) { document.body.removeChild(modal); } }; } /** * 手动选课功能 - 自动发送心跳包 * 功能说明: * 1. 启动后自动发送心跳包(8-10分钟) * 2. 在后台实时观察页面 * 3. 页面切换后自动对新页面发送心跳包 * 4. 配合自动跳转使用,实现全自动刷课 * * 使用方法: * 1. 打开任意课程页面 * 2. 点击"手动选课"按钮启动 * 3. 脚本会自动发送心跳包 * 4. 页面切换后自动继续发送心跳包 * 5. 点击"停止手动选课"按钮停止 * * 开发方法: * - 从 units 变量或 URL 中获取课程ID * - 使用 setInterval 定时发送心跳包(每30秒一次) * - 随机生成8-10分钟的学习时长 * - 心跳包通过 sendHeartBeatAjax() 函数发送 * - 使用 GM_setValue 保存运行状态 * - 页面加载时自动检测状态并启动 */ function ManualSelectAuto(minMinutes, maxMinutes) { console.log('=== ManualSelectAuto 函数开始执行 ==='); console.log('参数:minMinutes=' + minMinutes + ', maxMinutes=' + maxMinutes); var isRunning = GM_getValue('manualSelectRunning', false); if(isRunning) { console.log('手动选课已在运行,停止所有功能...'); stopAll(); alert('手动选课和自动跳转已全部停止!'); return; } console.log('启动手动选课...'); manualSelectRunning = true; GM_setValue('manualSelectRunning', true); if(manualSelectButton) { manualSelectButton.textContent = '停止手动选课'; manualSelectButton.style.backgroundColor = '#f44336'; } startHeartbeat(minMinutes, maxMinutes); } function updateButtonState(button, text, color) { if(!button) return; button.textContent = text; button.style.backgroundColor = color; } function stopAll() { console.log('=== stopAll 函数开始执行 ==='); manualSelectRunning = false; autoJumpRunning = false; GM_setValue('manualSelectRunning', false); GM_setValue('autoJumpRunning', false); updateButtonState(manualSelectButton, '手动选课', '#4CAF50'); updateButtonState(autoJumpButton, '开始自动跳转', '#9C27B0'); console.log('所有功能已停止'); } async function startHeartbeat(minMinutes, maxMinutes) { console.log('=== startHeartbeat 函数开始执行 ==='); var activityId = null; function getActivityId() { if(typeof units !== 'undefined' && units && units.length > 0) { console.log('使用 units 变量获取当前课程ID'); for(var i=0;i 0) { for(var j=0;j setTimeout(resolve, 500)); if(!activityId) { console.log('第一次获取失败,再次尝试...'); getActivityId(); } await new Promise(resolve => setTimeout(resolve, 500)); if(!activityId) { console.log('第二次获取失败,再次尝试...'); getActivityId(); } await new Promise(resolve => setTimeout(resolve, 500)); if(!activityId) { console.log('第三次获取失败,再次尝试...'); getActivityId(); } await new Promise(resolve => setTimeout(resolve, 500)); var isRunning = GM_getValue('manualSelectRunning', false); if(!isRunning || !manualSelectRunning) { console.log('❌ 手动选课已停止,取消发送心跳包!'); return; } if(!activityId) { console.log('❌ 错误:未找到当前课程ID!'); return; } var randomMinutes = Math.floor(Math.random() * (maxMinutes - minMinutes + 1)) + minMinutes; var randomSeconds = randomMinutes * 60; var secondsPerHeartbeat = 30; var totalHeartbeats = Math.ceil(randomSeconds / secondsPerHeartbeat); console.log('课程信息:'); console.log(' - ID: ' + activityId); console.log(' - 随机时长: ' + randomMinutes + '分钟'); console.log(' - 需要发送心跳包: ' + totalHeartbeats + '次'); console.log('开始快速发送心跳包...'); var heartbeatCount = 0; for(var i = 0; i < totalHeartbeats; i++) { var isRunning = GM_getValue('manualSelectRunning', false); if(!isRunning || !manualSelectRunning) { console.log('❌ 手动选课已停止,强制终止发送心跳包!'); updateButtonState(manualSelectButton, '手动选课', '#4CAF50'); return; } sendHeartBeatAjax(); heartbeatCount++; console.log('已发送第 ' + heartbeatCount + '/' + totalHeartbeats + ' 次心跳包'); if(i < totalHeartbeats - 1) { await new Promise(resolve => setTimeout(resolve, 100)); } } var checkRunning = GM_getValue('manualSelectRunning', false); if(!checkRunning || !manualSelectRunning) { console.log('❌ 手动选课已停止,强制终止发送心跳包!'); updateButtonState(manualSelectButton, '手动选课', '#4CAF50'); return; } console.log('✓ 已发送 ' + totalHeartbeats + ' 次心跳包'); console.log('✓ 当前课程完成!'); var isRunning = GM_getValue('manualSelectRunning', false); if(isRunning) { console.log('手动选课仍在运行,等待页面切换...'); console.log('页面切换后会自动发送新的心跳包'); } } /** * 调试功能 - 获取下一页网址 * 功能说明: * 1. 收集所有课程的 URL * 2. 获取下一个课程的网址(不跳转) * 3. 在控制台输出详细信息 * 4. 用于调试 URL 生成逻辑 * * 使用方法: * 1. 打开任意课程页面 * 2. 点击"获取下一页网址"按钮 * 3. 在 F12 控制台查看下一页的详细信息 */ function DebugGetNextUrl() { console.log('=== DebugGetNextUrl 函数开始执行 ==='); var allCourses = []; var currentActivityId = null; var currentCourseVersionId = null; var urlParams = new URLSearchParams(window.location.search); currentActivityId = urlParams.get('activityId'); currentCourseVersionId = urlParams.get('courseVersionId'); console.log('当前课程ID: ' + currentActivityId); console.log('当前courseVersionId: ' + currentCourseVersionId); if(typeof units !== 'undefined' && units && units.length > 0) { console.log('使用 units 变量获取课程列表'); for(var i=0;i 0) { for(var j=0;j= allCourses.length) { console.log('✓ 已经是最后一个课程!'); alert('已经是最后一个课程!'); return; } var nextCourse = allCourses[nextIndex]; console.log('========== 下一页信息 =========='); console.log('当前索引: ' + currentIndex); console.log('下一个索引: ' + nextIndex); console.log('下一个课程ID: ' + nextCourse.id); console.log('下一个课程名称: ' + nextCourse.name); console.log('下一个课程URL: ' + nextCourse.url); console.log('================================'); alert('下一页网址已输出到控制台 (F12)'); } /** * 调试功能 - 跳转一次 * 功能说明: * 1. 收集所有课程的 URL * 2. 只跳转到下一个课程(不保存进度) * 3. 不会自动继续跳转 * 4. 用于调试自动跳转功能 * * 使用方法: * 1. 打开任意课程页面 * 2. 点击"跳转一次 (Debug)"按钮 * 3. 脚本会跳转到下一个课程 * 4. 需要手动再次点击按钮才能继续跳转 * * 开发方法: * - 从 units 变量或 DOM 中获取所有课程列表 * - 使用 window.location.href 跳转页面 * - 不保存进度,每次都从当前课程开始 * - 不使用自动继续逻辑 */ function DebugJumpOnce() { console.log('=== DebugJumpOnce 函数开始执行 ==='); var allCourses = []; var currentActivityId = null; var currentCourseVersionId = null; var failedCourses = GM_getValue('failedCourses', []); var urlParams = new URLSearchParams(window.location.search); currentActivityId = urlParams.get('activityId'); currentCourseVersionId = urlParams.get('courseVersionId'); console.log('当前课程ID: ' + currentActivityId); console.log('当前courseVersionId: ' + currentCourseVersionId); console.log('失败课程数量: ' + failedCourses.length); console.log('=== 当前页面源代码 ==='); console.log(document.documentElement.outerHTML); if(typeof units !== 'undefined' && units && units.length > 0) { console.log('使用 units 变量获取课程列表'); for(var i=0;i 0) { for(var j=0;j= allCourses.length) { console.log('✓ 已经是最后一个课程!'); alert('已经是最后一个课程!'); return; } var nextCourse = allCourses[nextIndex]; console.log('准备跳转:'); console.log(' - 当前索引: ' + currentIndex); console.log(' - 下一个索引: ' + nextIndex); console.log(' - 下一个课程ID: ' + nextCourse.id); console.log(' - 下一个课程名称: ' + nextCourse.name); console.log(' - 下一个课程URL: ' + nextCourse.url); var jumpDelay = GM_getValue('jumpDelay', 2); console.log('等待' + jumpDelay + '秒后跳转...'); setTimeout(function() { console.log('跳转到课程: ' + nextCourse.name); window.location.href = nextCourse.url; }, jumpDelay * 1000); } /** * 自动跳转功能 - 自动遍历所有课程 * 功能说明: * 1. 收集所有课程的 URL * 2. 自动跳转到下一个课程(等待5秒) * 3. 不会发送心跳包 * 4. 可以随时停止 * * 使用方法: * 1. 打开任意课程页面 * 2. 点击"开始自动跳转"按钮 * 3. 脚本会自动跳转到下一个课程 * 4. 点击"停止自动跳转"按钮可以停止 * * 开发方法: * - 从 units 变量或 DOM 中获取所有课程列表 * - 使用 window.location.href 跳转页面 * - 使用 setTimeout 延迟5秒后跳转 * - 使用 GM_setValue 保存当前进度 * - 页面加载时自动检测进度并继续跳转 * * 注意事项: * - 此功能只负责跳转,不发送心跳包 * - 可以与"手动选课"功能配合使用,实现全自动刷课 * - 建议先开启"手动选课",再开启"自动跳转" */ var autoJumpRunning = false; var autoJumpButton = null; function AllCourseAuto(minMinutes, maxMinutes) { console.log('=== AllCourseAuto 函数开始执行 ==='); console.log('参数:minMinutes=' + minMinutes + ', maxMinutes=' + maxMinutes); var jumpDelay = GM_getValue('jumpDelay', 2); console.log('跳转延迟: ' + jumpDelay + '秒'); if(autoJumpRunning) { console.log('自动跳转已在运行中,停止所有功能...'); stopAll(); alert('自动跳转和手动选课已全部停止!'); return; } var allCourses = []; var currentCourseVersionId = null; var urlParams = new URLSearchParams(window.location.search); currentCourseVersionId = urlParams.get('courseVersionId'); console.log('当前courseVersionId: ' + currentCourseVersionId); if(typeof units !== 'undefined' && units && units.length > 0) { console.log('使用 units 变量获取课程列表'); for(var i=0;i 0) { for(var j=0;j= allCourses.length) { console.log('✓ 全部课程跳转完成!'); autoJumpRunning = false; GM_setValue('autoJumpRunning', false); if(autoJumpButton) { autoJumpButton.textContent = '已完成'; autoJumpButton.style.backgroundColor = '#4CAF50'; } alert('全部课程跳转完成!'); return; } var course = allCourses[currentIndex]; console.log('课程信息:'); console.log(' - ID: ' + course.id); console.log(' - 名称: ' + course.name); console.log(' - URL: ' + course.url); console.log('等待' + jumpDelay + '秒后跳转...'); setTimeout(function() { if(!autoJumpRunning) { console.log('自动跳转已停止,取消跳转'); return; } console.log('跳转到课程: ' + course.name); window.location.href = course.url; }, jumpDelay * 1000); } console.log('启动第一个课程跳转...'); processNextCourse(); } function InjectClickButton() { // 原方法:一键完成本课程功能 // var StartTree = document.evaluate('//*[@id="courseContent_1_switch"]',document).iterateNext(); // var ShowText =document.evaluate('//*[@id="courseContent_1_a"]',document).iterateNext(); // ShowText.textContent = "点击一键完成本课"; // ShowText.style.color="red"; // StartTree.removeAttribute("treenode_switch"); // StartTree.onclick=()=>{StartRunCourse()} var savedLeft = GM_getValue('buttonLeft', '20px'); var savedTop = GM_getValue('buttonTop', null); var savedBottom = GM_getValue('buttonBottom', '20px'); var buttonContainer = document.createElement('div'); buttonContainer.style.cssText = 'position: fixed; bottom: ' + savedBottom + '; left: ' + savedLeft + '; z-index: 9999; background: white; padding: 10px; border: 2px solid #4CAF50; border-radius: 5px; box-shadow: 0 2px 10px rgba(0,0,0,0.3); cursor: move;'; if(savedTop) { buttonContainer.style.top = savedTop; buttonContainer.style.bottom = 'auto'; } var title = document.createElement('div'); title.textContent = '江苏开放大学刷课脚本'; title.style.cssText = 'font-weight: bold; margin-bottom: 10px; color: #333; user-select: none;'; buttonContainer.appendChild(title); var buttons = [ { text: '+30秒', seconds: 30, color: '#2196F3' }, { text: '+1分钟', seconds: 60, color: '#4CAF50' }, { text: '+5分钟', seconds: 300, color: '#FF9800' } ]; buttons.forEach(btn => { var button = document.createElement('button'); button.textContent = btn.text; button.style.cssText = `margin: 5px; padding: 8px 15px; background-color: ${btn.color}; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 14px;`; button.onclick = (e) => { e.stopPropagation(); AddTime(btn.seconds); }; buttonContainer.appendChild(button); }); var divider2 = document.createElement('div'); divider2.style.cssText = 'border-top: 1px solid #ddd; margin: 10px 0;'; buttonContainer.appendChild(divider2); var secondRow = document.createElement('div'); secondRow.style.cssText = 'display: flex; align-items: center; gap: 5px;'; var tenMinButton = document.createElement('button'); tenMinButton.textContent = '+10分钟'; tenMinButton.style.cssText = 'margin: 5px; padding: 8px 15px; background-color: #E91E63; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 14px;'; tenMinButton.onclick = (e) => { e.stopPropagation(); AddTime(600); }; secondRow.appendChild(tenMinButton); var randomButton = document.createElement('button'); randomButton.textContent = '+随机'; randomButton.style.cssText = 'margin: 5px; padding: 8px 10px; background-color: #00BCD4; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 14px; width: 80px;'; randomButton.onclick = (e) => { e.stopPropagation(); var minMin = parseInt(document.getElementById('randomMin').value) || 8; var maxMin = parseInt(document.getElementById('randomMax').value) || 10; GM_setValue('randomMin', minMin); GM_setValue('randomMax', maxMin); var randomMinutes = Math.floor(Math.random() * (maxMin - minMin + 1)) + minMin; AddTime(randomMinutes * 60); }; secondRow.appendChild(randomButton); var settingsIcon = document.createElement('span'); settingsIcon.innerHTML = '⚙️'; settingsIcon.style.cssText = 'cursor: pointer; font-size: 18px; margin-left: 5px;'; settingsIcon.onclick = (e) => { e.stopPropagation(); toggleSettingsPanel(); }; secondRow.appendChild(settingsIcon); buttonContainer.appendChild(secondRow); var settingsPanel = document.createElement('div'); settingsPanel.id = 'settingsPanel'; settingsPanel.style.cssText = 'display: none; margin-top: 10px; padding: 10px; background: #f5f5f5; border-radius: 3px;'; var randomTitle = document.createElement('div'); randomTitle.textContent = '随机时间设置'; randomTitle.style.cssText = 'font-weight: bold; margin-bottom: 8px; color: #333; font-size: 12px;'; settingsPanel.appendChild(randomTitle); var randomSettings = document.createElement('div'); randomSettings.style.cssText = 'display: flex; align-items: center; gap: 5px;'; var randomMinLabel = document.createElement('span'); randomMinLabel.textContent = '最小:'; randomMinLabel.style.cssText = 'font-size: 12px;'; randomSettings.appendChild(randomMinLabel); var randomMinInput = document.createElement('input'); randomMinInput.type = 'number'; randomMinInput.value = GM_getValue('randomMin', '8'); randomMinInput.id = 'randomMin'; randomMinInput.style.cssText = 'width: 50px; padding: 3px;'; randomMinInput.onchange = function() { GM_setValue('randomMin', parseInt(this.value) || 8); }; randomSettings.appendChild(randomMinInput); var randomMaxLabel = document.createElement('span'); randomMaxLabel.textContent = '最大:'; randomMaxLabel.style.cssText = 'font-size: 12px;'; randomSettings.appendChild(randomMaxLabel); var randomMaxInput = document.createElement('input'); randomMaxInput.type = 'number'; randomMaxInput.value = GM_getValue('randomMax', '10'); randomMaxInput.id = 'randomMax'; randomMaxInput.style.cssText = 'width: 50px; padding: 3px;'; randomMaxInput.onchange = function() { GM_setValue('randomMax', parseInt(this.value) || 10); }; randomSettings.appendChild(randomMaxInput); var unitLabel = document.createElement('span'); unitLabel.textContent = '分钟'; unitLabel.style.cssText = 'font-size: 12px;'; randomSettings.appendChild(unitLabel); settingsPanel.appendChild(randomSettings); buttonContainer.appendChild(settingsPanel); var divider = document.createElement('div'); divider.style.cssText = 'border-top: 1px solid #ddd; margin: 10px 0;'; buttonContainer.appendChild(divider); var allCourseTitle = document.createElement('div'); allCourseTitle.textContent = '自动跳转'; allCourseTitle.style.cssText = 'font-weight: bold; margin-bottom: 5px; color: #333; font-size: 12px;'; buttonContainer.appendChild(allCourseTitle); var autoJumpContainer = document.createElement('div'); autoJumpContainer.style.cssText = 'display: flex; align-items: center; gap: 5px;'; var allCourseButton = document.createElement('button'); allCourseButton.textContent = '开始自动跳转'; allCourseButton.style.cssText = 'flex: 1; margin: 5px; padding: 8px 15px; background-color: #9C27B0; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 14px;'; allCourseButton.onclick = (e) => { e.stopPropagation(); console.log('=== 自动跳转按钮被点击 ==='); var jumpDelay = parseInt(document.getElementById('jumpDelay').value) || 5; GM_setValue('jumpDelay', jumpDelay); AllCourseAuto(8, 10); }; autoJumpContainer.appendChild(allCourseButton); autoJumpButton = allCourseButton; var jumpDelayInput = document.createElement('input'); jumpDelayInput.type = 'number'; jumpDelayInput.value = GM_getValue('jumpDelay', '2'); jumpDelayInput.id = 'jumpDelay'; jumpDelayInput.style.cssText = 'width: 50px; padding: 5px; border: 1px solid #ddd; border-radius: 3px; font-size: 12px;'; autoJumpContainer.appendChild(jumpDelayInput); var jumpDelayLabel = document.createElement('span'); jumpDelayLabel.textContent = '秒'; jumpDelayLabel.style.cssText = 'font-size: 12px; color: #666;'; autoJumpContainer.appendChild(jumpDelayLabel); buttonContainer.appendChild(autoJumpContainer); var divider3 = document.createElement('div'); divider3.style.cssText = 'border-top: 1px solid #ddd; margin: 10px 0;'; buttonContainer.appendChild(divider3); var manualTitle = document.createElement('div'); manualTitle.textContent = '手动选课模式'; manualTitle.style.cssText = 'font-weight: bold; margin-bottom: 5px; color: #333; font-size: 12px;'; buttonContainer.appendChild(manualTitle); var manualTimeContainer = document.createElement('div'); manualTimeContainer.style.cssText = 'margin: 5px 0;'; var manualMinLabel = document.createElement('span'); manualMinLabel.textContent = '最小(分钟):'; manualMinLabel.style.cssText = 'font-size: 12px;'; manualTimeContainer.appendChild(manualMinLabel); var manualMinInput = document.createElement('input'); manualMinInput.type = 'number'; manualMinInput.value = GM_getValue('manualMin', '8'); manualMinInput.id = 'manualMin'; manualMinInput.style.cssText = 'width: 50px; margin: 0 5px; padding: 3px;'; manualMinInput.onchange = function() { GM_setValue('manualMin', parseInt(this.value) || 8); }; manualTimeContainer.appendChild(manualMinInput); var manualMaxLabel = document.createElement('span'); manualMaxLabel.textContent = '最大(分钟):'; manualMaxLabel.style.cssText = 'font-size: 12px;'; manualTimeContainer.appendChild(manualMaxLabel); var manualMaxInput = document.createElement('input'); manualMaxInput.type = 'number'; manualMaxInput.value = GM_getValue('manualMax', '10'); manualMaxInput.id = 'manualMax'; manualMaxInput.style.cssText = 'width: 50px; margin: 0 5px; padding: 3px;'; manualMaxInput.onchange = function() { GM_setValue('manualMax', parseInt(this.value) || 10); }; manualTimeContainer.appendChild(manualMaxInput); buttonContainer.appendChild(manualTimeContainer); var manualButtonContainer = document.createElement('div'); manualButtonContainer.style.cssText = 'display: flex; gap: 5px; margin: 5px 0;'; var manualButton = document.createElement('button'); manualButton.textContent = '启动手动选课'; manualButton.style.cssText = 'flex: 1; padding: 8px 15px; background-color: #FF5722; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 14px;'; manualButton.onclick = (e) => { e.stopPropagation(); console.log('=== 手动选课按钮被点击 ==='); var minMin = parseInt(document.getElementById('manualMin').value) || 8; var maxMin = parseInt(document.getElementById('manualMax').value) || 10; GM_setValue('manualMin', minMin); GM_setValue('manualMax', maxMin); GM_setValue('manualSelectMode', true); manualSelectMode = true; manualMinMinutes = minMin; manualMaxMinutes = maxMin; manualSelectButton = manualButton; console.log('手动选课模式已启动,随机时间:' + minMin + '~' + maxMin + '分钟'); ManualSelectAuto(minMin, maxMin); }; manualButtonContainer.appendChild(manualButton); var stopManualButton = document.createElement('button'); stopManualButton.textContent = '停止手动选课'; stopManualButton.style.cssText = 'flex: 1; padding: 8px 15px; background-color: #607D8B; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 14px;'; stopManualButton.onclick = (e) => { e.stopPropagation(); console.log('=== 停止手动选课按钮被点击 ==='); GM_setValue('manualSelectMode', false); manualSelectMode = false; manualSelectRunning = false; if(manualSelectButton) { manualSelectButton.textContent = '启动手动选课'; manualSelectButton.style.backgroundColor = '#FF5722'; } console.log('手动选课模式已停止'); alert('手动选课模式已停止!'); }; manualButtonContainer.appendChild(stopManualButton); buttonContainer.appendChild(manualButtonContainer); var divider5 = document.createElement('div'); divider5.style.cssText = 'border-top: 1px solid #ddd; margin: 10px 0;'; buttonContainer.appendChild(divider5); var debugTitle = document.createElement('div'); debugTitle.textContent = 'Debug 栏目 ▶'; debugTitle.style.cssText = 'font-weight: bold; margin-bottom: 5px; color: #666; font-size: 12px; cursor: pointer; user-select: none;'; debugTitle.onclick = (e) => { e.stopPropagation(); var debugContent = document.getElementById('debugContent'); if(debugContent.style.display === 'none') { debugContent.style.display = 'block'; debugTitle.textContent = 'Debug 栏目 ▼'; } else { debugContent.style.display = 'none'; debugTitle.textContent = 'Debug 栏目 ▶'; } }; buttonContainer.appendChild(debugTitle); var debugContent = document.createElement('div'); debugContent.id = 'debugContent'; debugContent.style.cssText = 'display: none; margin: 5px 0;'; var debugGetUrlButton = document.createElement('button'); debugGetUrlButton.textContent = '获取下一页网址'; debugGetUrlButton.style.cssText = 'margin: 5px; padding: 8px 15px; background-color: #2196F3; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 14px; width: 100%;'; debugGetUrlButton.onclick = (e) => { e.stopPropagation(); console.log('=== 获取下一页网址按钮被点击 ==='); DebugGetNextUrl(); }; debugContent.appendChild(debugGetUrlButton); var debugJumpButton2 = document.createElement('button'); debugJumpButton2.textContent = '跳转一次 (Debug)'; debugJumpButton2.style.cssText = 'margin: 5px; padding: 8px 15px; background-color: #FF5722; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 14px; width: 100%;'; debugJumpButton2.onclick = (e) => { e.stopPropagation(); console.log('=== 跳转一次按钮被点击 ==='); DebugJumpOnce(); }; debugContent.appendChild(debugJumpButton2); buttonContainer.appendChild(debugContent); var divider6 = document.createElement('div'); divider6.style.cssText = 'border-top: 1px solid #ddd; margin: 10px 0;'; buttonContainer.appendChild(divider6); var helpButton = document.createElement('button'); helpButton.textContent = '使用说明'; helpButton.style.cssText = 'margin: 5px; padding: 8px 15px; background-color: #607D8B; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 14px; width: 100%;'; helpButton.onclick = (e) => { e.stopPropagation(); showHelpModal(); }; buttonContainer.appendChild(helpButton); document.body.appendChild(buttonContainer); var isDragging = false; var offsetX, offsetY; buttonContainer.addEventListener('mousedown', function(e) { if (e.target.tagName === 'BUTTON' || e.target.tagName === 'INPUT') return; isDragging = true; offsetX = e.clientX - buttonContainer.offsetLeft; offsetY = e.clientY - buttonContainer.offsetTop; buttonContainer.style.cursor = 'grabbing'; }); document.addEventListener('mousemove', function(e) { if (!isDragging) return; buttonContainer.style.left = (e.clientX - offsetX) + 'px'; buttonContainer.style.top = (e.clientY - offsetY) + 'px'; buttonContainer.style.bottom = 'auto'; buttonContainer.style.right = 'auto'; }); document.addEventListener('mouseup', function() { if(isDragging) { GM_setValue('buttonLeft', buttonContainer.style.left); GM_setValue('buttonTop', buttonContainer.style.top); GM_setValue('buttonBottom', 'auto'); } isDragging = false; buttonContainer.style.cursor = 'move'; }); manualSelectMode = GM_getValue('manualSelectMode', false); manualMinMinutes = GM_getValue('manualMin', 8); manualMaxMinutes = GM_getValue('manualMax', 10); if(manualSelectMode) { console.log('检测到手动选课模式已激活,但需要手动启动'); GM_setValue('manualSelectMode', false); manualSelectMode = false; if(manualSelectButton) { manualSelectButton.textContent = '启动手动选课'; manualSelectButton.style.backgroundColor = '#FF5722'; } } autoJumpRunning = GM_getValue('autoJumpRunning', false); if(autoJumpRunning) { console.log('检测到自动跳转正在运行'); if(autoJumpButton) { autoJumpButton.textContent = '停止自动跳转'; autoJumpButton.style.backgroundColor = '#f44336'; } } manualSelectRunning = GM_getValue('manualSelectRunning', false); if(manualSelectRunning) { console.log('检测到手动选课正在运行'); if(manualSelectButton) { manualSelectButton.textContent = '停止手动选课'; manualSelectButton.style.backgroundColor = '#f44336'; } } } function GetDocId() { var DocActivy = document.getElementsByClassName('activity doc'); var DocIds = [] for(var i=0;i { e.stopPropagation(); CrackCopyPaste(); }; buttonContainer.appendChild(crackButton); var statusText = document.createElement('div'); statusText.id = 'crackStatus'; statusText.textContent = '未破解'; statusText.style.cssText = 'margin-top: 10px; padding: 5px; background: #f5f5f5; border-radius: 3px; font-size: 12px; color: #666; text-align: center;'; buttonContainer.appendChild(statusText); document.body.appendChild(buttonContainer); var isDragging = false; var offsetX, offsetY; buttonContainer.addEventListener('mousedown', function(e) { if (e.target.tagName === 'BUTTON') return; isDragging = true; offsetX = e.clientX - buttonContainer.offsetLeft; offsetY = e.clientY - buttonContainer.offsetTop; buttonContainer.style.cursor = 'grabbing'; }); document.addEventListener('mousemove', function(e) { if (!isDragging) return; buttonContainer.style.left = (e.clientX - offsetX) + 'px'; buttonContainer.style.top = (e.clientY - offsetY) + 'px'; buttonContainer.style.bottom = 'auto'; buttonContainer.style.right = 'auto'; }); document.addEventListener('mouseup', function() { isDragging = false; buttonContainer.style.cursor = 'move'; }); } function CrackCopyPaste() { console.log('=== 开始破解复制粘贴限制 ==='); var allElements = document.querySelectorAll('*'); var count = 0; allElements.forEach(function(element) { var originalStyle = element.style.cssText; element.style.userSelect = 'auto'; element.style.webkitUserSelect = 'auto'; element.style.mozUserSelect = 'auto'; element.style.msUserSelect = 'auto'; element.oncopy = null; element.oncut = null; element.onpaste = null; element.removeAttribute('oncopy'); element.removeAttribute('oncut'); element.removeAttribute('onpaste'); element.removeEventListener('copy', function(e) { e.preventDefault(); }); element.removeEventListener('cut', function(e) { e.preventDefault(); }); element.removeEventListener('paste', function(e) { e.preventDefault(); }); count++; }); document.oncopy = null; document.oncut = null; document.onpaste = null; document.removeEventListener('copy', function(e) { e.preventDefault(); }); document.removeEventListener('cut', function(e) { e.preventDefault(); }); document.removeEventListener('paste', function(e) { e.preventDefault(); }); var style = document.createElement('style'); style.innerHTML = ` * { user-select: auto !important; -webkit-user-select: auto !important; -moz-user-select: auto !important; -ms-user-select: auto !important; } `; document.head.appendChild(style); console.log('✓ 已处理 ' + count + ' 个元素'); console.log('✓ 复制粘贴限制已破解!'); var statusText = document.getElementById('crackStatus'); if(statusText) { statusText.textContent = '✓ 已破解 - 可以自由复制粘贴'; statusText.style.color = '#4CAF50'; statusText.style.background = '#E8F5E9'; } alert('复制粘贴限制已破解!现在可以自由复制粘贴了。'); } /** * 主运行函数 * 功能说明: * 1. 检测当前页面类型 * 2. 注入相应的功能按钮 * 3. 如果自动跳转正在运行,页面刷新后自动继续 * * 开发方法: * - 使用 location.href 判断页面类型 * - display 页面:注入刷课按钮 * - showHomeworkByStatus 页面:注入复制按钮 * - 其他页面:注入心跳包函数到全局 * - 使用 GM_getValue 检查自动跳转是否正在运行 */ function MainRun() { var isAutoJumpRunning = GM_getValue('autoJumpRunning', false); var isManualSelectRunning = GM_getValue('manualSelectRunning', false); var failedCourses = GM_getValue('failedCourses', []); function checkPageError() { var bodyText = document.body ? document.body.innerText : ''; if(bodyText.includes('抱歉,您访问的页面不存在') || bodyText.includes('访问失效') || bodyText.includes('页面不存在')) { console.log('❌ 检测到页面错误,跳转失败!'); console.log('可能此栏目已经完毕,学科完毕'); var currentUrl = window.location.href; var urlParams = new URLSearchParams(window.location.search); var currentActivityId = urlParams.get('activityId'); if(currentActivityId && !failedCourses.includes(currentActivityId)) { failedCourses.push(currentActivityId); GM_setValue('failedCourses', failedCourses); console.log('已记录失败课程ID: ' + currentActivityId); } alert('访问失效!跳过此课程,继续下一个。'); setTimeout(function() { window.history.back(); }, 1000); return true; } return false; } if(checkPageError()) { return; } if(isAutoJumpRunning && location.href.includes("display")) { var jumpDelay = GM_getValue('jumpDelay', 2); console.log('检测到自动跳转正在运行,' + jumpDelay + '秒后继续跳转...'); var urlParams = new URLSearchParams(window.location.search); var currentCourseVersionId = urlParams.get('courseVersionId'); console.log('当前courseVersionId: ' + currentCourseVersionId); var cancelTimer = false; var countdown = 1; var totalSeconds = countdown + jumpDelay; var notice = document.createElement('div'); notice.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%, -50%);background:#f44336;color:white;padding:20px 40px;border-radius:10px;z-index:99999;cursor:pointer;font-size:32px;font-weight:bold;text-align:center;'; notice.innerHTML = '
点此框可以终止任务
' + totalSeconds + '秒
'; notice.onclick = function() { cancelTimer = true; stopAll(); document.body.removeChild(notice); }; document.body.appendChild(notice); var countdownInterval = setInterval(function() { countdown--; totalSeconds = countdown + jumpDelay; notice.innerHTML = '
点此框可以终止任务
' + totalSeconds + '秒
'; if(countdown <= 0) { clearInterval(countdownInterval); if(cancelTimer) return; document.body.removeChild(notice); var isAutoJumpStillRunning = GM_getValue('autoJumpRunning', false); if(!isAutoJumpStillRunning || !autoJumpRunning) { console.log('❌ 自动跳转已停止,取消跳转!'); return; } console.log('倒计时结束,等待 ' + jumpDelay + ' 秒后跳转...'); var jumpCountdown = jumpDelay; var jumpNotice = document.createElement('div'); jumpNotice.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%, -50%);background:#f44336;color:white;padding:20px 40px;border-radius:10px;z-index:99999;cursor:pointer;font-size:32px;font-weight:bold;text-align:center;'; jumpNotice.innerHTML = '
点此框可以终止任务
' + jumpCountdown + '秒
'; jumpNotice.onclick = function() { cancelTimer = true; stopAll(); document.body.removeChild(jumpNotice); }; document.body.appendChild(jumpNotice); var jumpInterval = setInterval(function() { jumpCountdown--; if(jumpCountdown >= 0) { jumpNotice.innerHTML = '
点此框可以终止任务
' + jumpCountdown + '秒
'; } if(jumpCountdown < 0) { clearInterval(jumpInterval); if(document.body.contains(jumpNotice)) { document.body.removeChild(jumpNotice); } if(!autoJumpRunning || !GM_getValue('autoJumpRunning', false)) { console.log('❌ 自动跳转已停止,取消跳转!'); return; } var allCourses = []; if(typeof units !== 'undefined' && units && units.length > 0) { for(var i=0;i 0) { for(var j=0;j= allCourses.length) { console.log('✓ 全部课程跳转完成!'); GM_setValue('autoJumpRunning', false); alert('全部课程跳转完成!'); return; } var nextCourse = allCourses[nextIndex]; console.log('自动跳转到第 ' + (nextIndex + 1) + ' 个课程: ' + nextCourse.name); var topNotice = document.createElement('div'); topNotice.style.cssText = 'position:fixed;top:0;left:0;width:100%;background:#9C27B0;color:white;padding:10px;text-align:center;z-index:99998;font-size:16px;font-weight:bold;'; topNotice.textContent = '学科成功'; document.body.appendChild(topNotice); setTimeout(function() { if(document.body.contains(topNotice)) { document.body.removeChild(topNotice); } window.location.href = nextCourse.url; }, 1000); } }, 1000); } }, 1000); } if(isManualSelectRunning && location.href.includes("display")) { console.log('检测到手动选课正在运行,自动发送心跳包...'); manualSelectRunning = true; if(manualSelectButton) { manualSelectButton.textContent = '停止手动选课'; manualSelectButton.style.backgroundColor = '#f44336'; } var savedMin = GM_getValue('manualMin', 8); var savedMax = GM_getValue('manualMax', 10); console.log('使用保存的时间设置:' + savedMin + '~' + savedMax + '分钟'); startHeartbeat(savedMin, savedMax); } if(location.href.includes("display")) { InjectClickButton(); } else if(location.href.includes("showHomeworkByStatus")) { InjectCopyButton(); } else { unsafeWindow.DocHeart_Send = DocHeart_Send; unsafeWindow.VideoHeart_Send = VideoHeart_Send; unsafeWindow.AudioHeart_Send = AudioHeart_Send; console.info(`%c+`,`background-image:url(https://s3.bmp.ovh/imgs/2022/03/5e6d432c16060d22.jpg);background-size: contain; background-repeat: no-repeat; color: transparent;padding: 122px 217px;`) console.info("%c当你打开控制台的时候,请记住以下几个规则:\n1.DocHeart_Send函数可以帮你刷全部的文档课程\n2.VideoHeart_Send函数可以帮你刷全部的视频课程\n3.AudioHeart_Send函数可以帮你刷全部的音频课程\n4.在课程页面右上角有手动增加时长的按钮(+30秒、+1分钟、+5分钟)\n按需使用,不要一次性全部输入,不然程序会混乱,使用过程中感觉浏览器有点卡顿是正常现象,程序在无延迟的发送网络请求,所以会发生卡顿\n当你发现一排红报错的时候,请不要犹豫,立马刷新页面,重新开始刷课。\n\n二改说明:本脚本基于原作者Pwn的开源脚本进行二次开发,增加了手动增加时长的功能。","color:red;margin-top:10px"); } } MainRun();