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