// ==UserScript==
// @name 【学习通】【刷课】【继续教育学习】【超星小助手】
// @namespace wbmb
// @version 1.0.0
// @description 支持超星视频、文档、答题
// @author wbmb
// @run-at document-end
// @storageName unrivalxxt
// @match *://*.chaoxing.com/*
// @match *://*.edu.cn/*
// @match *://*.nbdlib.cn/*
// @match *://*.hnsyu.net/*
// @match *://*.ac.cn/*
// @icon http://pan-yz.chaoxing.com/favicon.ico
// @grant unsafeWindow
// @grant GM_xmlhttpRequest
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addValueChangeListener
// @grant GM_info
// @connect mooc1-1.chaoxing.com
// @connect mooc1.chaoxing.com
// @connect mooc1-2.chaoxing.com
// @connect passport2-api.chaoxing.com
// @connect 14.29.190.187
// @connect cx.icodef.com
// @license GPL-3.0-or-later
// @original-script https://scriptcat.org/zh-CN/script-show-page/379
// @original-author unrival
// @original-license GPL-3.0-or-later
// ==/UserScript==
/**
* 学习通小助手 - 优化版
* 优化内容:
* 1. 模块化重构
* 2. 性能优化(减少DOM操作、优化定时器、网络请求优化)
* 3. 错误处理机制完善
* 4. 功能增强(智能任务调度、配置管理、缓存机制)
* 5. 安全性优化
* 6. 可维护性优化
*/
// 兼容性处理:为旧浏览器添加replaceAll方法
if (!String.prototype.replaceAll) {
String.prototype.replaceAll = function(search, replacement) {
return this.split(search).join(replacement);
};
}
(() => {
'use strict';
// 配置管理模块
const Config = {
token: GM_getValue('tikutoken', ''),
jumpType: 1, // 0:智能模式,1:遍历模式,2:不跳转
disableMonitor: 0, // 0:无操作,1:解除多端学习监控
accuracy: 60, //章节测试正确率百分比
randomDo: 1, //找不到答案时自动选择
backGround: 0, //是否对接超星挂机小助手
autoLogin: 0, //掉线是否自动登录
phoneNumber: '', //自动登录的手机号
password: '', //自动登录的密码
// 网络配置
host: 'http://14.29.190.187:54223/',
ctUrl: 'https://cx.icodef.com/wyn-nb?v=4',
// 获取配置
get(key) {
return this[key];
},
// 设置配置
set(key, value) {
this[key] = value;
if (key === 'tikutoken') {
GM_setValue('tikutoken', value);
}
}
};
// 工具函数模块
const Utils = {
// 获取URL参数
getQueryVariable(variable) {
let q = location.search.substring(1),
v = q.split("&"),
r = false;
for (let i = 0, l = v.length; i < l; i++) {
let p = v[i].split("=");
if (p[0] == variable) r = p[1];
}
return r;
},
// 获取Cookie
getCookie(name) {
return `; ${document.cookie}`.split(`; ${name}=`).pop().split(';').shift();
},
// 处理图片
handleImgs(s) {
const imgEs = s.match(/(
]*)>)/);
if (imgEs) {
for (let j = 0, k = imgEs.length; j < k; j++) {
const urls = imgEs[j].match(
/http[s]?:\/\/(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+/),
url;
if (urls) {
const url = urls[0].replace(/http[s]?:\/\//, '');
s = s.replaceAll(imgEs[j], url);
}
}
}
return s;
},
// 字符串处理
trim(s) {
return this.handleImgs(s)
.replaceAll('javascript:void(0);', '')
.replaceAll(" ", '')
.replaceAll(",", ',')
.replaceAll("。", '.')
.replaceAll(":", ':')
.replaceAll(";", ';')
.replaceAll("?", '?')
.replaceAll("(", '(')
.replaceAll(")", ')')
.replaceAll("“", '"')
.replaceAll("”", '"')
.replaceAll("!", '!')
.replaceAll("-", ' ')
.replace(/(<([^>]+)>)/ig, '')
.replace(/^\s+/ig, '')
.replace(/\s+$/ig, '');
},
// 生成时间戳
getTimestamp() {
return String(Math.round(new Date()));
},
// 格式化时间
formatTime() {
const nowTime = new Date();
const nowHour = (Array(2).join(0) + nowTime.getHours()).slice(-2);
const nowMin = (Array(2).join(0) + nowTime.getMinutes()).slice(-2);
const nowSec = (Array(2).join(0) + nowTime.getSeconds()).slice(-2);
return `${nowHour}:${nowMin}:${nowSec}`;
},
// 延迟执行
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
},
// 生成随机数
random(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
};
// 日志系统模块
const Logger = {
logArry: [],
maxLogs: 50,
// 添加日志
addLog(str, color = "black") {
if (this.logArry.length >= this.maxLogs) {
this.logArry.shift();
}
const timeStr = Utils.formatTime();
this.logArry.push(`[${timeStr}] ${str}`);
this.updateLogView();
},
// 更新日志视图
updateLogView() {
const logElement = document.getElementById('log');
if (!logElement) return;
let logStr = "";
for (let logI = 0, logLen = this.logArry.length; logI < logLen; logI++) {
logStr += this.logArry[logI] + "
";
}
// 使用文档片段减少DOM操作
const fragment = document.createDocumentFragment();
const tempDiv = document.createElement('div');
tempDiv.innerHTML = logStr;
while (tempDiv.firstChild) {
fragment.appendChild(tempDiv.firstChild);
}
logElement.innerHTML = '';
logElement.appendChild(fragment);
logElement.scrollTop = logElement.scrollHeight;
}
};
// 网络请求模块
const Network = {
// 请求缓存
cache: new Map(),
// 发送GET请求
get(url, headers = {}, timeout = 5000) {
return new Promise((resolve, reject) => {
// 检查缓存
const cacheKey = url;
if (this.cache.has(cacheKey)) {
resolve(this.cache.get(cacheKey));
return;
}
GM_xmlhttpRequest({
method: "get",
headers,
url,
timeout,
onload: (res) => {
// 缓存响应
this.cache.set(cacheKey, res);
resolve(res);
},
onerror: (err) => reject(err),
ontimeout: (err) => reject(err)
});
});
},
// 发送POST请求
post(url, data, headers = {}, timeout = 5000) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "post",
headers,
url,
data,
timeout,
onload: (res) => resolve(res),
onerror: (err) => reject(err),
ontimeout: (err) => reject(err)
});
});
},
// 清除缓存
clearCache() {
this.cache.clear();
}
};
// 任务管理模块
const TaskManager = {
missionList: {},
busyThread: 0,
stop: false,
// 添加任务
addTask(task) {
this.missionList['m' + task.jobid] = {
...task,
done: false,
running: false
};
},
// 执行任务
async executeTasks() {
if (this.stop) return;
// 检查是否有正在运行的任务
for (const itemName in this.missionList) {
if (this.missionList[itemName].running) {
setTimeout(() => this.executeTasks(), 500);
return;
}
}
// 执行下一个未完成的任务
for (const itemName in this.missionList) {
if (!this.missionList[itemName].done) {
const task = this.missionList[itemName];
task.running = true;
try {
switch (task.type) {
case 'video':
await VideoHandler.process(task);
break;
case 'document':
await DocumentHandler.process(task);
break;
case 'work':
await WorkHandler.process(task);
break;
}
} catch (error) {
Logger.addLog(`任务执行失败: ${error.message}`, 'red');
} finally {
task.running = false;
task.done = true;
}
setTimeout(() => this.executeTasks(), 500);
return;
}
}
// 所有任务完成
if (this.busyThread <= 0) {
if (Config.get('jumpType') != 2) {
unsafeWindow.top.jump = true;
Logger.addLog('所有任务处理完毕,5秒后自动下一章', 'green');
} else {
Logger.addLog('所有任务处理完毕,用户设置为不跳转,脚本已结束运行', 'green');
}
} else {
setTimeout(() => this.executeTasks(), 500);
}
},
// 增加忙碌线程数
increaseBusyThread() {
this.busyThread++;
},
// 减少忙碌线程数
decreaseBusyThread() {
this.busyThread = Math.max(0, this.busyThread - 1);
},
// 停止任务
stopTasks() {
this.stop = true;
}
};
// 视频处理模块
const VideoHandler = {
// 准备视频信息
async prepareVideo(item) {
TaskManager.increaseBusyThread();
try {
const statusUrl = `${location.protocol}//${location.host}/ananas/status/${item.property.objectid}?k=${Utils.getCookie('fid')}&flag=normal&_dc=${Utils.getTimestamp()}`;
const res = await Network.get(statusUrl, {
'Host': location.host,
'Referer': GM_getValue('vrefer', `${location.protocol}//${location.host}/ananas/modules/video/index.html?v=2022-1118-1729`),
'Sec-Fetch-Site': 'same-origin'
});
const videoInfo = JSON.parse(res.responseText);
const duration = videoInfo.duration;
const dtoken = videoInfo.dtoken;
if (!duration) {
this.addTaskToView('[无效视频]' + item.property.name);
return;
}
TaskManager.addTask({
module: item.property.module,
type: 'video',
dtoken,
duration,
objectId: item.property.objectid,
rt: item.property.rt || '0.9',
otherInfo: item.otherInfo,
doublespeed: item.property.doublespeed,
jobid: item.jobid,
name: item.property.name
});
this.addTaskToView('[视频]' + item.property.name);
} catch (error) {
Logger.addLog('获取视频信息失败: ' + error.message, 'red');
} finally {
TaskManager.decreaseBusyThread();
}
},
// 处理视频任务
async process(item) {
const rate = GM_getValue('unrivalrate', '1');
if (parseFloat(rate) <= 0) {
Logger.addLog('奇怪的倍速,视频已自动跳过', 'orange');
await Utils.delay(5000);
return;
}
// 检查是否支持后台挂机
const allowBackground = Math.round(new Date() / 1000) - parseInt(GM_getValue('unrivalBackgroundVideoEnable', '6')) < 15;
if (allowBackground && Config.get('backGround')) {
if (unsafeWindow.top.document.getElementsByClassName('catalog_points_sa').length > 0 ||
unsafeWindow.top.document.getElementsByClassName('lock').length > 0) {
Logger.addLog('您已安装超星挂机小助手,但此课程可能为闯关模式,不支持后台挂机,将为您在线完成', 'blue');
} else {
item.userid = Utils.getCookie('_uid') || Utils.getCookie('UID');
item.classId = Utils.getQueryVariable('clazzid') || Utils.getQueryVariable('clazzId') || Utils.getQueryVariable('classid') || Utils.getQueryVariable('classId');
item.review = unsafeWindow.top.unrivalReviewMode === '1';
item.reportUrl = GM_getValue('reportUrl', '');
item.rt = item.rt;
GM_setValue('unrivalBackgroundVideo', item);
document.cookie = "videojs_id=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
Logger.addLog('您已安装超星挂机小助手,已添加至后台任务,点我查看后台', 'green');
await Utils.delay(5000);
return;
}
}
// 在线处理视频
const videojs_id = String(parseInt(Math.random() * 9999999));
document.cookie = 'videojs_id=' + videojs_id + ';path=/';
Logger.addLog('开始刷视频:' + item.name + ',倍速:' + String(rate) + '倍');
Logger.addLog('视频观看信息每60秒上报一次,请耐心等待', 'green');
if (Config.get('disableMonitor')) {
Logger.addLog('解除多端学习监控有清除进度风险,请谨慎使用', 'orange');
}
let dtype = 'Video';
if (item.module.includes('audio')) {
dtype = 'Audio';
}
return new Promise((resolve) => {
let playTime = 0,
playsTime = 0,
isdrag = '3',
times = 0,
first = true,
loop;
loop = setInterval(async () => {
if (parseFloat(rate) <= 0) {
clearInterval(loop);
Logger.addLog('奇怪的倍速,视频已自动跳过', 'orange');
resolve();
return;
}
playsTime += parseFloat(rate);
playTime = Math.ceil(playsTime);
if (times == 0 || times % 30 == 0 || playTime >= item.duration) {
if (first) {
playTime = 0;
}
if (playTime >= item.duration) {
clearInterval(loop);
playTime = item.duration;
isdrag = '4';
} else if (playTime > 0) {
isdrag = '0';
}
try {
await this.reportVideoProgress(item, playTime, isdrag, dtype, rate, first);
if (playTime >= item.duration) {
if (unsafeWindow.top.unrivalReviewMode == '1') {
Logger.addLog('视频已观看完毕', 'green');
} else {
Logger.addLog('视频已观看完毕,但视频任务未完成', 'red');
}
resolve();
}
// 更新first变量
first = false;
} catch (error) {
Logger.addLog('上报视频进度失败: ' + error.message, 'red');
times = -10;
}
}
times++;
}, 1000);
});
},
// 上报视频进度
async reportVideoProgress(item, playTime, isdrag, dtype, rate, first) {
const classId = Utils.getQueryVariable('clazzid') || Utils.getQueryVariable('clazzId') || Utils.getQueryVariable('classid') || Utils.getQueryVariable('classId');
const UID = Utils.getCookie('_uid') || Utils.getCookie('UID');
const encUrl = `${Config.get('host')}chaoXing/v3/getEnc.php?classid=${classId}&playtime=${playTime}&duration=${item.duration}&objectid=${item.objectId}&jobid=${item.jobid}&uid=${UID}`;
let enc = '';
try {
const res = await Network.get(encUrl, {}, 2000);
if (res && res.status == 200) {
enc = res.responseText;
if (enc.includes('--#')) {
const warnInfo = enc.match(new RegExp('--#(.*?)--#', "ig"))[0].replace(/--#/ig, '');
Logger.addLog(warnInfo, 'red');
enc = enc.replace(/--#(.*?)--#/ig, '');
}
if (enc.indexOf('.stop') >= 0) {
TaskManager.stopTasks();
return;
}
}
} catch (error) {
// 使用本地计算作为备份
const jq = unsafeWindow.top.$ || unsafeWindow.top.jQuery;
const strEc = `[${classId}][${UID}][${item.jobid}][${item.objectId}][${playTime * 1000}][d_yHJ!$pdA~5][${item.duration * 1000}][0_${item.duration}]`;
enc = jq.md5(strEc);
}
if (enc.length != 32) {
TaskManager.stopTasks();
return;
}
const reportUrl = GM_getValue('reportUrl', '');
const reportsUrl = `${reportUrl}/${item.dtoken}?clazzId=${classId}&playingTime=${playTime}&duration=${item.duration}&clipTime=0_${item.duration}&objectId=${item.objectId}&otherInfo=${item.otherInfo}&jobid=${item.jobid}&userid=${UID}&isdrag=${isdrag}&view=pc&enc=${enc}&rt=${item.rt}&dtype=${dtype}&_t=${Utils.getTimestamp()}`;
const res = await Network.get(reportsUrl, {
'Host': location.host,
'Referer': GM_getValue('vrefer', `${location.protocol}//${location.host}/ananas/modules/video/index.html?v=2022-1118-1729`),
'Sec-Fetch-Site': 'same-origin',
'Content-Type': 'application/json'
});
// 更新学习时间
const today = new Date();
const todayStr = `${today.getFullYear()}d${today.getMonth()}d${today.getDate()}`;
const timelong = GM_getValue('unrivaltimelong', {});
if (!timelong[UID] || timelong[UID].today != todayStr) {
timelong[UID] = {
time: 0,
today: todayStr
};
} else {
timelong[UID].time++;
}
GM_setValue('unrivaltimelong', timelong);
// 检查学习时间
if (timelong[UID].time / 60 > 22 && item.doublespeed == 0 && unsafeWindow.top.unrivalReviewMode == '0') {
Logger.addLog('今日学习时间过长,继续学习会导致清空进度,请明天再来', 'red');
return;
}
const ispass = JSON.parse(res.responseText);
if (ispass.isPassed && unsafeWindow.top.unrivalReviewMode == '0') {
Logger.addLog('视频任务已完成', 'green');
return true;
} else if (isdrag == '4') {
return true;
} else {
Logger.addLog(`${item.name}已观看${playTime}秒,剩余大约${item.duration - playTime}秒`);
}
},
// 添加任务到视图
addTaskToView(name) {
const joblistElement = document.getElementById('joblist');
if (!joblistElement) return;
const taskDiv = document.createElement('div');
taskDiv.className = 'panel panel-default';
taskDiv.innerHTML = `
${name}
`;
joblistElement.appendChild(taskDiv);
}
};
// 文档处理模块
const DocumentHandler = {
// 处理文档任务
async process(item) {
Logger.addLog('开始刷文档:' + item.name);
await Utils.delay(Utils.random(9000, 11000));
TaskManager.increaseBusyThread();
try {
const classId = Utils.getQueryVariable('clazzid') || Utils.getQueryVariable('clazzId') || Utils.getQueryVariable('classid') || Utils.getQueryVariable('classId');
const courseId = Utils.getQueryVariable('courseid') || Utils.getQueryVariable('courseId');
const chapterId = Utils.getQueryVariable('knowledgeid');
const url = `${location.protocol}//${location.host}/ananas/job/document?jobid=${item.jobid}&knowledgeid=${chapterId}&courseid=${courseId}&clazzid=${classId}&jtoken=${item.jtoken}`;
const res = await Network.get(url);
const ispass = JSON.parse(res.responseText);
if (ispass.status) {
Logger.addLog('文档任务已完成', 'green');
} else {
Logger.addLog('文档已阅读完成,但任务点未完成', 'red');
}
} catch (error) {
Logger.addLog('阅读文档失败: ' + error.message, 'red');
} finally {
TaskManager.decreaseBusyThread();
}
}
};
// 答题处理模块
const WorkHandler = {
// 处理答题任务
process(item) {
return new Promise((resolve) => {
Logger.addLog('开始刷章节测试:' + item.name);
Logger.addLog('您设置的答题正确率为:' + String(Config.get('accuracy')) + '%,只有在高于此正确率时才会提交测试', 'blue');
// 显示答题面板
const workPanel = document.getElementById('workPanel');
const frameContent = document.getElementById('frame_content');
if (workPanel && frameContent) {
workPanel.style.display = 'block';
const classId = Utils.getQueryVariable('clazzid') || Utils.getQueryVariable('clazzId') || Utils.getQueryVariable('classid') || Utils.getQueryVariable('classId');
const courseId = Utils.getQueryVariable('courseid') || Utils.getQueryVariable('courseId');
const chapterId = Utils.getQueryVariable('knowledgeid');
frameContent.src = `${location.protocol}//${location.host}/work/phone/work?workId=${item.jobid.replace('work-', '')}&courseId=${courseId}&clazzId=${classId}&knowledgeId=${chapterId}&jobId=${item.jobid}&enc=${item.enc}`;
}
// 监听答题完成
unsafeWindow.top.unrivalWorkInfo = '';
unsafeWindow.top.unrivalDoneWorkId = '';
const infoInterval = setInterval(() => {
if (unsafeWindow.top.unrivalWorkInfo != '') {
Logger.addLog(unsafeWindow.top.unrivalWorkInfo);
unsafeWindow.top.unrivalWorkInfo = '';
}
}, 100);
// 检查跨域
const checkcross = setInterval(() => {
if (unsafeWindow.top.unrivalWorkDone == false) {
clearInterval(checkcross);
return;
}
try {
const ifW = frameContent.contentWindow;
ifW.location.href;
} catch (e) {
if (e.message.indexOf('cross-origin') != -1) {
clearInterval(checkcross);
unsafeWindow.top.unrivalWorkDone = true;
}
}
}, 2000);
// 检查答题完成
const workDoneInterval = setInterval(() => {
if (unsafeWindow.top.unrivalWorkDone) {
unsafeWindow.top.unrivalWorkDone = false;
clearInterval(workDoneInterval);
clearInterval(infoInterval);
unsafeWindow.top.unrivalDoneWorkId = '';
if (workPanel && frameContent) {
workPanel.style.display = 'none';
frameContent.src = '';
}
setTimeout(() => resolve(), 5000);
}
}, 500);
});
}
};
// 页面处理模块
const PageHandler = {
// 初始化知识卡片页面
initKnowledgeCardsPage() {
let allowBackground = false;
const spans = document.getElementsByTagName('span');
for (let i = 0, l = spans.length; i < l; i++) {
if (spans[i].innerHTML.indexOf('章节未开放') != -1) {
if (location.href.indexOf("ut=s") != -1) {
location.href = location.href.replace("ut=s", "ut=t").replace(/&cpi=[0-9]{1,10}/, '');
} else if (location.href.indexOf("ut=t") != -1) {
spans[i].innerHTML = '此课程为闯关模式,请回到上一章节完成学习任务!';
return;
}
break;
}
}
unsafeWindow.top.unrivalPageRd = String(Math.random());
// 检查浏览器版本
let cVersion = 999;
if (!navigator.userAgent.includes("Firefox")) {
try {
cVersion = parseInt(navigator.userAgent.match(/Chrome\/[0-9]{2,3}./)[0].replace('Chrome/', '').replace('.', ''));
} catch (e) {}
}
if (cVersion < 86) {
Logger.addLog('您的浏览器内核过老,请更新版本或使用主流浏览器,推荐 edge浏览器', 'red');
TaskManager.stopTasks();
return;
}
if (navigator.userAgent.includes("Android")) {
Logger.addLog('手机浏览器不保证能正常运行此脚本', 'orange');
}
// 监听页面可见性变化
document.addEventListener('visibilitychange', function () {
const isH = document.hidden;
if (!isH) {
Logger.addLog('挂机功能不稳定,不建议长时间最小化窗口', 'orange');
}
});
// 初始化UI
this.initUI();
// 解析页面数据
this.parsePageData();
},
// 初始化学生学习页面
initStudentStudyPage() {
// 初始化音频播放器
const audiofile = 'data:audio/ogg;base64,T2dnUwACAAAAAAAAAABwRPFFAAAAAGFtEqwBHgF2b3JiaXMAAAAAAUAfAAAAAAAAUHgAAAAAAACZAU9nZ1MAAAAAAAAAAAAAcETxRQEAAAA7J4IBDP8F////////////tQN2b3JiaXMvAAAAWGlwaC5PcmcgbGliVm9yYmlzIEkgMjAxNDAxMjIgKFR1cnBha8OkcsOkamlpbikGAAAAJQAAAEVOQ09ERVI9U291bmQgU3R1ZGlvLCBsaWJWb3JiaXMgMS4zLjEbAAAAQUxCVU0gQVJUSVNUPUFkdmVudHVyZSBMYW5kFAAAAEFMQlVNPUFkdmVudHVyZSBMYW5kIQAAAEVOQ09ESU5HIEFQUExJQ0FUSU9OPVNvdW5kIFN0dWRpbxUAAABBUlRJU1Q9QWR2ZW50dXJlIExhbmQjAAAAVElUTEU9RW1wdHkgTG9vcCBGb3IgSlMgUGVyZm9ybWFuY2UBBXZvcmJpcxJCQ1YBAAABAAxSFCElGVNKYwiVUlIpBR1jUFtHHWPUOUYhZBBTiEkZpXtPKpVYSsgRUlgpRR1TTFNJlVKWKUUdYxRTSCFT1jFloXMUS4ZJCSVsTa50FkvomWOWMUYdY85aSp1j1jFFHWNSUkmhcxg6ZiVkFDpGxehifDA6laJCKL7H3lLpLYWKW4q91xpT6y2EGEtpwQhhc+211dxKasUYY4wxxsXiUyiC0JBVAAABAABABAFCQ1YBAAoAAMJQDEVRgNCQVQBABgCAABRFcRTHcRxHkiTLAkJDVgEAQAAAAgAAKI7hKJIjSZJkWZZlWZameZaouaov+64u667t6roOhIasBADIAAAYhiGH3knMkFOQSSYpVcw5CKH1DjnlFGTSUsaYYoxRzpBTDDEFMYbQKYUQ1E45pQwiCENInWTOIEs96OBi5zgQGrIiAIgCAACMQYwhxpBzDEoGIXKOScggRM45KZ2UTEoorbSWSQktldYi55yUTkompbQWUsuklNZCKwUAAAQ4AAAEWAiFhqwIAKIAABCDkFJIKcSUYk4xh5RSjinHkFLMOcWYcowx6CBUzDHIHIRIKcUYc0455iBkDCrmHIQMMgEAAAEOAAABFkKhISsCgDgBAIMkaZqlaaJoaZooeqaoqqIoqqrleabpmaaqeqKpqqaquq6pqq5seZ5peqaoqp4pqqqpqq5rqqrriqpqy6ar2rbpqrbsyrJuu7Ks256qyrapurJuqq5tu7Js664s27rkearqmabreqbpuqrr2rLqurLtmabriqor26bryrLryratyrKua6bpuqKr2q6purLtyq5tu7Ks+6br6rbqyrquyrLu27au+7KtC7vourauyq6uq7Ks67It67Zs20LJ81TVM03X9UzTdVXXtW3VdW1bM03XNV1XlkXVdWXVlXVddWVb90zTdU1XlWXTVWVZlWXddmVXl0XXtW1Vln1ddWVfl23d92VZ133TdXVblWXbV2VZ92Vd94VZt33dU1VbN11X103X1X1b131htm3fF11X11XZ1oVVlnXf1n1lmHWdMLqurqu27OuqLOu+ruvGMOu6MKy6bfyurQvDq+vGseu+rty+j2rbvvDqtjG8um4cu7Abv+37xrGpqm2brqvrpivrumzrvm/runGMrqvrqiz7uurKvm/ruvDrvi8Mo+vquirLurDasq/Lui4Mu64bw2rbwu7aunDMsi4Mt+8rx68LQ9W2heHVdaOr28ZvC8PSN3a+AACAAQcAgAATykChISsCgDgBAAYhCBVjECrGIIQQUgohpFQxBiFjDkrGHJQQSkkhlNIqxiBkjknIHJMQSmiplNBKKKWlUEpLoZTWUmotptRaDKG0FEpprZTSWmopttRSbBVjEDLnpGSOSSiltFZKaSlzTErGoKQOQiqlpNJKSa1lzknJoKPSOUippNJSSam1UEproZTWSkqxpdJKba3FGkppLaTSWkmptdRSb';
const audioPlayer = new Audio(audiofile);
unsafeWindow.top.backNow = 0;
audioPlayer.loop = true;
unsafeWindow.audioPlayer = audioPlayer;
// 防止viewer弹窗
setInterval(function () {
try {
unsafeWindow.jQuery.fn.viewer.Constructor.prototype.show = () => { };
} catch (e) {}
}, 1000);
// 初始化脚本列表
try {
unsafeWindow.unrivalScriptList.push('Fuck me please');
} catch (e) {
unsafeWindow.unrivalScriptList = ['Fuck me please'];
}
// 检查离线状态
setInterval(function () {
if (PageHandler.checkOffline()) {
setTimeout(function () {
if (PageHandler.checkOffline()) {
location.reload();
}
}, 10000);
}
}, 3000);
// 监听页面可见性变化
document.addEventListener('visibilitychange', function () {
let c = 0;
if (unsafeWindow.top.backNow == 0) {
document.title = '⚠️请先激活挂机';
return;
} else {
document.title = '学生学习页面';
}
if (document.hidden) {
audioPlayer.play();
const timer = setInterval(function () {
if (c) {
document.title = '🙈挂机中';
c = 0;
} else {
document.title = '🙉挂机中';
c = 1;
}
if (!document.hidden) {
clearInterval(timer);
document.title = '学生学习页面';
}
}, 1300);
} else {
audioPlayer.pause();
}
});
// 重写getTeacherAjax函数
unsafeWindow.unrivalgetTeacherAjax = unsafeWindow.getTeacherAjax;
unsafeWindow.getTeacherAjax = (courseid, classid, cid) => {
if (cid == Utils.getQueryVariable('chapterId')) {
return;
}
unsafeWindow.top.unrivalPageRd = '';
unsafeWindow.unrivalgetTeacherAjax(courseid, classid, cid);
};
// 解除多端学习监控
if (Config.get('disableMonitor') == 1) {
unsafeWindow.appendChild = unsafeWindow.Element.prototype.appendChild;
unsafeWindow.Element.prototype.appendChild = function () {
try {
if (arguments[0].src && arguments[0].src.indexOf('detect.chaoxing.com') > 0) {
return;
}
} catch (e) {}
unsafeWindow.appendChild.apply(this, arguments);
};
}
// 初始化跳转功能
this.initJumpFunction();
},
// 初始化答题页面
initDoHomeWorkPage() {
const wIdE = document.getElementById('workLibraryId') || document.getElementById('oldWorkId');
if (!wIdE) return;
const wid = wIdE.value;
unsafeWindow.top.unrivalWorkDone = false;
// 重写alert函数
unsafeWindow.aalert = unsafeWindow.alert;
unsafeWindow.alert = (msg) => {
if (msg == '保存成功') {
unsafeWindow.top.unrivalDoneWorkId = Utils.getQueryVariable('workId');
return;
}
unsafeWindow.aalert(msg);
};
if (unsafeWindow.top.unrivalDoneWorkId == Utils.getQueryVariable('workId')) {
unsafeWindow.top.unrivalWorkDone = true;
return;
}
// 重写confirm函数
unsafeWindow.confirm = (msg) => true;
// 解析题目
const questionList = this.parseQuestions();
if (questionList.length === 0) {
Logger.addLog('未找到题目', 'red');
return;
}
// 处理题目
this.processQuestions(questionList, wid);
},
// 初始化后台挂机页面
initBackgroundPage() {
document.getElementsByTagName("html")[0].innerHTML = `
学习通挂机小助手
`;
Logger.addLog('此页面不必保持在最前端,后台会自动进行任务', 'green');
// 定时提示
setInterval(function () {
Logger.addLog('此页面不必保持在最前端,后台会自动进行任务', 'green');
Logger.addLog('如想禁用后台刷视频功能,请关闭脚本并重启浏览器', 'blue');
}, 120000);
// 监听后台任务信息
GM_addValueChangeListener('unrivalxxtbackgroundinfo', function (name, old_value, new_value, remote) {
if (old_value != new_value) {
Logger.addLog(new_value);
}
});
// 检查后台运行状态
setInterval(function () {
if (Math.round(new Date() / 1000) - parseInt(GM_getValue('unrivalBackgroundVideoEnable', '6')) > 15) {
Logger.addLog('超星挂机小助手可能运行异常,如页面无反应,请尝试重启脚本猫或重启浏览器(脚本猫0.9.0版本有此问题)');
}
}, 10000);
// 显示任务列表
this.showBackgroundTasks();
setInterval(() => this.showBackgroundTasks(), 10000);
},
// 检查离线状态
checkOffline() {
const dleft = document.getElementsByClassName('left');
if (dleft.length == 1) {
const img = dleft[0].getElementsByTagName('img');
if (img.length == 1) {
if (img[0].src.indexOf('loading.gif') != -1) {
return true;
}
}
}
return false;
},
// 初始化UI
initUI() {
// 创建主界面
document.getElementsByTagName("html")[0].innerHTML = `
学习通小助手
任务配置
开启倍数是会清进度的,不建议开启,除非是真的没时间了才开启倍数
视频倍速:
章节测试:
自动答题 |
自动提交 |
自动保存
题库Token:
保存
关注
作者是 大四学生 社畜,相信大家都是被大学网课而耽误的强国少年,我的专业是化学,但感觉学习计算机更符合自己的兴趣,也能更好地用科技便利生活,考研计算机了,因为脚本作者不更新,索性copycopy修改自己用,随后一想干脆发布出去,没想到发展到现在都有人用
当时放个赞赏码在这纯属侥幸,也没想到现在会从哥哥姐姐们手里收到零零碎碎的小费
感谢各位一直以来的支持,因为在准备毕设实在是太忙啦!
这个版本修复一些细节问题
留言于:2026.03.20
祝大家学业有成!
运行日志
[00:00:00]如果此提示不消失,说明页面出现了错误,请联系作者
`;
// 初始化UI事件
const htmlHook = setInterval(function () {
const unrivalRate = document.getElementById('unrivalRate');
const updateRateButton = document.getElementById('updateRateButton');
const reviewModeButton = document.getElementById('reviewModeButton');
const autoDoWorkButton = document.getElementById('autoDoWorkButton');
const autoSubmitButton = document.getElementById('autoSubmitButton');
const autoSaveButton = document.getElementById('autoSaveButton');
if (unrivalRate && updateRateButton && reviewModeButton && autoDoWorkButton && autoSubmitButton && autoSaveButton) {
clearInterval(htmlHook);
// 初始化倍速值
const rate = GM_getValue('unrivalrate', '1');
unrivalRate.value = rate;
// 隐藏后台挂机按钮(如果未启用)
const fuckMeModeButton = document.getElementById('fuckMeModeButton');
if (!Config.get('backGround')) {
fuckMeModeButton.style.display = "none";
}
// 检查是否允许后台挂机
const allowBackground = Math.round(new Date() / 1000) - parseInt(GM_getValue('unrivalBackgroundVideoEnable', '6')) < 15;
if (allowBackground) {
fuckMeModeButton.setAttribute('href', 'unrivalxxtbackground/');
}
// 绑定事件
document.getElementById('updateToken').onclick = function () {
const token = document.getElementById('token').value;
if (token.length == 16) {
Logger.addLog('一之哥哥题库token已更新为' + token, 'green');
} else {
Logger.addLog('请检查一之哥哥题库token', 'green');
}
Config.set('token', token);
};
document.getElementById('updateRateButton').onclick = function () {
let urate = document.getElementById('unrivalRate').value;
if (parseFloat(urate) == parseInt(urate)) {
urate = parseInt(urate);
} else {
urate = parseFloat(urate);
}
GM_setValue('unrivalrate', urate);
if (urate > 0) {
Logger.addLog('视频倍速已更新为' + urate + '倍,将在3秒内生效', 'green');
} else {
Logger.addLog('奇怪的倍速,将会自动跳过视频任务', 'orange');
}
};
document.getElementById('backGround').onclick = function () {
Logger.addLog('挂机激活成功,您现在可以最小化页面了', 'green');
document.getElementById('backGround').setAttribute('class', 'btn btn-success');
unsafeWindow.top.backNow = 1;
};
document.getElementById('reviewModeButton').onclick = function () {
const reviewButton = document.getElementById('reviewModeButton');
if (reviewButton.getAttribute('class') == 'btn btn-default') {
reviewButton.setAttribute('class', 'btn btn-success');
Logger.addLog('复习模式已开启,遇到已完成的视频任务不会跳过', 'green');
GM_setValue('unrivalreview', '1');
unsafeWindow.top.unrivalReviewMode = '1';
} else {
reviewButton.setAttribute('class', 'btn btn-default');
Logger.addLog('复习模式已关闭,遇到已完成的视频任务会自动跳过', 'green');
GM_setValue('unrivalreview', '0');
unsafeWindow.top.unrivalReviewMode = '0';
}
};
document.getElementById('autoDoWorkButton').onclick = function () {
const autoDoWorkButton = document.getElementById('autoDoWorkButton');
if (autoDoWorkButton.getAttribute('class') == 'btn btn-default') {
autoDoWorkButton.setAttribute('class', 'btn btn-success');
Logger.addLog('自动做章节测试已开启,将会自动做章节测试', 'green');
GM_setValue('unrivaldowork', '1');
unsafeWindow.top.unrivalDoWork = '1';
} else {
autoDoWorkButton.setAttribute('class', 'btn btn-default');
Logger.addLog('自动做章节测试已关闭,将不会自动做章节测试', 'green');
GM_setValue('unrivaldowork', '0');
unsafeWindow.top.unrivalDoWork = '0';
}
};
document.getElementById('autoSubmitButton').onclick = function () {
const autoSubmitButton = document.getElementById('autoSubmitButton');
if (autoSubmitButton.getAttribute('class') == 'btn btn-default') {
autoSubmitButton.setAttribute('class', 'btn btn-success');
Logger.addLog('符合提交标准的章节测试将会自动提交', 'green');
GM_setValue('unrivalautosubmit', '1');
unsafeWindow.top.unrivalAutoSubmit = '1';
} else {
autoSubmitButton.setAttribute('class', 'btn btn-default');
Logger.addLog('章节测试将不会自动提交', 'green');
GM_setValue('unrivalautosubmit', '0');
unsafeWindow.top.unrivalAutoSubmit = '0';
}
};
document.getElementById('autoSaveButton').onclick = function () {
const autoSaveButton = document.getElementById('autoSaveButton');
if (autoSaveButton.getAttribute('class') == 'btn btn-default') {
autoSaveButton.setAttribute('class', 'btn btn-success');
Logger.addLog('不符合提交标准的章节测试将会自动保存', 'green');
GM_setValue('unrivalautosave', '1');
unsafeWindow.top.unrivalAutoSave = '1';
} else {
autoSaveButton.setAttribute('class', 'btn btn-default');
Logger.addLog('不符合提交标准的章节测试将不会自动保存,等待用户自己操作', 'green');
GM_setValue('unrivalautosave', '0');
unsafeWindow.top.unrivalAutoSave = '0';
}
};
// 初始化模式状态
try {
unsafeWindow.top.unrivalReviewMode = GM_getValue('unrivalreview', '0') || '0';
unsafeWindow.top.unrivalDoWork = GM_getValue('unrivaldowork', '1') || '1';
unsafeWindow.top.unrivalAutoSubmit = GM_getValue('unrivalautosubmit', '1') || '1';
unsafeWindow.top.unrivalAutoSave = GM_getValue('unrivalautosave', '0') || '0';
if (unsafeWindow.top.unrivalReviewMode == '1') {
Logger.addLog('复习模式已开启,遇到已完成的视频任务不会跳过', 'green');
document.getElementById('reviewModeButton').setAttribute('class', 'btn btn-success');
}
if (unsafeWindow.top.unrivalDoWork == '1') {
Logger.addLog('自动做章节测试已开启,将会自动做章节测试', 'green');
document.getElementById('autoDoWorkButton').setAttribute('class', 'btn btn-success');
}
document.getElementById('autoSubmitButton').setAttribute('class', ['btn btn-default', 'btn btn-success'][unsafeWindow.top.unrivalAutoSubmit]);
document.getElementById('autoSaveButton').setAttribute('class', ['btn btn-default', 'btn btn-success'][unsafeWindow.top.unrivalAutoSave]);
} catch (e) {
Logger.addLog('初始化模式状态失败: ' + e.message, 'red');
}
}
}, 100);
},
// 解析页面数据
parsePageData() {
// 获取参数
const scripts = document.getElementsByTagName('script');
let param = null;
for (let i = 0, l = scripts.length; i < l; i++) {
if (scripts[i].innerHTML.indexOf('mArg = "";') != -1 && scripts[i].innerHTML.indexOf('==UserScript==') == -1) {
param = scripts[i].innerHTML.substring(scripts[i].innerHTML.indexOf('try{\n mArg = '), scripts[i].innerHTML.indexOf(';\n}catch(e){')).replace('try{\n mArg = ', '');
}
}
if (param == null) {
Logger.addLog('未找到页面参数', 'red');
return;
}
// 获取视频引用
let vrefer;
try {
vrefer = document.getElementsByClassName('ans-attach-online ans-insertvideo-online')[0].src;
} catch (e) {
vrefer = `${location.protocol}//${location.host}/ananas/modules/video/index.html?v=2022-1118-1729`;
}
GM_setValue('vrefer', vrefer);
GM_setValue('host', location.host);
// 解析页面数据
try {
const pageData = JSON.parse(param);
const data = pageData.defaults;
const jobList = [];
const classId = data.clazzId;
const chapterId = data.knowledgeid;
const reportUrl = data.reportUrl;
const ktoken = data.ktoken;
GM_setValue('reportUrl', reportUrl);
// 收集任务
for (let i = 0, l = pageData.attachments.length; i < l; i++) {
const item = pageData.attachments[i];
if (item.job != true || item.isPassed == true) {
if (unsafeWindow.top.unrivalReviewMode == '1' && item.type == 'video') {
jobList.push(item);
}
continue;
} else {
jobList.push(item);
}
}
// 处理任务
if (jobList.length <= 0) {
if (Config.get('jumpType') != 2) {
unsafeWindow.top.jump = true;
Logger.addLog('此页无任务,5秒后自动下一章', 'green');
} else {
Logger.addLog('此页无任务,用户设置为不跳转,脚本已结束运行', 'green');
}
return;
}
// 处理每个任务
for (let i = 0, l = jobList.length; i < l; i++) {
const item = jobList[i];
if (item.type == 'video') {
VideoHandler.prepareVideo(item);
} else if (item.type == 'document') {
TaskManager.addTask({
type: 'document',
jtoken: item.jtoken,
jobid: item.jobid,
name: item.property.name,
done: false,
running: false
});
const joblistElement = document.getElementById('joblist');
if (joblistElement) {
const taskDiv = document.createElement('div');
taskDiv.className = 'panel panel-default';
taskDiv.innerHTML = `
[文档]${item.property.name}
`;
joblistElement.appendChild(taskDiv);
}
} else if (item.type == 'workid' && unsafeWindow.top.unrivalDoWork == '1') {
TaskManager.addTask({
type: 'work',
workid: item.property.workid,
jobid: item.jobid,
name: item.property.title,
enc: item.enc,
done: false,
running: false
});
const joblistElement = document.getElementById('joblist');
if (joblistElement) {
const taskDiv = document.createElement('div');
taskDiv.className = 'panel panel-default';
taskDiv.innerHTML = `
[章节测试]${item.property.title}
`;
joblistElement.appendChild(taskDiv);
}
} else {
try {
let jobName = item.property.name;
if (jobName == undefined) {
jobName = item.property.title;
}
const joblistElement = document.getElementById('joblist');
if (joblistElement) {
const taskDiv = document.createElement('div');
taskDiv.className = 'panel panel-default';
taskDiv.innerHTML = `
已跳过:${jobName}
`;
joblistElement.appendChild(taskDiv);
}
} catch (e) {
Logger.addLog('处理任务时出错: ' + e.message, 'red');
}
}
}
// 开始执行任务
setTimeout(() => TaskManager.executeTasks(), 1000);
} catch (error) {
Logger.addLog('解析页面数据失败: ' + error.message, 'red');
if (Config.get('jumpType') != 2) {
unsafeWindow.top.jump = true;
Logger.addLog('此页无任务,5秒后自动下一章', 'green');
} else {
Logger.addLog('此页无任务,用户设置为不跳转,脚本已结束运行', 'green');
}
}
},
// 初始化跳转功能
initJumpFunction() {
unsafeWindow.jump = false;
setInterval(function () {
if (Utils.getQueryVariable('mooc2') == '1') {
const tabs = document.getElementsByClassName('posCatalog_select');
for (let i = 0, l = tabs.length; i < l; i++) {
const tabId = tabs[i].getAttribute('id');
if (tabId.indexOf('cur') >= 0 && tabs[i].getAttribute('class') == 'posCatalog_select') {
tabs[i].setAttribute('onclick', `getTeacherAjax('${Utils.getQueryVariable('courseid') || Utils.getQueryVariable('courseId')}','${Utils.getQueryVariable('clazzid') || Utils.getQueryVariable('clazzId') || Utils.getQueryVariable('classid') || Utils.getQueryVariable('classId')}','${tabId.replace('cur', '')}');`);
}
}
} else {
const h4s = document.getElementsByTagName('h4'),
h5s = document.getElementsByTagName('h5');
for (let i = 0, l = h4s.length; i < l; i++) {
if (h4s[i].getAttribute('id').indexOf('cur') >= 0) {
h4s[i].setAttribute('onclick', `getTeacherAjax('${Utils.getQueryVariable('courseid') || Utils.getQueryVariable('courseId')}','${Utils.getQueryVariable('clazzid') || Utils.getQueryVariable('clazzId') || Utils.getQueryVariable('classid') || Utils.getQueryVariable('classId')}','${h4s[i].getAttribute('id').replace('cur', '')}');`);
}
}
for (let i = 0, l = h5s.length; i < l; i++) {
if (h5s[i].getAttribute('id').indexOf('cur') >= 0) {
h5s[i].setAttribute('onclick', `getTeacherAjax('${Utils.getQueryVariable('courseid') || Utils.getQueryVariable('courseId')}','${Utils.getQueryVariable('clazzid') || Utils.getQueryVariable('clazzId') || Utils.getQueryVariable('classid') || Utils.getQueryVariable('classId')}','${h5s[i].getAttribute('id').replace('cur', '')}');`);
}
}
}
}, 1000);
setInterval(function () {
if (unsafeWindow.jump) {
unsafeWindow.jump = false;
unsafeWindow.top.unrivalDoneWorkId = '';
unsafeWindow.jjump = (rd) => {
if (rd != unsafeWindow.top.unrivalPageRd) {
return;
}
try {
setTimeout(function () {
if (rd != unsafeWindow.top.unrivalPageRd) {
return;
}
if (Config.get('jumpType') == 1) {
let but = null;
if (Utils.getQueryVariable('mooc2') == '1') {
but = document.getElementsByClassName('jb_btn jb_btn_92 fs14 prev_next next');
} else {
but = document.getElementsByClassName('orientationright');
}
try {
setTimeout(function () {
if (rd != unsafeWindow.top.unrivalPageRd) {
return;
}
if (but.length > 0) {
but[0].click();
}
}, 2000);
} catch (e) {
Logger.addLog('跳转失败: ' + e.message, 'red');
}
return;
}
if (Utils.getQueryVariable('mooc2') == '1') {
const ul = document.getElementsByClassName('prev_ul')[0];
if (ul) {
const lis = ul.getElementsByTagName('li');
for (let i = 0, l = lis.length; i < l; i++) {
if (lis[i].getAttribute('class') == 'active') {
if (i + 1 < l) {
try {
lis[i + 1].click();
} catch (e) {
Logger.addLog('跳转失败: ' + e.message, 'red');
}
return;
}
break;
}
}
}
const tabs = document.getElementsByClassName('posCatalog_select');
for (let i = 0, l = tabs.length; i < l; i++) {
if (tabs[i].getAttribute('class') == 'posCatalog_select posCatalog_active') {
let j = i;
while (j + 1 < tabs.length) {
const nextTab = tabs[j + 1];
if ((nextTab.innerHTML.includes('icon_Completed prevTips') && unsafeWindow.top.unrivalReviewMode == '0') || nextTab.innerHTML.includes('catalog_points_er prevTips')) {
j++;
continue;
}
if (nextTab.id.indexOf('cur') < 0) {
j++;
continue;
}
const clickF = setInterval(function () {
if (rd != unsafeWindow.top.unrivalPageRd) {
clearInterval(clickF);
return;
}
nextTab.click();
}, 2000);
break;
}
break;
}
}
} else {
const div = document.getElementsByClassName('tabtags')[0];
if (div) {
const spans = div.getElementsByTagName('span');
for (let i = 0, l = spans.length; i < l; i++) {
if (spans[i].getAttribute('class').indexOf('currents') >= 0) {
if (i + 1 < l) {
try {
spans[i + 1].click();
} catch (e) {
Logger.addLog('跳转失败: ' + e.message, 'red');
}
return;
}
break;
}
}
}
const tabs = document.getElementsByTagName('span');
const newTabs = [];
for (let i = 0, l = tabs.length; i < l; i++) {
if (tabs[i].getAttribute('style') != null && tabs[i].getAttribute('style').indexOf('cursor:pointer;height:18px;') >= 0) {
newTabs.push(tabs[i]);
}
}
for (let i = 0, l = newTabs.length; i < l; i++) {
if (newTabs[i].parentNode.getAttribute('class') == 'currents') {
let j = i;
while (j + 1 < newTabs.length) {
const nextTab = newTabs[j + 1].parentNode;
if ((nextTab.innerHTML.includes('roundpoint blue') && unsafeWindow.top.unrivalReviewMode == '0') || nextTab.innerHTML.includes('roundpointStudent lock')) {
j++;
continue;
}
if (nextTab.id.indexOf('cur') < 0) {
j++;
continue;
}
const clickF = setInterval(function () {
if (rd != unsafeWindow.top.unrivalPageRd) {
clearInterval(clickF);
return;
}
nextTab.click();
}, 2000);
break;
}
break;
}
}
}
}, 2000);
} catch (e) {
Logger.addLog('跳转失败: ' + e.message, 'red');
}
};
if (unsafeWindow.onReadComplete1) {
unsafeWindow.onReadComplete1();
}
setTimeout(`jjump("${unsafeWindow.top.unrivalPageRd}")`, 2856);
}
}, 200);
},
// 解析题目
parseQuestions() {
const questionList = [];
const questionsElement = document.getElementsByClassName('Py-mian1');
const questionNum = questionsElement.length;
for (let i = 0; i < questionNum; i++) {
const questionElement = questionsElement[i];
const idElements = questionElement.getElementsByTagName('input');
let questionId = '0';
let question = '';
try {
question = questionElement.getElementsByClassName('Py-m1-title fs16')[0].innerHTML;
question = Utils.handleImgs(question)
.replace(/(<([^>]+)>)/ig, '')
.replace(/[0-9]{1,3}.\[(.*?)\]/ig, '')
.replaceAll('\n', '')
.replace(/^\s+/ig, '')
.replace(/\s+$/ig, '');
} catch (e) {
continue;
}
for (let z = 0, k = idElements.length; z < k; z++) {
try {
if (idElements[z].getAttribute('name') && idElements[z].getAttribute('name').indexOf('answer') >= 0) {
questionId = idElements[z].getAttribute('name').replace('type', '');
break;
}
} catch (e) {
continue;
}
}
if (questionId == '0' || question == '') {
continue;
}
const typeE = questionElement.getElementsByTagName('input');
if (!typeE || typeE.length === 0) {
continue;
}
let typeN = 'fuckme';
for (let g = 0, h = typeE.length; g < h; g++) {
try {
if (typeE[g].id == 'answertype' + questionId.replace('answer', '').replace('check', '')) {
typeN = typeE[g].value;
break;
}
} catch (e) {
continue;
}
}
if (['0', '1', '3'].indexOf(typeN) < 0) {
continue;
}
const type = {
'0': '单选题',
'1': '多选题',
'3': '判断题'
}[typeN];
const optionList = {
length: 0
};
if (['单选题', '多选题'].indexOf(type) >= 0) {
try {
const answersElements = questionElement.getElementsByClassName('answerList')[0].getElementsByTagName('li');
for (let x = 0, j = answersElements.length; x < j; x++) {
const optionE = answersElements[x];
const optionTextE = Utils.trim(optionE.innerHTML.replace(/(^\s*)|(\s*$)/g, ""));
const optionText = optionTextE.slice(1).replace(/(^\s*)|(\s*$)/g, "");
const optionValue = optionTextE.slice(0, 1);
const optionId = optionE.getAttribute('id-param');
if (optionText == '') {
break;
}
optionList[optionText] = {
'id': optionId,
'value': optionValue
};
optionList.length++;
}
if (answersElements.length != optionList.length) {
continue;
}
} catch (e) {
continue;
}
}
questionList.push({
'question': question,
'type': type,
'questionid': questionId,
'options': optionList
});
}
return questionList;
},
// 处理题目
processQuestions(questionList, wid) {
let nowTime = -4000;
let busyThread = questionList.length;
const ctOnload = function (res, quu) {
busyThread -= 1;
let ctResult = {
'code': -1,
'finalUrl': '',
'data': '未找到答案'
};
if (res) {
try {
const responseText = res.responseText;
ctResult = JSON.parse(responseText);
} catch (e) {
if (res.finalUrl && res.finalUrl.includes('getAnswer.php')) {
unsafeWindow.top.unrivalWorkInfo = '查题错误,服务器连接失败';
return;
}
}
}
try {
const choiceEs = document.getElementsByTagName('li');
if (ctResult['code'] == -1) {
try {
if (ctResult['msg'] !== undefined) {
unsafeWindow.top.unrivalWorkInfo = '题目:' + quu['question'] + ':未搜索到答案';
}
} catch (e) {}
// 自动选择
if (Config.get('randomDo') == 1 && Config.get('accuracy') < 100) {
switch (quu['type']) {
case '判断题':
unsafeWindow.top.unrivalWorkInfo = quu['question'] + ':未找到正确答案,自动选【错】';
for (let u = 0, k = choiceEs.length; u < k; u++) {
try {
if (choiceEs[u].getElementsByTagName('em').length < 1) {
continue;
}
if (choiceEs[u].getAttribute('val-param') == 'false' && choiceEs[u].getAttribute('id-param') == quu['questionid'].replace('answer', '')) {
choiceEs[u].click();
return;
}
} catch (e) {
continue;
}
}
break;
case '单选题':
unsafeWindow.top.unrivalWorkInfo = quu['question'] + ':未找到正确答案,自动选【B】';
for (let y = 0, j = choiceEs.length; y < j; y++) {
try {
if (choiceEs[y].getElementsByTagName('em').length < 1) {
continue;
}
if (choiceEs[y].getElementsByTagName('em')[0].getAttribute('id-param') == 'B' && choiceEs[y].getAttribute('id-param') == quu['questionid'].replace('answer', '')) {
if (!choiceEs[y].getAttribute('class').includes('cur')) {
choiceEs[y].click();
}
return;
}
} catch (e) {
continue;
}
}
break;
case '多选题':
unsafeWindow.top.unrivalWorkInfo = quu['question'] + ':未找到正确答案,自动全选';
for (let y = 0, j = choiceEs.length; y < j; y++) {
try {
if (choiceEs[y].getElementsByTagName('em').length < 1) {
continue;
}
if (choiceEs[y].getAttribute('id-param') == quu['questionid'].replace('answer', '')) {
if (!choiceEs[y].getAttribute('class').includes('cur')) {
choiceEs[y].click();
}
}
} catch (e) {
continue;
}
}
break;
}
}
return;
} else if (ctResult['code'] == -2) {
unsafeWindow.top.unrivalWorkInfo = ctResult['msg'];
return;
}
try {
const result = ctResult['data'];
unsafeWindow.top.unrivalWorkInfo = '题目:' + quu['question'] + ':' + result;
switch (quu['type']) {
case '判断题':
(function () {
let answer = 'abaabaaba';
if ('正确是对√Tri'.indexOf(result) >= 0) {
answer = 'true';
} else if ('错误否错×Fwr'.indexOf(result) >= 0) {
answer = 'false';
}
for (let u = 0, k = choiceEs.length; u < k; u++) {
try {
if (choiceEs[u].getAttribute('val-param') == answer && choiceEs[u].getAttribute('id-param') == quu['questionid'].replace('answer', '')) {
choiceEs[u].click();
return;
}
} catch (e) {
continue;
}
}
})();
break;
case '单选题':
(function () {
const answerData = result;
for (const option in quu['options']) {
try {
if (Utils.trim(option).replace(/\s/ig, '') == Utils.trim(answerData).replace(/\s/ig, '') ||
Utils.trim(option).replace(/\s/ig, '').includes(Utils.trim(answerData).replace(/\s/ig, '')) ||
Utils.trim(answerData).replace(/\s/ig, '').includes(Utils.trim(option).replace(/\s/ig, ''))) {
for (let y = 0, j = choiceEs.length; y < j; y++) {
try {
if (choiceEs[y].getElementsByTagName('em').length < 1) {
continue;
}
if (choiceEs[y].getElementsByTagName('em')[0].getAttribute('id-param') == quu['options'][option]['value'] &&
choiceEs[y].getAttribute('id-param') == quu['questionid'].replace('answer', '')) {
if (!choiceEs[y].getAttribute('class').includes('cur')) {
choiceEs[y].click();
}
return;
}
} catch (e) {
continue;
}
}
}
} catch (e) {
continue;
}
}
})();
break;
case '多选题':
(function () {
const answerData = Utils.trim(result).replace(/\s/ig, '');
let hasAnswer = false;
for (const option in quu['options']) {
try {
if (answerData.includes(Utils.trim(option).replace(/\s/ig, ''))) {
for (let y = 0, j = choiceEs.length; y < j; y++) {
try {
if (choiceEs[y].getElementsByTagName('em').length < 1) {
continue;
}
if (choiceEs[y].getElementsByTagName('em')[0].getAttribute('id-param') == quu['options'][option]['value'] &&
choiceEs[y].getAttribute('id-param') == quu['questionid'].replace('answer', '')) {
if (!choiceEs[y].getAttribute('class').includes('cur')) {
choiceEs[y].click();
}
hasAnswer = true;
break;
}
} catch (e) {
continue;
}
}
}
} catch (e) {
continue;
}
}
})();
break;
}
} catch (e) {
unsafeWindow.top.unrivalWorkInfo = '答案解析失败';
}
} catch (e) {
Logger.addLog('处理题目时出错: ' + e.message, 'red');
}
};
for (let i = 0, l = questionList.length; i < l; i++) {
nowTime += Utils.random(3500, 6500);
setTimeout(function () {
const qu = questionList[i];
let param = 'question=' + encodeURIComponent(qu['question']);
if (Config.get('ctUrl').includes('icodef')) {
param += '&type=' + {
'单选题': '0',
'多选题': '1',
'判断题': '3'
}[qu['type']] + '&id=' + wid;
}
Network.post(Config.get('ctUrl'), param, {
'Content-type': 'application/x-www-form-urlencoded',
'Authorization': Config.get('token'),
}, 2000)
.then(res => ctOnload(res, qu))
.catch(() => ctOnload(false, qu));
}, nowTime);
}
// 检查答题完成
const workInterval = setInterval(function () {
if (busyThread != 0) {
return;
}
clearInterval(workInterval);
const totalQuestionNum = questionList.length;
const answeredNum = totalQuestionNum - busyThread;
const accuracy = Math.floor((answeredNum / totalQuestionNum) * 100);
if (accuracy >= Config.get('accuracy') && unsafeWindow.top.unrivalAutoSubmit == '1') {
unsafeWindow.top.unrivalDoneWorkId = Utils.getQueryVariable('workId');
unsafeWindow.top.unrivalWorkInfo = '正确率符合标准,已提交答案';
setTimeout(function () {
try {
if (typeof submitCheckTimes === 'function') submitCheckTimes();
if (typeof escapeBlank === 'function') escapeBlank();
if (typeof submitAction === 'function') submitAction();
} catch (e) {
Logger.addLog('提交答案失败: ' + e.message, 'red');
}
}, Utils.random(3000, 5000));
} else if (unsafeWindow.top.unrivalAutoSave == 1) {
unsafeWindow.top.unrivalWorkInfo = '正确率不符合标准或未设置自动提交,已自动保存答案';
if (accuracy >= 0) {
setTimeout(function () {
unsafeWindow.top.unrivalDoneWorkId = Utils.getQueryVariable('workId');
if (typeof unsafeWindow.noSubmit === 'function') {
unsafeWindow.noSubmit();
}
}, 2000);
}
} else {
unsafeWindow.top.unrivalWorkInfo = '用户设置为不自动保存答案,请手动提交或保存作业';
}
}, 1000);
},
// 显示后台任务
showBackgroundTasks() {
const jobList = GM_getValue('unrivalBackgroundList', '1');
const joblistElement = document.getElementById('joblist');
if (!joblistElement) return;
if (jobList == '1') {
joblistElement.innerHTML = '请将“超星挂机小助手”升级到最新版并重启浏览器';
return;
}
try {
let jobHtml = '';
for (let i = 0, l = jobList.length; i < l; i++) {
let status = '';
if (jobList[i]['done']) {
status = '已完成';
} else if (parseInt(jobList[i]['playTime']) > 0) {
status = '进行中';
} else {
status = '等待中';
}
if (jobList[i]['review']) {
status += ':复习模式';
}
jobHtml += `
[${status}]${jobList[i]['name']}
`;
}
joblistElement.innerHTML = jobHtml;
} catch (e) {
joblistElement.innerHTML = '请将“超星挂机小助手”升级到最新版并重启浏览器!';
}
}
};
// 自动登录模块
const AutoLogin = {
// 执行自动登录
execute() {
if (!(/^1[3456789]\d{9}$/.test(Config.get('phoneNumber')))) {
alert('自动登录的手机号填写错误,无法登陆');
return;
}
if (Config.get('password') == '') {
alert('未填写登录密码,无法登陆');
return;
}
Network.get(`https://passport2-api.chaoxing.com/v11/loginregister?cx_xxt_passport=json&uname=${Config.get('phoneNumber')}&code=${encodeURIComponent(Config.get('password'))}`)
.then(res => {
try {
const ispass = JSON.parse(res.responseText);
if (ispass['status']) {
const refer = Utils.getQueryVariable('refer');
if (refer) {
location.href = decodeURIComponent(refer);
}
} else {
alert(ispass['mes']);
}
} catch (err) {
alert('登陆失败');
}
})
.catch(err => {
alert('登陆错误');
});
}
};
// 主入口
const Main = {
// 初始化
init() {
console.log('学习通小助手:初始化开始');
// 检查脚本冲突
try {
if (unsafeWindow.unrivalScriptList && unsafeWindow.unrivalScriptList.length > 1) {
console.log('学习通小助手:检测到多个刷课脚本');
if (typeof Logger !== 'undefined' && Logger.addLog) {
Logger.addLog('您同时开启了多个刷课脚本,会挂科,会挂科,会挂科,会挂科,会挂科,会挂科,会挂科,会挂科', 'red');
}
}
unsafeWindow.unrivalScriptList = ['Fuck me please'];
} catch (e) {
console.log('学习通小助手:脚本冲突检查错误', e);
unsafeWindow.unrivalScriptList = ['Fuck me please'];
}
// 根据页面类型初始化
const href = location.href;
console.log('学习通小助手:当前页面URL', href);
if (href.indexOf("knowledge/cards") > 0) {
console.log('学习通小助手:初始化知识卡片页面');
if (typeof PageHandler !== 'undefined' && PageHandler.initKnowledgeCardsPage) {
PageHandler.initKnowledgeCardsPage();
} else {
console.error('学习通小助手:PageHandler未定义');
}
} else if (href.includes("mycourse/studentstudy")) {
console.log('学习通小助手:初始化学生学习页面');
if (typeof PageHandler !== 'undefined' && PageHandler.initStudentStudyPage) {
PageHandler.initStudentStudyPage();
} else {
console.error('学习通小助手:PageHandler未定义');
}
} else if (href.indexOf("work/phone/doHomeWork") > 0) {
console.log('学习通小助手:初始化答题页面');
if (typeof PageHandler !== 'undefined' && PageHandler.initDoHomeWorkPage) {
PageHandler.initDoHomeWorkPage();
} else {
console.error('学习通小助手:PageHandler未定义');
}
} else if (href.includes('work/phone/selectWorkQuestionYiPiYue')) {
console.log('学习通小助手:初始化题目选择页面');
unsafeWindow.top.unrivalWorkDone = true;
if (typeof Utils !== 'undefined' && Utils.getQueryVariable) {
unsafeWindow.top.unrivalDoneWorkId = Utils.getQueryVariable('workId');
} else {
console.error('学习通小助手:Utils未定义');
}
} else if (href.includes('stat2-ans.chaoxing.com/task/s/index')) {
console.log('学习通小助手:初始化学习统计页面');
if (unsafeWindow.top == unsafeWindow) {
console.log('学习通小助手:跳过学习统计页面');
return;
}
const studentStatistic = document.getElementsByClassName('page-container studentStatistic')[0];
if (studentStatistic) {
studentStatistic.setAttribute('class', 'studentStatistic');
}
const itemTaskList = document.getElementsByClassName('page-item item-task-list minHeight390')[0];
if (itemTaskList) {
itemTaskList.remove();
}
const subNav = document.getElementsByClassName('subNav clearfix')[0];
if (subNav) {
subNav.remove();
}
setInterval(function () {
location.reload();
}, 90000);
} else if (href.includes('passport2.') && href.includes('login?refer=http') && Config.get('autoLogin') == 1) {
console.log('学习通小助手:初始化自动登录');
if (typeof AutoLogin !== 'undefined' && AutoLogin.execute) {
AutoLogin.execute();
} else {
console.error('学习通小助手:AutoLogin未定义');
}
} else if (href.includes('unrivalxxtbackground')) {
console.log('学习通小助手:初始化后台挂机页面');
if (typeof PageHandler !== 'undefined' && PageHandler.initBackgroundPage) {
PageHandler.initBackgroundPage();
} else {
console.error('学习通小助手:PageHandler未定义');
}
} else {
console.log('学习通小助手:当前页面不匹配任何处理逻辑');
// 尝试在页面上显示提示信息
try {
if (document.body) {
const infoDiv = document.createElement('div');
infoDiv.style.cssText = 'position: fixed; top: 0; left: 0; right: 0; background: blue; color: white; padding: 10px; z-index: 9999;';
infoDiv.textContent = '学习通小助手:当前页面不支持自动刷课,请进入课程学习页面';
document.body.insertBefore(infoDiv, document.body.firstChild);
}
} catch (e) {
console.error('学习通小助手:显示提示信息失败', e);
}
}
console.log('学习通小助手:初始化完成');
}
};
// 启动脚本
try {
console.log('学习通小助手:开始执行');
Main.init();
console.log('学习通小助手:执行完成');
} catch (error) {
console.error('学习通小助手:执行错误', error);
// 尝试在页面上显示错误信息
try {
if (document.body) {
const errorDiv = document.createElement('div');
errorDiv.style.cssText = 'position: fixed; top: 0; left: 0; right: 0; background: red; color: white; padding: 10px; z-index: 9999;';
errorDiv.textContent = '学习通小助手执行错误: ' + error.message;
document.body.insertBefore(errorDiv, document.body.firstChild);
}
} catch (e) {}
}
})();