// ==UserScript==
// @name 上海开大助手
// @namespace http://tampermonkey.net/
// @homepage https://shkd.script.woca.fun
// @description 上海开放大学学习平台,自动刷课,题目共享
// @author AchieveHF
// @version 1.0.13
// @match *://*.shou.org.cn/*
// @match *://live.eeo.cn/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=shou.org.cn
// @grant GM_setValue
// @grant GM_getValue
// @require https://code.jquery.com/jquery-3.6.0.min.js
// @require https://unpkg.com/layui@2.9.20/dist/layui.js
// @license MIT
// @run-at document-idle
// @downloadURL none
// ==/UserScript==
(function() {
'use strict';
const css = document.createElement('link');
css.rel = 'stylesheet';
css.href = '//shkd.script.woca.fun/static/layui/css/layui.css';
document.head.appendChild(css);
const apiDomain = 'https://shkd.script.woca.fun/';
// const apiDomain = 'http://localhost:8000/';
let link = parseURLDetails();
if (link.domain == 'live.eeo.cn') {
setTimeout(() => {
//第三方回放页面
$('#player_html5_api')[0].muted = true;
$('#player_html5_api')[0].play()
return;
}, 3000)
}
switch (link.path) {
case '/study/assignment/preview.aspx':
//插入工具
$.ajax({
url: apiDomain + '/api/getBody', //获取题目接口
type: 'GET',
data: {
path: link.path,
},
success: function(data) {
$('.e-q-t').append(data)
}
})
//监听工具使用
$('body').on('click', '.searchQuestion', function() {
let q = getQuestion($(this).parent().parent())
var index = layer.load(0, { shade: false });
$.ajax({
url: apiDomain + '/api/searchQuestion', //获取题目接口
type: 'POST',
data: {
question: q,
},
success: (res) => {
layer.close(index);
if (res.code == 200) {
$(this).parent().find('.result').html('参考答案:
' + res.data.answer + '
')
$(this).parent().parent().find('.e-a .ErichText').each((index, elem) => {
// 获取当前选项的文本,并去掉多余的换行符和空格
const optionText = $(elem).text().trim().replaceAll('\n', '');
// 获取答案,按 '\n' 分隔为数组
const answerList = res.data.answer.split('\n').map(ans => ans.trim());
// 检查当前选项是否在答案数组中
if (answerList.includes(optionText)) {
$(elem).css('background-color', '#F5C16B'); // 设置背景色
}
});
layer.msg(res.msg)
} else {
layer.msg(res.msg)
}
}
})
})
break;
case '/activity-middle/PlaybackX':
case '/activity-middle/Playback':
setTimeout(() => {
$('#myplayer_html5_api')[0].muted = true
$('#myplayer_html5_api')[0].play()
}, 5000)
break;
case '/study/activity-classInVideo.aspx':
setTimeout(() => {
window.open($('#classIn')[0].src, '_blank', 'width=800,height=600')
window.close();
}, 5000)
break;
case '/study/play.aspx':
setTimeout(() => {
$('#video')[0].muted = true
$('#video')[0].play()
}, 5000)
break;
case '/study/activity-preview.aspx':
if (link.params.auto !== undefined) {
window.location.href = $('.am-btn.am-btn-success.am-btn-default').attr('href')
}
break;
case '/study/directory.aspx':
//观看课程页面
const mustCourse = GM_getValue('mustCourse', {})
setTimeout(() => {
if (mustCourse[link.params.CourseOpenId] != undefined) {
$('.bd .sh-cw-bd').prepend(`
`)
} else {
//没有找到必看
console.log(mustCourse)
}
}, 2000)
setTimeout(() => {
if (GM_getValue('autoPlay')) {
autoPlayStart()
}
}, 5000)
//
//
//
必看课程
$('body').on('click', '#autoPlay', function () {
GM_setValue('autoPlay', GM_getValue('autoPlay') ? false : true)
if (GM_getValue('autoPlay')) {
$(this).removeClass('layui-bg-blue').addClass('layui-bg-red')
autoPlayStart()
} else {
$(this).removeClass('layui-bg-red').addClass('layui-bg-blue')
}
$(this).text('点击' + (GM_getValue('autoPlay') ? '关闭' : '开启') + '自动刷课')
})
break;
case '/scenter':
//学习空间首页
const liveCourse = {};
//获取课程列表
const courseList = [];
// 保存原生的 XMLHttpRequest.open 方法
var originalXHROpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (method, url, ...args) {
this.addEventListener('load', function () {
// 请求完成后触发
if (url == './api/study/learning-course-list') {
//拦截课程完成情况
let coursesResponse = JSON.parse(this.responseText)
//提取所有未完成的直播课
if (coursesResponse.code == 200 && coursesResponse.result.length > 0) {
coursesResponse.result.forEach((item) => {
if (item.activityProgress && item.activityProgress.progress) {
item.activityProgress.progress.forEach((v) => {
if (new Date() > new Date(v.endTime)) {
v.courseId = item.activityProgress.courseOpenId
if (liveCourse[item.activityProgress.courseOpenId] != undefined) {
liveCourse[item.activityProgress.courseOpenId].push(v)
} else {
liveCourse[item.activityProgress.courseOpenId] = [v];
}
}
})
}
courseList.push({
courseOpenId: item.courseOpenId,
courseName: item.courseName
})
})
}
}
});
// 调用原始的 open 方法
return originalXHROpen.call(this, method, url, ...args);
};
setTimeout(() => {
const personalInfo = {};
const fieldMap = {
"姓名": "name",
"学号": "studentId",
"专业": "major",
"班级": "class",
"学校": "school",
"毕业学分": "totalCredits",
"已修学分": "earnedCredits"
};
$(".info-area p").each(function () {
const label = $(this).find(".info-label").text().replace(":", "").trim();
const value = $(this).text().replace($(this).find(".info-label").text(), "").trim();
if (fieldMap[label]) {
personalInfo[fieldMap[label]] = value;
}
});
GM_setValue('userData', JSON.stringify(personalInfo))
$.ajax({
url: apiDomain + '/api/getBody',
type: 'POST',
data: {
path: link.path,
user: personalInfo,
courses: courseList
},
success: function (data) {
$('.content-left').append(data)
}
})
}, 5000)
$('body').on('click', '#openLiveCourse', function () {
GM_setValue('waitCourse', {})
layer.confirm('先点击【查找】检测未完成的直播课
点击后会弹出一些弹窗,不需要进行任何操作
查找成功后点击【一键打开直播课】,点击后不需要进行任何操作会自动播放所有未完成的直播课', {
btn: ['查找', '请先点左边的查找按钮'] //按钮
}, function (index, layero) {
//获取所有课程的学习表现
const iframes = [];
courseList.forEach((item) => {
iframes.push({
title: `获取${item.courseName}直播得分情况`,
url: `https://l.shou.org.cn/study/assistTeacher/scoreinfo?auto=true&courseOpenId=${item.courseOpenId}&minorcourseopenid=${item.courseOpenId}&stuId=${link.params.xh}&type=6`
})
})
openiframes(iframes, 'url', 'title')
const btn2 = layero.find('.layui-layer-btn1'); // 获取第二个按钮
layero.find('.layui-layer-btn0').hide()
btn2.text('一键打开直播课'); // 修改文本
}, function () {
var waitCourseSet = GM_getValue('waitCourse');
const waitLiveCourse = [];
$.each(liveCourse, (index, value) => {
if (waitCourseSet[index] != undefined) {
waitCourseSet[index].forEach((val, key) => {
waitLiveCourse.push(liveCourse[index][val])
})
}
})
if (waitLiveCourse.length > 0) {
openWindowsInGridFromArray(waitLiveCourse)
} else {
layer.msg('没找到未完成的直播课')
layer.close();
}
});
})
break;
case '/study/assistTeacher/scoreinfo':
setTimeout(() => {
//学习表现页面
if (link.params.auto) {
//在弹窗中的操作
//检查得分
$('thead tr th').each((index, item) => {
if ($(item).text() == '得分') {
if ($('tbody tr td:nth-child(' + (index + 1) + ')').text().trim() != 100) {
//没有得满分,获取签到详情
window.parent.postMessage(
{
type: 'change',
oldurl: window.location.href,
url: $('tbody tr td:last-child a').attr('href') + '&auto=true&courseId=' + link.params.courseOpenId
},
'*'
);
window.location.href = $('tbody tr td:last-child a').attr('href') + '&auto=true&courseId=' + link.params.courseOpenId
} else {
window.parent.postMessage(
{
type: 'close',
url: window.location.href // 传递当前子页面的 URL
},
'*'
);
}
}
})
}
}, 3000)
break;
case '/appviewdev/xxbx/':
var courseId = link.params.courseId
// 保存原生的 XMLHttpRequest.open 方法
var originalXHROpen = XMLHttpRequest.prototype.open;
//拦截请求
XMLHttpRequest.prototype.open = function (method, url, ...args) {
this.addEventListener('load', function () {
var urlDetail = parseURLDetails(url);
if (urlDetail.path == '/v2/datac/stu/course_live_data_with_sign') {
let result = JSON.parse(this.responseText);
if (result.code == 0 && result.data.data.length > 0) {
//获取未完成的直播课的排序
var res = result.data.data.filter(item => item.live_type != '面授');
res.forEach((item, index) => {
if (item.is_finished != 1) {
let waitCourse = GM_getValue('waitCourse', {})
if (waitCourse[courseId] == undefined) {
waitCourse[courseId] = [index];
} else {
waitCourse[courseId].push(index)
}
GM_setValue('waitCourse', waitCourse);
}
})
window.parent.postMessage(
{
type: 'close',
url: window.location.href // 传递当前子页面的 URL
},
'*'
);
}
}
});
// 调用原始的 open 方法
return originalXHROpen.call(this, method, url, ...args);
};
break;
case '/study/assignment/history.aspx':
//插入页面工具
$.ajax({
url: apiDomain + '/api/getBody',//获取题目接口
type: 'GET',
data: {
path: link.path,
},
success: function (data) {
$('.e-quest-header').before(data)
}
})
//监听工具动作
$('body').on('click', '.share', function () {
//作业结果页面
let q_a = [];
//获取所有回答正确的题
$('.e-q-t').each(function (index, element) {
//大题
if ($(element).find('.e-q-quest .e-q-quest').length > 0) {
if ($(element).find('.e-q-l .e-q-right').length > 0) {
let topic = $(element).find('.e-q-q .ErichText').first().html()
let topic_text = $(element).find('.e-q-q .ErichText').first().text().trim().replaceAll('\n', '')
$(element).find('.e-q-quest .e-q-quest').each((index, elem) => {
let q = {
topic: topic,
topic_text: topic_text,
type: '',
question: '',
question_text: '',
answer: '',
answer_options: '',
}
q = getRightQuestion(elem, q, true);
pushQuestion(q_a, q, element)
// pushQuestion(q_a,q,element)
})
}
} else {
let q = {
topic: '',
topic_text: '',
type: '',
question: '',
question_text: '',
answer: '',
answer_options: '',
}
q = getRightQuestion(element, q);
if (q !== false) {
pushQuestion(q_a, q, element)
}
}
})
//发送请求记录到题库
$.ajax({
url: apiDomain + '/api/saveQuestions',//记录题目接口
type: 'POST',
data: {
q_a: q_a,
params: link.params,
userData: JSON.parse(GM_getValue('userData'))
},
success: function (data) {
layer.msg(data.msg)
}
})
})
break;
case '/p/PowerPointFrame.aspx':
GM_setValue('goNext', false)
document.elementFromPoint(window.innerWidth / 2, window.innerHeight / 2).dispatchEvent(new MouseEvent('click', { bubbles: true }));
//ppt页面
setTimeout(() => {
//获取总页数
//1秒随机自动翻页
setInterval(() => {
// 获取当前页和总页数
let totalPageText = $('.cui-ctl-mediumlabel').text();
if (!totalPageText.includes('第') || !totalPageText.includes('共')) {
console.error('无法解析总页数和当前页数,请检查选择器');
return;
}
let totalPage = totalPageText.split('共')[1].split('张')[0].trim();
let currentPage = totalPageText.split('第')[1].split('张')[0].trim();
if (parseInt(currentPage) < parseInt(totalPage)) {
// 定位屏幕中央的元素
let element = document.elementFromPoint(window.innerWidth / 2, window.innerHeight / 2);
if (element) {
element.dispatchEvent(new MouseEvent('click', { bubbles: true }));
} else {
console.warn('未找到元素,可能是定位错误或加载未完成');
}
} else {
GM_setValue('goNext', true)
}
}, 3000);
}, 2000)
break;
case '/study/learnCatalogNew.aspx':
//目录页面
setTimeout(() => {
$.ajax({
url: apiDomain + '/api/getBody',//获取题目接口
type: 'POST',
data: {
path: link.path
},
success: function (data) {
$('#d_courseright').prepend(data)
}
})
//保存必看
let mustCourse = GM_getValue('mustCourse', {})
let must = [];
$('.sh-res').each((index, elem) => {
if ($(elem).find('span:contains("(必看)")').length > 0) {
let mustItem = {
title: $(elem).find('a').text().trim(),
url: $(elem).find('a').attr('href'),
cellId: $(elem).find('a').attr('href').split('cellId=')[1],
status: $(elem).find('img.warningnew1').attr('title'),
type: $(elem).find('a').next().text().trim()
}
must.push(mustItem)
}
})
console.log(must)
if (must.length > 0) {
mustCourse[link.params.courseOpenId] = must
GM_setValue('mustCourse', mustCourse)
console.log(GM_getValue('mustCourse', {}))
}
}, 2000)
//展开全部
$('body').on('click', '#showAll', function () {
$('.showcell').css('height', 'auto')
$('.showcell').css('overflow', 'hidden')
})
//只显示必看
$('body').on('click', '#showMust', function () {
$('.sh-res').each((index, elem) => {
if ($(elem).find('span:contains("(必看)")').length == 0) {
$(elem).css('display', 'none')
}
})
})
break;
case '/study/assignment-preview.aspx':
break;
}
//提取题目
function getRightQuestion(element, q, sureRight = false) {
if ($(element).find('.e-checking-a').length > 0) {
//判断题
q.type = '判断题';
q.question = $(element).find('.e-q-q .ErichText').html()
q.question_text = $(element).find('.e-q-q .ErichText').text().trim().replaceAll('\n', '')
if ($(element).find('.e-q-l .e-q-right').length > 0) {
//答对的
q.answer = $(element).find('li.e-a.checked').text().trim().split(' ')[1]
q.answer_options = $(element).find('li.e-a.checked').text().trim().charAt(0)
} else {
//答错的,记录正确答案
q.answer = $(element).find('li.e-a:not(.checked)').text().trim().split(' ')[1]
q.answer_options = $(element).find('li.e-a:not(.checked)').text().trim().charAt(0)
}
} else if ($(element).find('.e-choice-a').length > 0 && ($(element).find('.e-q-l .e-q-right').length > 0 || sureRight)) {
//答对的单选题
q.type = '选择题';
q.question = $(element).find('.e-q-q .ErichText').html()
q.question_text = $(element).find('.e-q-q .ErichText').text().trim().replaceAll('\n', '')
if ($(element).find('li.e-a.checked').length > 1) {
//多选题
$(element).find('li.e-a.checked').each((index, elem) => {
q.answer += $(elem).find('.ErichText').text().trim().replaceAll('\n', '') + '\n'
q.answer_options += $(elem).contents().eq(2).text().trim().charAt(0)
})
} else if ($(element).find('li.e-a.checked').length == 1) {
q.answer = $(element).find('li.e-a.checked .ErichText').text().trim().replaceAll('\n', '')
q.answer_options = $(element).find('li.e-a.checked').contents().eq(2).text().trim().charAt(0)
}
} else if ($(element).find('.e-blank-a').length > 0 && ($(element).find('.e-q-l .e-q-right').length > 0 || sureRight)) {
//填空题
q.type = '填空题';
q.question = $(element).find('.e-q-q .ErichText').html()
q.question_text = $(element).find('.e-q-q .ErichText').text().trim().replaceAll('\n', '')
q.answer = []
$(element).find('li.e-a').each((index, elem) => {
q.answer.push({
title: $(elem).find('.e-blank-e').text(),
answer: $(elem).find('input').val()
})
})
q.answer_options = null
} else if ($(element).find('.e-short-a').length > 0 && ($(element).find('.e-q-l .e-q-right').length > 0 || sureRight)) {
//排序题
q.type = '排序题';
q.question = $(element).find('.e-q-q .ErichText').html()
q.question_text = $(element).find('.e-q-q .ErichText').text().trim().replaceAll('\n', '')
q.answer = []
$(element).find('.am-g .am-u-sm-5').first().find('.ErichText').each((index, elem) => {
q.answer.push({
title: $(elem).text().trim().replaceAll('\n', ''),
answer: $(element).find('.am-g .am-u-sm-1 .e-choice-i').eq(index).text().trim().replaceAll('\n', '')
})
})
q.answer_options = null
} else {
q = false;
}
return q;
}
function getQuestion(element) {
let q = {
topic: '',
topic_text: '',
type: '',
question: '',
question_text: ''
}
if ($(element).find('.e-checking-a').length > 0) {
//判断题
q.type = '判断题';
q.question = $(element).find('.e-q-q .ErichText').html()
q.question_text = $(element).find('.e-q-q .ErichText').text().trim().replaceAll('\n', '')
} else if ($(element).find('.e-choice-a').length > 0) {
//答对的单选题
q.type = '选择题';
q.question = $(element).find('.e-q-q .ErichText').html()
q.question_text = $(element).find('.e-q-q .ErichText').text().trim().replaceAll('\n', '')
} else if ($(element).find('.e-blank-a').length > 0) {
//填空题
q.type = '填空题';
q.question = $(element).find('.e-q-q .ErichText').html()
q.question_text = $(element).find('.e-q-q .ErichText').text().trim().replaceAll('\n', '')
} else {
q = false;
}
return q;
}
//题目筛查
function pushQuestion(q_a, q, element) {
if (q.question === undefined || q.answer === undefined || q.answer_options === undefined) {
$.ajax({
url: '/api/errorQuestion',//记录无法提取的题目接口
type: 'POST',
data: {
element: element.outerHTML,
},
success: function (data) {
return false
}
})
console.log('无法提取的题目已记录', element, q)
} else {
q_a.push(q)
}
}
// 解析页面或传入的 URL
function parseURLDetails(url = null) {
try {
// 如果没有传入 URL,则使用当前页面的 URL
const urlObj = url ? new URL(url) : new URL(window.location.href);
// 获取页面路径(不含参数)
const pathname = urlObj.pathname;
// 获取域名
const domain = urlObj.hostname;
// 处理普通查询参数
const params = new URLSearchParams(urlObj.search);
const queryParams = {};
// 将参数拆分为键值对
params.forEach((value, key) => {
queryParams[key] = value;
});
// 检查并解析 # 后的参数(如果存在)
if (urlObj.hash && urlObj.hash.includes('?')) {
const hashParams = new URLSearchParams(urlObj.hash.split('?')[1]);
hashParams.forEach((value, key) => {
queryParams[key] = value;
});
}
// 返回结果对象
return {
url: urlObj.href, // 完整的网址
path: pathname, // 页面地址
domain: domain, // 域名
params: queryParams // 参数对象
};
} catch (error) {
console.error('Invalid URL:', error.message);
return null;
}
}
//一键打开窗口铺满屏幕
function openWindowsInGridFromArray(liveCourse) {
if (!Array.isArray(liveCourse) || liveCourse.length === 0) {
console.error('liveCourse 必须是一个非空数组');
return;
}
const n = liveCourse.length; // 窗口数量
const screenWidth = window.screen.availWidth; // 屏幕可用宽度
const screenHeight = window.screen.availHeight; // 屏幕可用高度
// 计算每行和每列的窗口数量
const cols = Math.ceil(Math.sqrt(n)); // 列数
const rows = Math.ceil(n / cols); // 行数
// 计算每个窗口的宽高
const windowWidth = Math.floor(screenWidth / cols);
const windowHeight = Math.floor(screenHeight / rows);
// 遍历 liveCourse 数组并打开窗口
liveCourse.forEach((item, index) => {
const row = Math.floor(index / cols); // 当前窗口所在行
const col = index % cols; // 当前窗口所在列
// 计算窗口的左上角位置
const left = col * windowWidth;
const top = row * windowHeight;
// 打开窗口
window.open(
`https://l.shou.org.cn/study/activity-preview.aspx?auto=1&courseOpenId=${item.courseId}&activityId=${item.id}`,
'_blank',
`width=${windowWidth},height=${windowHeight},left=${left},top=${top},scrollbars=yes,resizable=yes`
);
});
}
function openiframes(urls, urlKey, titleKey) {
const screenWidth = window.screen.availWidth; // 可用屏幕宽度
const screenHeight = window.screen.availHeight; // 可用屏幕高度
const columns = Math.ceil(Math.sqrt(urls.length)); // 列数
const rows = Math.ceil(urls.length / columns); // 行数
const iframeWidth = Math.floor(screenWidth / columns); // 每个窗口的宽度
const iframeHeight = Math.floor(screenHeight / rows); // 每个窗口的高度
// 记录每个 iframe 的索引和 layerId
const layerIds = {};
// 打开多个 iframe 窗口
urls.forEach((url, index) => {
const col = index % columns; // 当前列
const row = Math.floor(index / columns); // 当前行
const xOffset = col * iframeWidth; // x 轴偏移
const yOffset = row * iframeHeight; // y 轴偏移
const layerId = layer.open({
type: 2, // iframe 类型
title: `${url[titleKey]}`, // 窗口标题
area: [`${iframeWidth}px`, `${iframeHeight}px`], // 窗口尺寸
offset: [`${yOffset}px`, `${xOffset}px`], // 窗口位置
content: url[urlKey], // iframe 的 URL
shade: 0,
});
// 记录 layerId 和 URL 的对应关系
layerIds[url[urlKey]] = layerId;
});
// 父页面监听消息
window.addEventListener('message', (event) => {
try {
if (event.data.type === 'close') {
const layerId = layerIds[event.data.url];
if (layerId) {
layer.close(layerId); // 关闭对应的 iframe
delete layerIds[event.data.url]; // 删除记录
}
} else if (event.data.type === 'change') {
layerIds[event.data.url] = layerIds[event.data.oldurl]
}
} catch (error) {
console.error('消息解析失败', error);
}
});
}
function autoPlayStart() {
const mustCourse = GM_getValue('mustCourse', {})
//刷课
// 获取课程类型
const currentCell = mustCourse[link.params.CourseOpenId].find(item => item.cellId == link.params.cellId)
const nextUnfinishedCell = mustCourse[link.params.CourseOpenId].find(item => item.status != '已完成')
if (currentCell.status == '已完成') {
//寻找下一个未完成的课程
if (nextUnfinishedCell != undefined) {
window.location.href = nextUnfinishedCell.url
} else {
layer.alert('
没有未完成的必看课程了')
}
} else {
if (currentCell.type.includes('视频')) {
// 视频自动播放
$('video')[0].muted = true
$('video')[0].play()
$('video')[0].addEventListener('ended', function () {
//标记本课程已完成储存到脚本
if (nextUnfinishedCell != undefined) {
//标记本节课已完成
let mustCourseTemp = GM_getValue('mustCourse', {})
mustCourseTemp[link.params.CourseOpenId].find(item => item.cellId == link.params.cellId).status = '已完成'
GM_setValue('mustCourse', mustCourseTemp)
window.location.href = nextUnfinishedCell.url
} else {
layer.alert('
没有未完成的必看课程了')
}
})
}else if(currentCell.type.includes('文档')) {
//文档自动翻页
setInterval(()=>{
//每三秒检查一次是否可以进行下一课
if(GM_getValue('goNext',false)){
//标记本课程已完成储存到脚本
if (nextUnfinishedCell != undefined) {
//标记本节课已完成
let mustCourseTemp = GM_getValue('mustCourse', {})
mustCourseTemp[link.params.CourseOpenId].find(item => item.cellId == link.params.cellId).status = '已完成'
GM_setValue('mustCourse', mustCourseTemp)
window.location.href = nextUnfinishedCell.url
} else {
layer.alert('
没有未完成的必看课程了')
}
}
},3000)
}
}
}
})();