// ==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(`
上海开大助手
1
`) } 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) } } } })();