// ==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";
}
})
})()