国开自动刷课

国家开放大学自动刷课

目前为 2023-05-05 提交的版本。查看 最新版本

// ==UserScript==
// @name         国开自动刷课
// @namespace    ry版
// @version      202305051250
// @description  国家开放大学自动刷课
// @author       流浪的蛊惑
// @match        *://lms.ouchn.cn/course/*
// @license      GPL-3.0
// @grant        none
// ==/UserScript==
function LogHelper() {
    const el_text = `
    <container-element class="normal"
        style="left: 10px; top: 50px; font: 14px Menlo, Monaco, Consolas, 'Courier New', monospace;">
        <style>
            /** 默认字体 */
            /** 输入框默认边距 */
            ul,
            ol {
                padding-left: 16px;
                margin: 0px;
            }

            a {
                color: #1890ff;
            }

            hr {
                border-style: solid;
                border-color: #63636346;
                border-width: 0px;
                border-bottom: 1px solid #63636346;
                margin-block-start: 1em;
                margin-block-end: 1em;
            }

            container-element.close {
                display: none;
            }

            container-element.minimize {
                min-width: unset;
            }

            container-element {
                position: fixed;
                top: 10%;
                left: 10%;
                z-index: 9999999999;
                text-align: left;
                min-width: 300px;
                -webkit-font-smoothing: antialiased;
                -moz-osx-font-smoothing: grayscale;
                color: #636363;
                box-shadow: 0 0 24px -12px #3f3f3f;
                border-radius: 8px;
            }

            container-element .header {
                display: flex;
                align-items: center;
                background-color: white;
                border-radius: 8px 8px 0px 0px;
                user-select: none;
                padding: 4px;
                color: #000;
                line-height: normal;
                height: auto;
                position: static;
            }

            container-element .header .profile {
                flex: 1;
                cursor: move;
            }

            container-element .header .switch:hover,
            container-element .header .dropdown:hover {
                background-color: #f3f3f3;
            }

            container-element .header .close:hover {
                background-color: #ff000038;
            }

            container-element .header .switch,
            container-element .header .close {
                cursor: pointer;
            }

            container-element .header .dropdown {
                line-height: 24px;
            }

            container-element .header .switch,
            container-element .header .close,
            container-element .header .profile {
                display: inline-flex;
                align-items: center;
                padding: 0px 8px;
            }

            container-element .logo {
                width: 18px;
                height: 18px;
                cursor: pointer;
            }



            container-element .body {
                overflow: auto;
                width: auto;
                height: 100%;
            }

            script-panel-element {
                display: block;
                background-color: white;
                border-radius: 0px 0px 8px 8px;
                padding: 0px 8px 12px 8px;
                resize: vertical;
                overflow: auto;

            }

            script-panel-element .script-panel-body {
                padding: 0px 8px;
            }

            script-panel-element+script-panel-element {
                margin-top: 12px;
            }

            container-element .card+.card {
                margin-top: 12px;
            }


            container-element .card {
                background-color: white;
                border-radius: 2px;
                padding: 0px 8px;
                border: none;
            }

            container-element .separator {
                display: flex;
                align-items: center;
                text-align: center;
                padding-bottom: 4px;
            }

            container-element .separator::before,
            container-element .separator::after {
                content: '';
                flex: 1;
                border-bottom: 1px solid #63636346;
            }


            container-element .console {
                max-height: 300px;
                max-width: 400px;
                overflow: auto;
                background-color: #292929;
                padding: 12px 6px;
                color: #ececec;
                font-size: 12px;
            }

            container-element .console .item {
                padding: 3px 0px;
                border-radius: 2px;
            }

            container-element .console .item .time {
                color: #757575;
            }

            container-element .console .item .info {
                background-color: #2196f3a3;
            }

            container-element .console .item .warn {
                background-color: #ffc107db;
            }

            container-element .console .item .error {
                background-color: #f36c71cc;
            }

            container-element .console .item .debug,
            container-element .console .item .log {
                background-color: #9e9e9ec4;
            }

            container-element .console *::selection {
                background-color: #ffffff6b;
            }

            /* 设置滚动条的样式 */
            container-element ::-webkit-scrollbar {
                width: 10px;
                height: 10px;
            }

            /* 滚动槽 */
            container-element ::-webkit-scrollbar-track {
                background: #ffffffd8;
                border-radius: 4px;
                margin: 4px;
            }

            /* 滚动条滑块 */
            container-element ::-webkit-scrollbar-thumb {
                border-radius: 4px;
                background: rgba(0, 0, 0, 0.253);
                box-shadow: inset006pxrgba(0, 0, 0, 0.3);
            }
            .footer {
                text-align: center;
            }
            .footer a {
                text-decoration: none;
            }
        </style>
        <div class="message-container"></div><header-element class="header" data-title="菜单栏-可拖动区域">
            <div class="profile" data-title="菜单栏(可拖动区域)">日志输出</div>
        </header-element>
        <div class="body" style="max-height: 294px; max-width: 1870px;"><script-panel-element>
                <div class="separator">📄 日志输出</div>
                <div class="notes card"></div>
                <div class="configs card">
                    <div class="configs-body"></div>
                </div>
                <div class="script-panel-body">
                    <div class="card console">

                    </div>
                </div>
            </script-panel-element></div>
        <div class="footer">
            <a id="startTech" href="javascript:void(0);">点我开始</a>
        </div>
    </container-element>
    `;
    $('.wrapper').append(el_text);
    this.WriteHtmlLine = (htmlContent, alignCenter = false, border = { borderTop: false, borderBottom: false }) => {
        const el = document.createElement('div');
        container = document.querySelector('container-element');
        el.classList.add('item');
        el.innerHTML = htmlContent;
        if (alignCenter) {
            el.style.textAlign = "center";
        };
        if (border.borderTop) {
            el.style.borderTop = "1px solid #767676";
        }
        if (border.borderBottom) {
            el.style.borderBottom = "1px solid #767676";
        }
        console.log(htmlContent);
        const body = container.querySelector('.body');
        const logEl = container.querySelector('.console');
        body.scrollTop = body.scrollHeight;
        logEl.appendChild(el);
        logEl.scrollTop = logEl.scrollHeight;
    }
}

(function () {
    const Log = new LogHelper();
    const notificationTypesAndText = {
        "material": "参考资料",
        "web_link": "线上链接",
        "online_video": "音视频教材",
        "slide": "微课",
        "lesson": "录播教材",
        "homework": "作业",
        "forum": "讨论区",
        "chatroom": "iSlide 直播",
        "questionnaire": "调查问卷",
        "page": "页面",
        "course_invite": "課程邀請",
        "scorm": "SCORM"
    };
    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() * (15 - 8) + 8) * 1000); // 取8000 - 15000之间的毫秒随机数
            await new Promise(resolve => setTimeout(resolve, sleep));
            Log.WriteHtmlLine(`课程模块:${CoursesModulesModel.name}(${CoursesModulesModel.id}) 当前进度${StrartCompleteness}% 随机延迟: <span class="time">${sleep}毫秒</span>`, 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, uploads } = LearnActivitieModel;
                    if (CompletedCourseModels.indexOf(parseInt(id)) !== -1) {
                        Log.WriteHtmlLine(`课程模块:${CoursesModulesModel.name} 模块标题:${title}(${notificationTypesAndText[type]}) 完成标准:${getCriterion(completion_criterion)}(${id}) <span class="info">已完成 跳过</span>`, false, { borderBottom: true });
                        continue;
                    } else {
                        await new Promise(resolve => setTimeout(resolve, sleep));
                        Log.WriteHtmlLine(`课程模块:${CoursesModulesModel.name} 模块标题:${title}(${notificationTypesAndText[type]}) 完成标准:${getCriterion(completion_criterion)}(${id}) 任务开始`, false, { borderBottom: true });
                    }
                    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)} <span class="info">完成</span>`);
                            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 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)} <span class="info">完成</span>`);
                            }
                            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)} <span class="info">完成</span>`);
                            }
                            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: "好好学习",
                                        content: "<p>好好学习,天天向上。</p>",
                                        category_id: CategoryId,
                                        uploads: []
                                    }),
                                    success: resolve,
                                    error: resolve
                                }));
                                Log.WriteHtmlLine(`模块标题:${title}(${notificationTypesAndText[type]}) 完成标准:${getCriterion(completion_criterion)} <span class="info">完成</span>`);
                            } else {
                                Log.WriteHtmlLine(`模块标题:${title}(${notificationTypesAndText[type]}) 完成标准:${getCriterion(completion_criterion)} 不需要发帖 <span class="info">完成</span>`);
                            }
                            break;
                        case "web_link":
                            Log.WriteHtmlLine(`模块标题:${title}(${notificationTypesAndText[type]}) 完成标准:${getCriterion(completion_criterion)} <span class="info">完成</span>`);
                            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)} <span class="warn">未完成</span> <br> 该任务无法完成。`);
                            break;
                    }
                }
            } catch (error) {
                Log.WriteHtmlLine(`<span class="error">代码出现了异常 按F12在控制台查看错误。</span>`, true, { borderBottom: true });
                console.error(error);
                await new Promise(resolve => setTimeout(resolve, sleep));
            }
            Log.WriteHtmlLine(`课程模块:${CoursesModulesModel.name}(${CoursesModulesModel.id}) 随机延迟: <span class="time">${sleep}毫秒</span>`, 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}% 耗时: <span class="time">${((EndTime - StartTime) / 1000).toFixed(2)}秒</span>`);
    }

    $('#startTech').on({
        click: function () {
            const courseId = document.querySelector("#courseId").value;
            LearnCourseId(courseId);
            this.onclick = null;
            this.style.cursor = "no-drop";
            this.style.color = "#ccc";
        }
    })
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址