// ==UserScript== // @name 调用国开API自动刷课(不答题考试) // @namespace http://ibaiyu.top/ // @version 1.3.4 // @description 调用国开API自动刷课(不答题考试) 支持自动访问线上链接、查看资料附件、观看视频、自动查看页面、自动参与发帖回帖。调用API接口实现! // @author 蜜桃加乌龙 // @icon data:image/png;base64,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 // @match *://lms.ouchn.cn/course/* // @license GPL-3.0 // @source https://scriptcat.org/script-show-page/986/ // @original-author 蜜桃加乌龙 // @original-license GPL-3.0 // @original-script https://scriptcat.org/script-show-page/986/ // @note 1.2.1:脚本无任何更新,主要是为了更新版本号 // @note 1.2.2:修复无法使用的BUG // @note 1.2.3:修复发帖功能无法使用的BUG // @note 1.3.0: 本次更新将会调用学习分析的API请求 这回在学习分析也可以看到学习记录啦!! // @note 1.3.1: 更新版本号 // @note 1.3.2: 更新控制台输出颜色 // @note 1.3.3: 更新控制台输出背景颜色 // @note 1.3.4: 优化部分速度和代码 // ==/UserScript== function LogHelper() { if (document.querySelector('container-element') == null) $('.wrapper').append(this.el_text); } LogHelper.prototype = { constructor: LogHelper, el_text: `
日志输出
📄 日志输出
`, WriteHtmlLine: (htmlContent, alignCenter = false, border = { borderTop: false, borderBottom: false }) => { const el = document.createElement('div'); container = document.querySelector('container-element'); el.classList.add('item'); if (alignCenter) { el.style.textAlign = "center"; }; if (border.borderTop) { el.style.borderTop = "1px solid #767676"; } if (border.borderBottom) { el.style.borderBottom = "1px solid #767676"; } const body = container.querySelector('.body'); const logEl = container.querySelector('.console'); logEl.appendChild(el); body.scrollTop = body.scrollHeight; logEl.scrollTop = logEl.scrollHeight; const result = [...htmlContent.matchAll(/(.+)<\/span>/g)][0]; if (result === undefined) { console.log("\n" + htmlContent); return; } htmlContent = htmlContent.replace(/(.+)<\/span>/g, `
${result[2]}`); el.innerHTML = htmlContent; htmlContent = htmlContent.replace(/(.+)<\/span>/gm, "%c" + result[2]) htmlContent = htmlContent.replace(/
/gm, "\n"); let color = "#757575"; switch (result[1]) { case "info": color = "#2196f3a3"; break; case "warn": color = "#ffc107db"; break; case "error": color = "#f36c71cc"; break; case "debug": color = "#9e9e9ec4"; break; case "log": color = "#9e9e9ec4"; } console.log("\n" + htmlContent, `color: #fff; background: ${color}; padding: 3px 2px; border-radius: 3px;`); } }; const wait = async (sleep) => new Promise(resolve => setTimeout(resolve, sleep)); (function () { const Log = new LogHelper(); const notificationTypesAndText = { "material": "参考资料", "web_link": "线上链接", "online_video": "音视频教材", "slide": "微课", "lesson": "录播教材", "homework": "作业", "forum": "讨论区", "chatroom": "iSlide 直播", "questionnaire": "调查问卷", "page": "页面", "course_invite": "課程邀請", "scorm": "SCORM" }; /** * 该函数用于添加学习行为时长 * 直接返回一个定时器 */ const globalData = { "course": { "id": 53636, "name": "汽车装饰与美容", "orgId": 8, "orgName": "广州开放大学", "orgCode": "441", "courseCode": "202303-50596441", "endDate": "None", "enableFaceService": "", "isSimulatingInstructor": false, "isInstructorView": false, "isMaster": false }, "user": { "id": 1996816, "name": "肖秉越", "userNo": "2244106453851", "orgId": 8, "mobile": "13411110761", "orgName": "广州开放大学", "orgCode": "441", "isCourseAdmin": false }, "dept": { "id": "1920", "name": "工贸技师分校", "code": "4417201" }, "isOpenUniversity": true, "courseRoles": [ "student" ], "deliveryOrg": "ouchn", "useSinglePage": true, "expandActivityInfo": false } 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 }); }); } function addVideoLearningRecords({ start_at, end_at, syllabus_id, activity_id, upload_id }) { const url = "https://lms.ouchn.cn/statistics/api/online-videos"; const duration = Math.ceil(Math.random() * 300 + 40); const data = JSON.stringify({ syllabus_id, activity_id, upload_id, start_at, end_at, duration, "user_id": globalData.user.id, "org_id": globalData.user.orgId, "course_id": globalData.course.id, "is_teacher": false, "is_student": true, "ts": Date.now(), "user_agent": window.navigator.userAgent, "meeting_type": "online_video", "org_name": globalData.user.orgName, "org_code": globalData.course.orgCode, "user_no": globalData.user.userNo, "user_name": globalData.user.name, "course_code": globalData.course.courseCode, "course_name": globalData.course.name, }); return new Promise((resolve, reject) => { $.ajax({ url, data, type: "POST", cache: false, contentType: "text/plain;charset=UTF-8", complete: resolve }); }); } async function LearnCourseId(courseId) { Log.WriteHtmlLine("===== 初始化中 =====", true, { borderBottom: true }); const getCriterion = completion_criterion => completion_criterion == undefined || completion_criterion == "" ? "无" : completion_criterion; const StartTime = performance.now(); // 代码开始时间 const StartCompletenessData = await new Promise(resolve => $.get(`https://lms.ouchn.cn/api/course/${courseId}/my-completeness`, (data, status, xhr) => status === "success" ? resolve(data) : { study_completeness: undefined })); const { study_completeness: StrartCompleteness } = StartCompletenessData; const CoursesModulesData = await new Promise(resolve => $.get(`https://lms.ouchn.cn/api/courses/${courseId}/modules`, (data, status, xhr) => status === "success" ? resolve(data) : { modules: [] })); const { modules: CoursesModulesModels } = CoursesModulesData; const CompletedCourseData = StartCompletenessData; const CompletedCourseModels = CompletedCourseData.completed_result.completed.learning_activity; for (let CoursesModulesModel of CoursesModulesModels) { let sleep = parseInt((Math.random() * (13 - 8) + 8) * 1000); // 取8000 - 13000之间的毫秒随机数 await wait(sleep); Log.WriteHtmlLine(`课程模块:${CoursesModulesModel.name}(${CoursesModulesModel.id}) 当前进度${StrartCompleteness}% 随机延迟: ${sleep}毫秒`, true, { borderBottom: true }); // 日志输出 const LearnActivitieData = await new Promise(resolve => $.get(`https://lms.ouchn.cn/api/course/${courseId}/all-activities?module_ids=[${CoursesModulesModel.id}]&activity_types=learning_activities,exams,classrooms`, (data, status, xhr) => status === "success" ? resolve(data) : { learning_activities: [] })); const { learning_activities: LearnActivitieModels } = LearnActivitieData; try { for (let LearnActivitieModel of LearnActivitieModels) { const { completion_criterion, type, title, id, // activity_id uploads, // uploads[x].id syllabus_id, } = LearnActivitieModel; if (CompletedCourseModels.indexOf(parseInt(id)) !== -1) { Log.WriteHtmlLine(`课程模块:${CoursesModulesModel.name} 模块标题:${title}(${notificationTypesAndText[type]}) 完成标准:${getCriterion(completion_criterion)}(${id}) 已完成 跳过`, false, { borderBottom: true }); continue; } else { Log.WriteHtmlLine(`课程模块:${CoursesModulesModel.name} 模块标题:${title}(${notificationTypesAndText[type]}) 完成标准:${getCriterion(completion_criterion)}(${id}) 任务开始`, false, { borderBottom: true }); } if (type === "online_video") { await wait(sleep); } else { await wait(3000); } await addLearningBehavior(id, type); switch (type) { case "page": await new Promise(resolve => $.post(`https://lms.ouchn.cn/api/course/activities-read/${id}`, {}, resolve)); Log.WriteHtmlLine(`模块标题:${title}(${notificationTypesAndText[type]}) 完成标准:${getCriterion(completion_criterion)} 完成`); break; case "online_video": for (let VideoUploadModel of uploads) { await new Promise(resolve => $.post(`https://lms.ouchn.cn/api/course/activities-read/${id}`, {}, resolve)); // 第一次的请求默认为没有参数。 for (let item of VideoUploadModel.videos) { await addVideoLearningRecords({ syllabus_id, activity_id: id, upload_id: VideoUploadModel.id, start_at: 0, end_at: item.duration, }); await new Promise(resolve => setTimeout(resolve, sleep)); await new Promise(resolve => $.ajax({ type: "POST", url: `https://lms.ouchn.cn/api/course/activities-read/${id}`, contentType: "application/json", dataType: "JSON", data: JSON.stringify({ start: 0, end: item.duration }), success: resolve, error: resolve })); } Log.WriteHtmlLine(`模块标题:${title}(${notificationTypesAndText[type]}) 完成标准:${getCriterion(completion_criterion)} 完成`); } break; case "material": for (let uploadModel of uploads) { await new Promise(resolve => $.ajax({ type: "POST", url: `https://lms.ouchn.cn/api/course/activities-read/${id}`, contentType: "application/json", dataType: "JSON", data: JSON.stringify({ upload_id: uploadModel.id }), success: resolve })); Log.WriteHtmlLine(`模块标题:${title}(${notificationTypesAndText[type]}) 完成标准:${getCriterion(completion_criterion)} 完成`); } break; case "forum": if (title === "课程答疑讨论区") { const { topic_category: { id: CategoryId } } = await new Promise(resolve => $.get(`https://lms.ouchn.cn/api/forum/${id}/category?fields=id`, {}, resolve)); await new Promise(resolve => $.ajax({ type: "POST", url: `https://lms.ouchn.cn/api/topics`, contentType: "application/json", dataType: "JSON", data: JSON.stringify({ title: `好好学习${Date.now()}`, content: `

好好学习,天天向上。${Date.now()}

`, category_id: CategoryId, uploads: [] }), success: resolve, error: resolve })); Log.WriteHtmlLine(`模块标题:${title}(${notificationTypesAndText[type]}) 完成标准:${getCriterion(completion_criterion)} 完成`); } else { Log.WriteHtmlLine(`模块标题:${title}(${notificationTypesAndText[type]}) 完成标准:${getCriterion(completion_criterion)} 不需要发帖 完成`); } break; case "web_link": Log.WriteHtmlLine(`模块标题:${title}(${notificationTypesAndText[type]}) 完成标准:${getCriterion(completion_criterion)} 完成`); await new Promise(resolve => $.post(`https://lms.ouchn.cn/api/course/activities-read/${id}`, {}, resolve)); break; default: Log.WriteHtmlLine(`模块标题:${LearnActivitieModel.title}(${notificationTypesAndText[type]}) 完成标准:${getCriterion(completion_criterion)} 未完成
该任务无法完成。`); break; } } } catch (error) { Log.WriteHtmlLine(`代码出现了异常 按F12在控制台查看错误。`, true, { borderBottom: true }); console.error(error); await new Promise(resolve => setTimeout(resolve, sleep)); } Log.WriteHtmlLine(`课程模块:${CoursesModulesModel.name}(${CoursesModulesModel.id}) 随机延迟: ${sleep}毫秒`, true, { borderBottom: true }); } const EndCompletenessData = await new Promise(resolve => $.get(`https://lms.ouchn.cn/api/course/${courseId}/my-completeness`, (data, status, xhr) => status === "success" ? resolve(data) : { study_completeness: undefined })); const { study_completeness: EndCompleteness } = EndCompletenessData; const EndTime = performance.now(); // 代码结束时间 Log.WriteHtmlLine(`学习前进度:${StrartCompleteness}% 学习后进度:${EndCompleteness}% 耗时: ${((EndTime - StartTime) / 1000).toFixed(2)}秒`); } $('#startTech').on({ click: function () { const courseId = document.querySelector("#courseId").value; LearnCourseId(courseId); this.onclick = null; this.style.cursor = "no-drop"; this.style.color = "#ccc"; } }) })()