// ==UserScript== // @name 国开自动刷课(不答题考试) // @namespace http://ibaiyu.top/ // @version 1.0.0 // @description 国开(国家开放大学)自动刷课(不答题考试) 支持自动访问线上链接、查看资料附件、观看视频、自动查看页面。 // @author 南风依旧 // @match *://lms.ouchn.cn/course/* // @original-author 南风依旧 // @original-license GPL-3.0 // @license GPL-3.0 // @downloadURL https://update.greasyfork.icu/scripts/478577/%E5%9B%BD%E5%BC%80%E8%87%AA%E5%8A%A8%E5%88%B7%E8%AF%BE%EF%BC%88%E4%B8%8D%E7%AD%94%E9%A2%98%E8%80%83%E8%AF%95%EF%BC%89.user.js // @updateURL https://update.greasyfork.icu/scripts/478577/%E5%9B%BD%E5%BC%80%E8%87%AA%E5%8A%A8%E5%88%B7%E8%AF%BE%EF%BC%88%E4%B8%8D%E7%AD%94%E9%A2%98%E8%80%83%E8%AF%95%EF%BC%89.meta.js // ==/UserScript== // 设置视频播放速度 建议最大4-8倍速 不然可能会卡 没有最大值 // 并且直接挂载到window上 window.playbackRate = 4; // 设置各种不同类型的课程任务之间的时间延迟,以便脚本在进行自动化学习时可以更好地模拟人类操作。 const interval = { loadCourse: 3000, // 加载课程列表的延迟时间 viewPage: 3000, // 查看页面类型课程的延迟时间 onlineVideo: 3000, // 播放在线视频课程的延迟时间 webLink: 3000, // 点击线上链接类型课程的延迟时间 forum: 3000, // 发帖子给论坛课程的延迟时间 material: 3000, // 查看附件类型课程的延迟时间 other: 3000 // 处理其他未知类型课程的延迟时间 }; (async function (window, document) { // 使用正则表达式从当前 URL 中提取出课程 ID。 const courseId = (await waitForElement("#courseId", interval.loadCourse)).value; // 运行 main(); // 保存值到本地存储 function GM_setValue(name, value) { localStorage.setItem(name, JSON.stringify(value)); } //从本地存储获取值 function GM_getValue(name, defaultValue) { const value = localStorage.getItem(name); if (value === null) { return defaultValue; } try { return JSON.parse(value); } catch (e) { console.error(`Error parsing stored value for ${name}:`, e); return defaultValue; } } // 创建返回到课程列表页面的函数。 async function returnCoursePage(waitTime = 500) { const backElement = await waitForElement("a.full-screen-mode-back", waitTime); if (backElement) { backElement?.click(); } else { throw new Error("异常 无法获取到返回课程列表页面的元素!"); } } // 将中文类型名称转换为英文枚举值。 function getTypeEum(type) { switch (type) { case "页面": return "page"; case "音视频教材": return "online_video"; case "线上链接": return "web_link"; case "讨论": return "forum"; case "参考资料": return "material"; default: return null; } } /** * 等待指定元素出现 * 返回一个Promise对象,对document.querySelector封装了一下 * @param selector dom选择器,像document.querySelector一样 * @param waitTime 等待时间 单位: ms */ async function waitForElement(selector, waitTime = 1000, maxCount = 10) { let count = 0; return new Promise(resolve => { let timeId = setInterval(() => { const element = document.querySelector(selector); if (element || count >= maxCount) { clearInterval(timeId); resolve(element || null); } count++; }, waitTime); }); } /** * 等待多个指定元素出现 * 返回一个Promise对象,对document.querySelectorAll封装了一下 * @param selector dom选择器,像document.querySelectorAll一样 * @param waitTime 等待时间 单位: ms */ async function waitForElements(selector, waitTime = 1000, maxCount = 10) { let count = 0; return new Promise(resolve => { let timeId = setInterval(() => { const element = document.querySelectorAll(selector); if (element || count >= maxCount) { clearInterval(timeId); resolve(element || null); } count++; }, waitTime); }); } // 等待指定时间 function wait(ms) { return new Promise(resolve => { setTimeout(resolve, ms); }); } /** * 该函数用于添加学习行为时长 */ function addLearningBehavior(activity_id, activity_type) { const duration = Math.ceil(Math.random() * 300 + 40); const data = JSON.stringify({ activity_id, activity_type, browser: 'chrome', course_id: globalData.course.id, course_code: globalData.course.courseCode, course_name: globalData.course.name, org_id: globalData.course.orgId, org_name: globalData.user.orgName, org_code: globalData.user.orgCode, dep_id: globalData.dept.id, dep_name: globalData.dept.name, dep_code: globalData.dept.code, user_agent: window.navigator.userAgent, user_id: globalData.user.id, user_name: globalData.user.name, user_no: globalData.user.userNo, visit_duration: duration }); const url = 'https://lms.ouchn.cn/statistics/api/user-visits'; return new Promise((resolve, reject) => { $.ajax({ url, data, type: "POST", cache: false, contentType: "text/plain;charset=UTF-8", complete: resolve }); }); } // 打开并播放在线视频课程。 async function openOnlineVideo() { // 等待 video 或 audio 元素加载完成 const videoElem = await waitForElement('video'); let audioElem = null; if (!videoElem) { audioElem = await waitForElement('audio'); } if (videoElem) { // 处理视频元素 console.log("正在播放视频中..."); // 设置播放速率 videoElem.playbackRate = playbackRate; // 监听播放速率变化事件并重新设置播放速率 videoElem.addEventListener('ratechange', function () { videoElem.playbackRate = playbackRate; }); // 监听视频播放结束事件 videoElem.addEventListener('ended', returnCoursePage); // 延迟一会儿以等待视频加载 await wait(interval.onlineVideo); // // 每隔一段时间检查是否暂停,并模拟点击继续播放并设置声音音量为0 setInterval(() => { videoElem.volume = 0; if (document.querySelector("i.mvp-fonts.mvp-fonts-play")) { document.querySelector("i.mvp-fonts.mvp-fonts-play").click(); } }, interval.onlineVideo); } else if (audioElem) { // 处理音频元素 console.log("正在播放音频中..."); // 监听音频播放结束事件 audioElem.addEventListener("ended", returnCoursePage); // 延迟一会儿以等待音频加载 await wait(interval.onlineVideo); // 每隔一段时间检查是否暂停,并模拟点击继续播放 setInterval(() => { audioElem.volume = 0; if (document.querySelector("i.font.font-audio-play")) { document.querySelector("i.font.font-audio-play").click(); } }, interval.onlineVideo); } } // 打开并查看页面类型课程。 function openViewPage() { // 当页面被加载完毕后延迟一会直接返回课程首页 setTimeout(returnCoursePage, interval.viewPage); } // 打开并点击线上链接类型课程。 async function openWebLink() { // 等待获取open-link-button元素 const ElementOpenLinkButton = await waitForElement(".open-link-button", interval.webLink); // 设置元素属性让它不会弹出新标签并设置href为空并模拟点击 ElementOpenLinkButton.target = "_self"; ElementOpenLinkButton.href = "javascript:void(0);"; ElementOpenLinkButton.click(); // 等待一段时间后执行returnCoursePage函数 setTimeout(returnCoursePage, interval.webLink); } function openApiMaterial() { // 用API去完成查看附件 const id = document.URL.match(/.*\/\/lms.ouchn.cn\/course\/[0-9]+\/learning-activity\/full-screen.+\/([0-9]+)/)[1]; const res = new Promise((resolve, reject) => { $.ajax({ url: `https://lms.ouchn.cn/api/activities/${id}`, type: "GET", success: resolve, error: reject }) }); res.then(async ({ uploads: uploadsModels }) => { uploadsModels.forEach(async ({ id: uploadId }) => { await wait(interval.material); await new Promise(resolve => $.ajax({ url: `https://lms.ouchn.cn/api/course/activities-read/${id}`, type: "POST", data: JSON.stringify({ upload_id: uploadId }), contentType: "application/json", dataType: "JSON", success: resolve, error: resolve })); }); await wait(interval.material); returnCoursePage(); }); res.catch((xhr, status, error) => { console.log(`这里出现了一个异常 | status: ${status}`); console.dir(error, xhr, status); }); } // 打开课程任务并发布帖子。 async function openForum() { // 使用 waitForElement 函数等待 .embeded-new-topic>i 元素出现,并赋值给 topicElement 变量 const topicElement = await waitForElement("button.ivu-btn.ivu-btn-primary i", interval.forum); if (!topicElement) { throw new Error("无法找到发帖按钮的元素。"); } // 点击话题元素并等待一段时间 topicElement.click(); await wait(interval.forum); // 获取标题、内容和提交按钮元素 const titleElem = $("#add-topic-popup > div > div.topic-form-section.main-area > form > div:nth-child(1) > div.field > input"); const contentElem = document.querySelector('#add-topic-popup > div > div.topic-form-section.main-area .simditor-body.needsclick[contenteditable]'); const submitElem = document.querySelector("#add-topic-popup > div > div.popup-footer > div > button.button.button-green.medium"); // 设置标题和内容 titleElem.val(`好好学习${Date.now()}`).trigger('change'); // 点击提交按钮并延迟一段时间后返回课程页面 contentElem.innerHTML = `
好好学习,天天向上。${Date.now()}
`; submitElem.click(); // 等待一段时间后执行returnCoursePage函数 setTimeout(returnCoursePage, interval.forum); } // 课程首页处理 async function courseIndex() { await new Promise(resolve => { console.log("正在展开所有课程任务"); let timeId = setInterval(() => { const allCollapsedElement = document.querySelector("i.icon.font.font-toggle-all-collapsed"); const allExpandedElement = document.querySelector("i.icon.font.font-toggle-all-expanded"); if (!allExpandedElement) { if (allCollapsedElement) { allCollapsedElement.click(); } } if (!allCollapsedElement && !allExpandedElement) { throw new Error("无法展开所有课程 可能是元素已更改,请联系作者更新。"); } { console.log("课程展开完成。"); clearInterval(timeId); resolve(); } }, interval.loadCourse); }); console.log("正在获取加载的课程任务"); const courseElements = await waitForElements('.learning-activity .clickable-area', interval.loadCourse); const courseElement = Array.from(courseElements).find(elem => { const type = $(elem.querySelector('i.font[original-title]')).attr('original-title'); // 获取该课程任务的类型 // const status = $(elem.querySelector('span.item-status')).text(); // 获取该课程任务是否进行中 // 👆上行代码由于无法获取到课程任务是否已关闭,目前暂时注释掉 if(type=='讨论'){ return false; } const typeEum = getTypeEum(type); if (!typeEum) { return false; } const completes = elem.querySelector('.ivu-tooltip-inner b').textContent === "已完成" ? true : false; // const result = status === "进行中" && typeEum != null && completes === false; const result = typeEum != null && completes === false; if (result) { GM_setValue(`typeEum-${courseId}`, typeEum); } return result; }); console.log(courseElement); if (courseElement) { console.log("发现未完成的课程"); $(courseElement).click(); } else { console.log("课程可能全部完成了"); } } function main() { if (/https:\/\/lms.ouchn.cn\/course\/\d+\/ng.*#\//m.test(document.URL)) { // 判断是否在课程首页 courseIndex(); } else if (/http[s]?:\/\/lms.ouchn.cn\/course\/\d+\/learning-activity\/full-screen[#]?\//.test(window.location.href)) { let timeId = 0; const activity_id = /http[s]?:\/\/lms.ouchn.cn\/course\/\d+\/learning-activity\/full-screen[#]?\/(\d+)/.exec(window.location.href)[1]; const typeEum = GM_getValue(`typeEum-${courseId}`, null); addLearningBehavior(activity_id, typeEum); switch (typeEum) { case "page": console.log("正在查看页面。"); openViewPage(); return; case "online_video": openOnlineVideo(); return; case "web_link": console.log("正在点击外部链接~"); openWebLink(); return; case "forum": console.log("发帖中!"); returnCoursePage() //openForum(); return; case "material": console.log("正在给课件发送已阅读状态"); openApiMaterial(); return; default: setTimeout(returnCoursePage, interval.other); return; } } } })(window, document);