Greasy Fork

Greasy Fork is available in English.

QingJiaoHelper

青骄第二课堂小助手: 长期更新 | 2022 知识竞赛 | 跳过视频 | 自动完成所有课程 | 每日领取学分 | 课程自动填充答案

当前为 2023-10-01 提交的版本,查看 最新版本

// ==UserScript==
// @name                 QingJiaoHelper
// @namespace            http://tampermonkey.net/
// @version              0.3.0
// @description          青骄第二课堂小助手: 长期更新 | 2022 知识竞赛 | 跳过视频 | 自动完成所有课程 | 每日领取学分 | 课程自动填充答案
// @author               WindLeaf233
// @match                *://www.2-class.com/*
// @match                *://2-class.com/*
// @grant                GM_addStyle
// @grant                GM_getResourceText
// @grant                GM_registerMenuCommand
// @grant                GM_getValue
// @grant                GM_setValue
// @license              GPL-3.0
// @supportURL           https://github.com/WindLeaf233/QingJiaoHelper
// @require              https://cdn.bootcdn.net/ajax/libs/toastify-js/1.12.0/toastify.min.js
// @require              http://greasyfork.icu/scripts/453791-lib2class/code/lib2class.js?version=1258326
// @require              https://cdn.bootcdn.net/ajax/libs/axios/1.3.6/axios.min.js
// @resource toastifycss https://cdn.bootcdn.net/ajax/libs/toastify-js/1.12.0/toastify.min.css
// @resource spectrecss  https://cdn.jsdelivr.net/gh/WindLeaf233/QingJiaoHelper/spectre.css
// ==/UserScript==
var _this = this;
var apiGetGradeLevels = {
    method: "GET",
    api: "/course/getHomepageGrade",
};
var apiGetCoursesByGradeLevel = {
    method: "GET",
    api: "/course/getHomepageCourseList?grade=${grade}&pageSize=50&pageNo=1",
};
var apiGetSelfCoursesByGradeLevel = {
    method: "GET",
    api: "/course/getHomepageCourseList?grade=自学&pageNo=1&pageSize=500&sort=&type=${grade}",
};
var apiGetTestPaperList = {
    method: "GET",
    api: "/exam/getTestPaperList?courseId=${courseId}",
};
var apiCommitExam = {
    method: "POST",
    api: "/exam/commit",
};
var apiAddMedal = {
    method: "GET",
    api: "/medal/addMedal",
};
var apiGetBeforeResourcesByCategoryName = {
    method: "POST",
    api: "/resource/getBeforeResourcesByCategoryName",
};
var apiAddPCPlayPV = {
    method: "POST",
    api: "/resource/addPCPlayPV",
};
var apiLikePC = {
    method: "POST",
    api: "/resource/likePC",
};
function requestAPI(api, params, data) {
    return __awaiter(this, void 0, void 0, function () {
        var method, origin, url, key;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    method = api.method;
                    origin = "https://www.2-class.com";
                    url = "".concat(origin, "/api").concat(api.api);
                    for (key in params) {
                        url = url.replaceAll("${" + key + "}", params[key]);
                    }
                    if (!(method === "GET")) return [3 /*break*/, 2];
                    return [4 /*yield*/, axios({ method: "GET", url: url })
                            .then(function (response) {
                            var rdata = response.data;
                            console.debug("[".concat(method, "] ").concat(url), data, rdata);
                            if (rdata.success === false || rdata.data === null) {
                                var errorMessage = rdata.errorMsg;
                                var errorCode = rdata.errorCode;
                                console.error("API 返回错误 [".concat(errorCode, "]:").concat(errorMessage, ",请刷新页面重试!"));
                                return null;
                            }
                            else {
                                return rdata;
                            }
                        })
                            .catch(function (reason) {
                            showMessage("请求 API 失败(".concat(reason.code, "):").concat(reason.message, "\n请将控制台中的具体报错提交!"), "red");
                            console.error("请求失败(".concat(reason.status, "/").concat(reason.code, ")→").concat(reason.message, "→"), reason.toJSON(), reason.response, reason.stack);
                        })];
                case 1: return [2 /*return*/, _a.sent()];
                case 2: return [4 /*yield*/, axios({
                        method: "POST",
                        url: url,
                        headers: {
                            "Content-Type": "application/json;charset=UTF-8",
                        },
                        data: data,
                    }).then(function (response) {
                        var rdata = response.data;
                        console.debug("[".concat(method, "] ").concat(url), data, rdata);
                        if (rdata.success === false || rdata.data === null) {
                            var errorMessage = rdata.errorMsg;
                            var errorCode = rdata.errorCode;
                            console.error("API 返回错误 [".concat(errorCode, "]:").concat(errorMessage, ",请刷新页面重试!"));
                            return null;
                        }
                        else {
                            return rdata;
                        }
                    })];
                case 3: return [2 /*return*/, _a.sent()];
            }
        });
    });
}
function getAvailableGradeLevels() {
    return __awaiter(this, void 0, void 0, function () {
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, requestAPI(apiGetGradeLevels).then(function (data) {
                        return data ? data.data.map(function (it) { return it.value; }) : null;
                    })];
                case 1: return [2 /*return*/, _a.sent()];
            }
        });
    });
}
function getCoursesByGradeLevel(gradeLevel) {
    return __awaiter(this, void 0, void 0, function () {
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, requestAPI(apiGetCoursesByGradeLevel, {
                        grade: gradeLevel,
                    }).then(function (data) {
                        return data ? data.data.list : null;
                    })];
                case 1: return [2 /*return*/, _a.sent()];
            }
        });
    });
}
function getSelfCoursesByGradeLevel(gradeLevel) {
    return __awaiter(this, void 0, void 0, function () {
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, requestAPI(apiGetSelfCoursesByGradeLevel, {
                        grade: gradeLevel,
                    }).then(function (data) {
                        return data ? data.data.list : null;
                    })];
                case 1: return [2 /*return*/, _a.sent()];
            }
        });
    });
}
function getTestPaperList(courseId) {
    return __awaiter(this, void 0, void 0, function () {
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, requestAPI(apiGetTestPaperList, { courseId: courseId }).then(function (data) {
                        return data ? data.data.testPaperList : null;
                    })];
                case 1: return [2 /*return*/, _a.sent()];
            }
        });
    });
}
function getCourseAnswers(courseId) {
    return __awaiter(this, void 0, void 0, function () {
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, getTestPaperList(courseId).then(function (testPaperList) {
                        if (!isNone(testPaperList)) {
                            var answers = testPaperList.map(function (column) { return column.answer; });
                            console.debug("成功获取课程 [".concat(courseId, "] 的答案"), answers);
                            return answers.map(function (it) { return it.split("").join(","); });
                        }
                        else {
                            console.error("无法获取课程 [".concat(courseId, "] 答案!"));
                            return null;
                        }
                    })];
                case 1: return [2 /*return*/, _a.sent()];
            }
        });
    });
}
function commitExam(data) {
    return __awaiter(this, void 0, void 0, function () {
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, requestAPI(apiCommitExam, {}, data)];
                case 1: return [2 /*return*/, _a.sent()];
            }
        });
    });
}
function addMedal() {
    return __awaiter(this, void 0, void 0, function () {
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, requestAPI(apiAddMedal).then(function (data) {
                        if (isNone(data)) {
                            return null;
                        }
                        else {
                            var flag = data.flag;
                            var num = data.medalNum;
                            if (flag) {
                                return num;
                            }
                            else {
                                return undefined;
                            }
                        }
                    })];
                case 1: return [2 /*return*/, _a.sent()];
            }
        });
    });
}
function getBeforeResourcesByCategoryName(data) {
    return __awaiter(this, void 0, void 0, function () {
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, requestAPI(apiGetBeforeResourcesByCategoryName, {}, data).then(function (data) {
                        return data
                            ? data.data.list.map(function (it) {
                                return {
                                    title: it.briefTitle,
                                    resourceId: it.resourceId,
                                };
                            })
                            : null;
                    })];
                case 1: return [2 /*return*/, _a.sent()];
            }
        });
    });
}
function addPCPlayPV(data) {
    return __awaiter(this, void 0, void 0, function () {
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, requestAPI(apiAddPCPlayPV, {}, data).then(function (data) {
                        return data ? data.data.result : null;
                    })];
                case 1: return [2 /*return*/, _a.sent()];
            }
        });
    });
}
function likePC(data) {
    return __awaiter(this, void 0, void 0, function () {
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, requestAPI(apiLikePC, {}, data).then(function (data) {
                        if (isNone(data)) {
                            return null;
                        }
                        else {
                            var rdata = data.data;
                            return !Number.isNaN(Number(rdata)) || rdata.errorCode === "ALREADY_like";
                        }
                    })];
                case 1: return [2 /*return*/, _a.sent()];
            }
        });
    });
}
var scriptName = "QingJiaoHelper";
var scriptVersion = "v0.3.0";
var toastifyDuration = 3 * 1000;
var toastifyGravity = "top";
var toastifyPosition = "left";
var __DATA__ = function () { return window["__DATA__"]; };
var reqtoken = function () { return __DATA__().reqtoken; };
var userInfo = function () { return __DATA__().userInfo; };
var isLogined = function () { return JSON.stringify(userInfo()) !== "{}"; };
var accountGradeLevel = function () {
    return isLogined() ? userInfo().department.gradeName : "未登录";
};
var coursesGradeLevels = function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
    switch (_a.label) {
        case 0: return [4 /*yield*/, getAvailableGradeLevels()];
        case 1: return [2 /*return*/, _a.sent()];
    }
}); }); };
var selfCoursesGradeLevels = function () { return __awaiter(_this, void 0, void 0, function () {
    return __generator(this, function (_a) {
        return [2 /*return*/, [
                "小学",
                "初中",
                "高中",
                "中职",
                "通用",
            ]];
    });
}); };
("use strict");
var isTaskCoursesEnabled = function () {
    return getGMValue("qjh_isTaskCoursesEnabled", false);
};
var isTaskSelfCourseEnabled = function () {
    return getGMValue("qjh_isTaskSelfCourseEnabled", false);
};
var isTaskGetCreditEnabled = function () {
    return getGMValue("qjh_isTaskGetCreditEnabled", false);
};
var isTaskSingleCourseEnabled = function () {
    return getGMValue("qjh_isTaskSingleCourseEnabled", true);
};
var isTaskSkipEnabled = function () {
    return getGMValue("qjh_isTaskSkipEnabled", true);
};
var isTaskFinalExaminationEnabled = function () {
    return getGMValue("qjh_isTaskFinalExaminationEnabled", false);
};
var isFullAutomaticEmulationEnabled = function () {
    return getGMValue("qjh_isFullAutomaticEmulationEnabled", false);
};
var autoComplete = function () { return featureNotAvailable("自动完成"); };
var autoCompleteCreditsDone = function () {
    return getGMValue("qjh_autoCompleteCreditsDone", false);
};
var features = [
    {
        key: "courses",
        title: "自动完成所有课程(不包括考试)",
        matcher: ["/courses", "/drugControlClassroom/courses"],
        task: function () { return taskCourses(false); },
        enabled: isTaskCoursesEnabled,
    },
    {
        key: "selfCourse",
        title: "自动完成所有自学课程(不包括考试)",
        matcher: ["/selfCourse", "/drugControlClassroom/selfCourse"],
        task: function () { return taskCourses(true); },
        enabled: isTaskSelfCourseEnabled,
    },
    {
        key: "credit",
        title: "自动获取每日学分(会花费一段时间,请耐心等待)",
        matcher: ["/admin/creditCenter"],
        task: taskGetCredit,
        enabled: isTaskGetCreditEnabled,
    },
    {
        key: "singleCourse",
        title: "单个课程自动完成",
        matcher: /\/courses\/exams\/(\d+)/,
        task: taskSingleCourse,
        enabled: isTaskSingleCourseEnabled,
    },
    {
        key: "finalExamination",
        title: "期末考试",
        matcher: ["/courses/exams/finalExam"],
        task: taskFinalExamination,
        enabled: isTaskFinalExaminationEnabled,
    },
    {
        key: "skip",
        title: "显示课程视频跳过按钮",
        matcher: /\/courses\/(\d+)/,
        task: taskSkip,
        enabled: isTaskSkipEnabled,
    },
];
function triggerFeatures() {
    if (location.pathname === "/") {
        showMessage("".concat(scriptName, "\n版本:").concat(scriptVersion), "green");
    }
    features.forEach(function (feature) {
        var matcher = feature.matcher;
        var isMatched = matcher instanceof RegExp
            ? location.pathname.match(matcher)
            : matcher.indexOf(location.pathname) !== -1;
        if (isMatched && feature.enabled()) {
            showMessage("激活功能:".concat(feature.title), "green");
            feature.task();
        }
    });
}
(function () {
    for (var _i = 0, _a = document.getElementsByTagName("script"); _i < _a.length; _i++) {
        var script = _a[_i];
        if (script.innerText.indexOf("window.__DATA__") !== -1) {
            eval(script.innerText);
        }
    }
    GM_addStyle(GM_getResourceText("toastifycss"));
    GM_addStyle(GM_getResourceText("spectrecss"));
    GM_registerMenuCommand("菜单", showMenu);
    prepareMenu();
    var pathname = location.pathname;
    setInterval(function () {
        var newPathName = location.pathname;
        if (newPathName !== pathname) {
            console.debug("地址改变", pathname, newPathName);
            pathname = newPathName;
            triggerFeatures();
        }
    });
    triggerFeatures();
})();
var customGradeLevels = function () {
    return getGMValue("qjh_customGradeLevels", []);
};
var customSelfGradeLevels = function () {
    return getGMValue("qjh_customSelfGradeLevels", []);
};
function prepareMenu() {
    return __awaiter(this, void 0, void 0, function () {
        var menuElement, coursesGradeLevelsList, selfCoursesGradeLevelsList, titleElement, _loop_1, _i, _a, _b, selector, gradeLevels, customGradeLevelsList, customGradeLevelsListChangeHandler, closeButton, toggleInputs, _loop_2, _c, toggleInputs_1, toggleInput, featButtons, _loop_3, _d, featButtons_1, featButton;
        return __generator(this, function (_e) {
            switch (_e.label) {
                case 0: return [4 /*yield*/, waitForElementLoaded("#qjh-menu")];
                case 1:
                    menuElement = _e.sent();
                    return [4 /*yield*/, coursesGradeLevels()];
                case 2:
                    coursesGradeLevelsList = _e.sent();
                    return [4 /*yield*/, selfCoursesGradeLevels()];
                case 3:
                    selfCoursesGradeLevelsList = _e.sent();
                    if (coursesGradeLevels === null || selfCoursesGradeLevelsList === null) {
                        showMessage("课程年级列表或自学课程年级列表获取失败!", "red");
                    }
                    return [4 /*yield*/, waitForElementLoaded("#qjh-menu-title")];
                case 4:
                    titleElement = _e.sent();
                    titleElement.append(scriptVersion);
                    _loop_1 = function (selector, gradeLevels, customGradeLevelsList, customGradeLevelsListChangeHandler) {
                        var element, _loop_4, _f, gradeLevels_1, gradeLevel;
                        return __generator(this, function (_g) {
                            switch (_g.label) {
                                case 0: return [4 /*yield*/, waitForElementLoaded(selector)];
                                case 1:
                                    element = _g.sent();
                                    if (gradeLevels === null) {
                                        return [2 /*return*/, "continue"];
                                    }
                                    _loop_4 = function (gradeLevel) {
                                        var label = document.createElement("label");
                                        label.className = "form-checkbox form-inline";
                                        var input = document.createElement("input");
                                        input.type = "checkbox";
                                        input.checked =
                                            customGradeLevelsList().indexOf(gradeLevel) !== -1;
                                        input.onchange = function () {
                                            if (input.checked) {
                                                customGradeLevelsListChangeHandler(Array.of.apply(Array, __spreadArray(__spreadArray([], customGradeLevelsList(), false), [gradeLevel], false)));
                                            }
                                            else {
                                                customGradeLevelsListChangeHandler(customGradeLevelsList().filter(function (it) { return it !== gradeLevel; }));
                                            }
                                        };
                                        var i = document.createElement("i");
                                        i.className = "form-icon";
                                        label.appendChild(input);
                                        label.appendChild(i);
                                        label.append(gradeLevel);
                                        element.appendChild(label);
                                    };
                                    for (_f = 0, gradeLevels_1 = gradeLevels; _f < gradeLevels_1.length; _f++) {
                                        gradeLevel = gradeLevels_1[_f];
                                        _loop_4(gradeLevel);
                                    }
                                    return [2 /*return*/];
                            }
                        });
                    };
                    _i = 0, _a = [
                        {
                            selector: "#qjh-menu-feat-courses",
                            gradeLevels: coursesGradeLevelsList,
                            customGradeLevelsList: customGradeLevels,
                            customGradeLevelsListChangeHandler: function (value) {
                                return GM_setValue("qjh_customGradeLevels", value);
                            },
                        },
                        {
                            selector: "#qjh-menu-feat-self-courses",
                            gradeLevels: selfCoursesGradeLevelsList,
                            customGradeLevelsList: customSelfGradeLevels,
                            customGradeLevelsListChangeHandler: function (value) {
                                return GM_setValue("qjh_customSelfGradeLevels", value);
                            },
                        },
                    ];
                    _e.label = 5;
                case 5:
                    if (!(_i < _a.length)) return [3 /*break*/, 8];
                    _b = _a[_i], selector = _b.selector, gradeLevels = _b.gradeLevels, customGradeLevelsList = _b.customGradeLevelsList, customGradeLevelsListChangeHandler = _b.customGradeLevelsListChangeHandler;
                    return [5 /*yield**/, _loop_1(selector, gradeLevels, customGradeLevelsList, customGradeLevelsListChangeHandler)];
                case 6:
                    _e.sent();
                    _e.label = 7;
                case 7:
                    _i++;
                    return [3 /*break*/, 5];
                case 8: return [4 /*yield*/, waitForElementLoaded("#qjh-menu-close-button")];
                case 9:
                    closeButton = _e.sent();
                    closeButton.onclick = function () {
                        menuElement.style.display = "none";
                    };
                    toggleInputs = nodeListToArray(document.querySelectorAll("input")).filter(function (element) { return element.getAttribute("qjh-type") === "toggle"; });
                    _loop_2 = function (toggleInput) {
                        var key = toggleInput.getAttribute("qjh-key");
                        toggleInput.checked = GM_getValue(key);
                        toggleInput.onchange = function () {
                            GM_setValue(key, toggleInput.checked);
                        };
                    };
                    for (_c = 0, toggleInputs_1 = toggleInputs; _c < toggleInputs_1.length; _c++) {
                        toggleInput = toggleInputs_1[_c];
                        _loop_2(toggleInput);
                    }
                    featButtons = nodeListToArray(document.querySelectorAll("button")).filter(function (element) { return element.getAttribute("qjh-feat-key") !== null; });
                    _loop_3 = function (featButton) {
                        var key = featButton.getAttribute("qjh-feat-key");
                        var feature = features.find(function (feature) { return feature.key === key; });
                        featButton.onclick = function () {
                            if (feature.enabled()) {
                                showMessage("手动激活功能:".concat(feature.title), "green");
                                feature.task();
                            }
                            else {
                                showMessage("功能 ".concat(feature.title, " 未被启用!"), "red");
                            }
                        };
                    };
                    for (_d = 0, featButtons_1 = featButtons; _d < featButtons_1.length; _d++) {
                        featButton = featButtons_1[_d];
                        _loop_3(featButton);
                    }
                    return [2 /*return*/];
            }
        });
    });
}
function startCourse(courseId) {
    return __awaiter(this, void 0, void 0, function () {
        var answers, data, response;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, getCourseAnswers(courseId)];
                case 1:
                    answers = _a.sent();
                    if (!(answers === null)) return [3 /*break*/, 2];
                    showMessage("[".concat(courseId, "] 无法获取当前课程的答案!"), "red");
                    return [2 /*return*/, false];
                case 2:
                    console.debug("正在提交课程 [".concat(courseId, "] 答案..."));
                    data = {
                        courseId: courseId,
                        examCommitReqDataList: answers.map(function (answer, index) {
                            return {
                                examId: index + 1,
                                answer: Number(answer) || answer, // 如果是单选,则必须要为数字
                            };
                        }),
                        reqtoken: reqtoken(),
                    };
                    return [4 /*yield*/, commitExam(data)];
                case 3:
                    response = _a.sent();
                    console.debug("提交课程 [".concat(data.courseId, "] 答案"), response);
                    return [2 /*return*/, !isNone(response)];
            }
        });
    });
}
function taskCourses(isSelfCourses) {
    return __awaiter(this, void 0, void 0, function () {
        var gradeLevels, _i, gradeLevels_2, gradeLevel, coursesList, _a, courseIds, committed, _b, courseIds_1, courseId, result;
        return __generator(this, function (_c) {
            switch (_c.label) {
                case 0:
                    if (!isLogined()) {
                        showMessage("你还没有登录!", "red");
                        return [2 /*return*/];
                    }
                    return [4 /*yield*/, (isSelfCourses
                            ? selfCoursesGradeLevels
                            : coursesGradeLevels)()];
                case 1:
                    gradeLevels = _c.sent();
                    if (gradeLevels === null) {
                        showMessage("获取年级名列表失败,功能已中止!", "red");
                        return [2 /*return*/];
                    }
                    console.debug("获取总年级名列表", gradeLevels);
                    gradeLevels = isSelfCourses ? customSelfGradeLevels() : customGradeLevels();
                    console.debug("已选择的年级列表", gradeLevels);
                    _i = 0, gradeLevels_2 = gradeLevels;
                    _c.label = 2;
                case 2:
                    if (!(_i < gradeLevels_2.length)) return [3 /*break*/, 13];
                    gradeLevel = gradeLevels_2[_i];
                    if (!isSelfCourses) return [3 /*break*/, 4];
                    return [4 /*yield*/, getSelfCoursesByGradeLevel(gradeLevel)];
                case 3:
                    _a = _c.sent();
                    return [3 /*break*/, 6];
                case 4: return [4 /*yield*/, getCoursesByGradeLevel(gradeLevel)];
                case 5:
                    _a = _c.sent();
                    _c.label = 6;
                case 6:
                    coursesList = _a;
                    if (coursesList === null) {
                        showMessage("[".concat(gradeLevel, "] 获取当前年级的课程列表失败,已跳过当前年级!"), "red");
                    }
                    courseIds = coursesList
                        .filter(function (it) { return !it.isFinish && it.title !== "期末考试"; })
                        .map(function (it) { return it.courseId; });
                    if (courseIds.length === 0) {
                        console.debug("[".concat(gradeLevel, "] 所有").concat(isSelfCourses ? "自学" : "", "课程都是完成状态,已跳过!"));
                        return [2 /*return*/];
                    }
                    console.debug("[".concat(gradeLevel, "] 未完成的").concat(isSelfCourses ? "自学" : "", "课程"), courseIds);
                    committed = 0;
                    _b = 0, courseIds_1 = courseIds;
                    _c.label = 7;
                case 7:
                    if (!(_b < courseIds_1.length)) return [3 /*break*/, 11];
                    courseId = courseIds_1[_b];
                    if (courseId === "finalExam") {
                        return [2 /*return*/];
                    }
                    if (!!isNone(courseId)) return [3 /*break*/, 9];
                    return [4 /*yield*/, startCourse(courseId)];
                case 8:
                    result = _c.sent();
                    if (result) {
                        committed++;
                    }
                    else {
                        console.error("[".concat(courseId, "] 无法提交当前课程,已跳过!"));
                    }
                    return [3 /*break*/, 10];
                case 9:
                    console.error("[".concat(gradeLevel, "] 无法找到 courseId,已跳过!"));
                    _c.label = 10;
                case 10:
                    _b++;
                    return [3 /*break*/, 7];
                case 11:
                    showMessage("成功完成了 ".concat(committed, " 个").concat(isSelfCourses ? "自学" : "", "课程!"), "green");
                    _c.label = 12;
                case 12:
                    _i++;
                    return [3 /*break*/, 2];
                case 13: return [2 /*return*/];
            }
        });
    });
}
function taskSingleCourse() {
    return __awaiter(this, void 0, void 0, function () {
        var courseId, answers;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    if (!isLogined()) {
                        showMessage("你还没有登录!", "red");
                        return [2 /*return*/];
                    }
                    courseId = location.pathname.match(/(\d+)/g)[0];
                    return [4 /*yield*/, getCourseAnswers(courseId)];
                case 1:
                    answers = _a.sent();
                    return [4 /*yield*/, emulateExamination(answers, "#app > div > div.home-container > div > div > div > div > div > button", "#app > div > div.home-container > div > div > div > div > div > div.exam-content-btnbox > button", "#app > div > div.home-container > div > div > div > div > div > div.exam-content-btnbox > div > button.ant-btn-primary", function (answers, _) {
                            var firstAnswer = answers.shift().toString();
                            return {
                                answer: firstAnswer,
                                matchedQuestion: null,
                            };
                        }, "答题 [".concat(courseId, "]"), answers.length)];
                case 2:
                    _a.sent();
                    return [2 /*return*/];
            }
        });
    });
}
function emulateExamination(answers, startButtonSelector, primaryNextButtonSelector, secondaryNextButtonSelector, answerHandler, examinationName, size) {
    if (size === void 0) { size = 100; }
    return __awaiter(this, void 0, void 0, function () {
        var isExaminationStarted, count, next, startButton;
        var _this = this;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    isExaminationStarted = false;
                    count = 0;
                    next = function (nextAnswers, nextButton) {
                        if (nextButton === void 0) { nextButton = null; }
                        return __awaiter(_this, void 0, void 0, function () {
                            var questionElement, questionText, _firstNextButton, _a, answer, matchedQuestion, selections, displayAnswer, finalQuestion, _i, _b, answerIndex, selectionElement;
                            return __generator(this, function (_c) {
                                switch (_c.label) {
                                    case 0: return [4 /*yield*/, waitForElementLoaded(".exam-content-question")];
                                    case 1:
                                        questionElement = _c.sent();
                                        questionText = removeSpaces(questionElement.innerText.split("\n")[0] // 获取第一行(题目都是在第一行)
                                        );
                                        if (!!isExaminationStarted) return [3 /*break*/, 4];
                                        return [4 /*yield*/, waitForElementLoaded(primaryNextButtonSelector)];
                                    case 2:
                                        _firstNextButton = _c.sent();
                                        isExaminationStarted = true;
                                        return [4 /*yield*/, next(nextAnswers, _firstNextButton)];
                                    case 3:
                                        _c.sent();
                                        return [3 /*break*/, 5];
                                    case 4:
                                        if (count > 0) {
                                            nextButton = document.querySelector(secondaryNextButtonSelector);
                                        }
                                        if (!isNone(size) && count < size) {
                                            nextButton.onclick = function () {
                                                setTimeout(function () { return next(nextAnswers, nextButton); }, 200);
                                                return;
                                            };
                                            _a = answerHandler(answers, questionText), answer = _a.answer, matchedQuestion = _a.matchedQuestion;
                                            selections = document.getElementsByClassName("exam-single-content-box");
                                            console.debug("选择", answer, selections);
                                            displayAnswer = toDisplayAnswer(answer);
                                            finalQuestion = matchedQuestion || questionText;
                                            if (!isFullAutomaticEmulationEnabled()) {
                                                showMessage("".concat(finalQuestion ? finalQuestion + "\n" : "", "第 ").concat(count + 1, " 题答案:").concat(displayAnswer), "green");
                                            }
                                            for (_i = 0, _b = answer.split(",").map(function (it) { return Number(it); }); _i < _b.length; _i++) {
                                                answerIndex = _b[_i];
                                                selectionElement = selections[answerIndex];
                                                selectionElement.click();
                                            }
                                            if (isFullAutomaticEmulationEnabled()) {
                                                nextButton.click();
                                            }
                                            count++;
                                        }
                                        _c.label = 5;
                                    case 5: return [2 /*return*/];
                                }
                            });
                        });
                    };
                    return [4 /*yield*/, waitForElementLoaded(startButtonSelector)];
                case 1:
                    startButton = _a.sent();
                    startButton.onclick = function () {
                        showMessage("开始 ".concat(examinationName, "!"), "blue");
                        next(answers, null);
                    };
                    return [2 /*return*/];
            }
        });
    });
}
function taskSkip() {
    return __awaiter(this, void 0, void 0, function () {
        var courseId, span, skipButton, skipSpan;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    if (!isLogined()) {
                        showMessage("你还没有登录!", "red");
                        return [2 /*return*/];
                    }
                    courseId = location.pathname.match(/(\d+)/g)[0];
                    return [4 /*yield*/, waitForElementLoaded("#app > div > div.home-container > div > div > div.course-title-box > div > a > span")];
                case 1:
                    span = _a.sent();
                    span.style.display = "inline-flex";
                    skipButton = document.createElement("button");
                    skipButton.type = "button";
                    skipButton.className = "ant-btn ant-btn-danger ant-btn-lg";
                    skipSpan = document.createElement("span");
                    skipSpan.innerText = "跳过";
                    skipButton.appendChild(skipSpan);
                    skipButton.onclick = function () {
                        location.href = "/courses/exams/".concat(courseId);
                    };
                    span.appendChild(skipButton);
                    return [2 /*return*/];
            }
        });
    });
}
function taskGetCredit() {
    return __awaiter(this, void 0, void 0, function () {
        var num, categories, done, failed, liked, _i, categories_1, category, data, resources, _a, resources_1, resource, resourceId, resourceData, result, likeResult, beforeDone, checkSuccess;
        return __generator(this, function (_b) {
            switch (_b.label) {
                case 0:
                    if (!isLogined()) {
                        showMessage("你还没有登录!", "red");
                        return [2 /*return*/];
                    }
                    return [4 /*yield*/, addMedal()];
                case 1:
                    num = _b.sent();
                    if (num !== undefined) {
                        showMessage("成功领取禁毒徽章 [".concat(num, "]!"), "green");
                    }
                    else if (num === null) {
                        showMessage("领取徽章失败!", "red");
                    }
                    else {
                        console.warn("无法领取徽章(可能已领取过),已跳过!");
                    }
                    categories = [
                        { name: "public_good", tag: "read" },
                        { name: "ma_yun_recommend", tag: "labour" },
                        { name: "ma_yun_recommend", tag: "movie" },
                        { name: "ma_yun_recommend", tag: "music" },
                        { name: "ma_yun_recommend", tag: "physicalEducation" },
                        { name: "ma_yun_recommend", tag: "arts" },
                        { name: "ma_yun_recommend", tag: "natural" },
                        { name: "ma_yun_recommend", tag: "publicWelfareFoundation" },
                        { name: "school_safe", tag: "safeVolunteer" },
                    ];
                    done = 0;
                    failed = 0;
                    liked = 0;
                    _i = 0, categories_1 = categories;
                    _b.label = 2;
                case 2:
                    if (!(_i < categories_1.length)) return [3 /*break*/, 9];
                    category = categories_1[_i];
                    data = {
                        categoryName: category.name,
                        pageNo: 1,
                        pageSize: 100,
                        reqtoken: reqtoken(),
                        tag: category.tag,
                    };
                    return [4 /*yield*/, getBeforeResourcesByCategoryName(data)];
                case 3:
                    resources = _b.sent();
                    if (resources === null) {
                        console.error("无法获取分类 ".concat(category.name, " 的资源,已跳过!"));
                        return [3 /*break*/, 8];
                    }
                    console.debug("获取分类 ".concat(category.name, " 的资源"), resources);
                    _a = 0, resources_1 = resources;
                    _b.label = 4;
                case 4:
                    if (!(_a < resources_1.length)) return [3 /*break*/, 8];
                    resource = resources_1[_a];
                    resourceId = resource.resourceId;
                    resourceData = { resourceId: resourceId, reqtoken: reqtoken() };
                    return [4 /*yield*/, addPCPlayPV(resourceData)];
                case 5:
                    result = _b.sent();
                    if (result) {
                        console.debug("成功完成资源 [".concat(resourceId, "]:").concat(resource.title));
                        done++;
                    }
                    else {
                        console.error("无法完成资源 ".concat(resourceId, ",已跳过!"));
                        failed++;
                    }
                    return [4 /*yield*/, likePC(resourceData)];
                case 6:
                    likeResult = _b.sent();
                    if (likeResult) {
                        console.debug("成功点赞资源 [".concat(resourceId, "]!"));
                        liked++;
                    }
                    else {
                        console.error("资源点赞失败 [".concat(resourceId, "],已跳过!"));
                    }
                    _b.label = 7;
                case 7:
                    _a++;
                    return [3 /*break*/, 4];
                case 8:
                    _i++;
                    return [3 /*break*/, 2];
                case 9:
                    beforeDone = done;
                    checkSuccess = setInterval(function () {
                        if (done !== 0) {
                            if (done === beforeDone) {
                                showMessage("成功完成 ".concat(done, "/").concat(failed, " 个资源,点赞 ").concat(liked, " 个!"), "green");
                                clearInterval(checkSuccess);
                            }
                            else {
                                beforeDone = done;
                            }
                        }
                    }, 500);
                    return [2 /*return*/];
            }
        });
    });
}
function taskFinalExamination() {
    return __awaiter(this, void 0, void 0, function () {
        var supportedFinal, gradeLevel, paperName, papers_1;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    supportedFinal = libs.supportedFinal;
                    gradeLevel = accountGradeLevel();
                    if (!supportedFinal.hasOwnProperty(gradeLevel)) return [3 /*break*/, 2];
                    paperName = supportedFinal[gradeLevel];
                    papers_1 = libs[paperName];
                    papers_1 = papers_1.map(function (it) {
                        return { question: it.question, answer: toAnswer(it.answer) };
                    });
                    return [4 /*yield*/, emulateExamination(papers_1.map(function (it) { return it.answer; }), "#app > div > div.home-container > div > div > div > div > div > button", "#app > div > div.home-container > div > div > div > div > div > div.exam-content-btnbox > button", "#app > div > div.home-container > div > div > div > div > div > div.exam-content-btnbox > div > button.ant-btn.ant-btn-primary", function (_, question) {
                            var _a = accurateFind(papers_1, question) || fuzzyFind(papers_1, question), answer = _a.answer, realQuestion = _a.realQuestion;
                            return {
                                answer: answer,
                                matchedQuestion: realQuestion,
                            };
                        }, "期末考试", 10 // TODO 这个 10 是干什么的我还没搞清楚,之后再说
                        )];
                case 1:
                    _a.sent();
                    return [3 /*break*/, 3];
                case 2:
                    showMessage("你的年级 [".concat(gradeLevel, "] 暂未支持期末考试!"), "red");
                    return [2 /*return*/];
                case 3: return [2 /*return*/];
            }
        });
    });
}
function showMessage(text, color) {
    Toastify({
        text: text,
        duration: toastifyDuration,
        newWindow: true,
        gravity: toastifyGravity,
        position: toastifyPosition,
        stopOnFocus: true,
        style: { background: color },
    }).showToast();
}
function featureNotAvailable(name) {
    if (name === void 0) { name = "(未知)"; }
    showMessage("".concat(name, " 功能当前不可用,请尝试刷新页面。如果问题依旧请上报这个 bug!"), "red");
}
function isNone(obj) {
    return obj == undefined || obj == null;
}
function getGMValue(name, defaultValue) {
    var value = GM_getValue(name);
    if (isNone(value)) {
        value = defaultValue;
        GM_setValue(name, defaultValue);
    }
    return value;
}
function waitForElementLoaded(querySelector) {
    return __awaiter(this, void 0, void 0, function () {
        return __generator(this, function (_a) {
            return [2 /*return*/, new Promise(function (resolve, reject) {
                    var attempts = 0;
                    var tryFind = function () {
                        var element = document.querySelector(querySelector);
                        if (element) {
                            resolve(element);
                        }
                        else {
                            attempts++;
                            if (attempts >= 30) {
                                console.error("无法找到元素 [".concat(querySelector, "],已放弃!"));
                                reject();
                            }
                            else {
                                setTimeout(tryFind, 250 * Math.pow(1.1, attempts));
                            }
                        }
                    };
                    tryFind();
                })];
        });
    });
}
function removeSpaces(string) {
    return string.replace(/\s*/g, "");
}
function toDisplayAnswer(answer) {
    var alphas = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
    var result = "";
    for (var _i = 0, _a = answer.split(","); _i < _a.length; _i++) {
        var singleAnswer = _a[_i];
        var index = Number(singleAnswer);
        result = result + alphas[index];
    }
    return result;
}
function toAnswer(answers) {
    var alphas = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
    var result = "";
    for (var _i = 0, answers_1 = answers; _i < answers_1.length; _i++) {
        var answer = answers_1[_i];
        result = result + alphas.indexOf(answer);
    }
    return result;
}
function nodeListToArray(nodeList) {
    return Array.prototype.slice.call(nodeList);
}
function converToGenericGradeLevel(gradeLevel) {
    var mapping = {
        七年级: "初一",
        八年级: "初二",
        九年级: "初三",
    };
    return mapping[gradeLevel];
}
function arrayDiff(array1, array2) {
    return array1.concat(array2).filter(function (v, _, array) {
        return array.indexOf(v) === array.lastIndexOf(v);
    });
}
function accurateFind(papers, question) {
    var result = papers.find(function (it) { return removeSpaces(it.question) === question; });
    if (!isNone(result)) {
        console.debug("精确匹配问题:".concat(question, " → ").concat(result.question));
        return { answer: result.answer, realQuestion: question };
    }
    else {
        return null;
    }
}
function fuzzyFind(papers, question) {
    var chars = question.split("");
    var length = chars.length;
    var percentages = [];
    for (var _i = 0, papers_2 = papers; _i < papers_2.length; _i++) {
        var paper = papers_2[_i];
        var questionChars = paper.question.split("");
        var diff = arrayDiff(chars, questionChars);
        var diffLength = diff.length;
        var percentage = diffLength / length;
        percentages.push({
            question: paper.question,
            answer: paper.answer,
            unconfidence: percentage,
        });
    }
    var theMostConfident = percentages.sort(function (a, b) { return a.unconfidence - b.unconfidence; })[0];
    var theMostConfidentQuestion = theMostConfident.question;
    var confidence = 1 - theMostConfident.unconfidence;
    console.debug("模糊匹配问题(".concat(confidence, "):").concat(question, " → ").concat(theMostConfidentQuestion));
    return {
        answer: theMostConfident.answer,
        realQuestion: theMostConfidentQuestion,
    };
}
var container = document.createElement("div");
container.setAttribute("id", "qjh-menu");
container.innerHTML = "<style>\n  .qjh-menu {\n    height: max-content;\n    box-shadow: 1px 1px 10px #909090;\n    padding: 1em;\n    position: fixed;\n    z-index: 999;\n    right: 1%;\n    top: 3%;\n    width: 25%;\n    -webkit-border-radius: 10px;\n    -moz-border-radius: 10px;\n    border-radius: 10px;\n  }\n\n  .form-inline {\n    display: inline-block;\n  }\n</style>\n\n<div class=\"card container qjh-menu\">\n  <div class=\"card-header\">\n    <div class=\"card-title text-bold h5\" id=\"qjh-menu-title\">\n      QingJiaoHelper\n      <button\n        class=\"btn btn-link float-right\"\n        type=\"button\"\n        id=\"qjh-menu-close-button\"\n      >\n        ❌\n      </button>\n    </div>\n  </div>\n\n  <div class=\"card-body\">\n    <div class=\"toast toast-warning\">\n      ⚠注意:勾选的功能会在下一次刷新页面时<mark><b>自动激活</b></mark\n      >,未勾选的功能只能手动启用!点击<b>一键完成</b>按钮可以在这个菜单中直接完成,而不用手动跳转到对应页面。\n    </div>\n\n    <div class=\"divider text-center\" data-content=\"功能\"></div>\n\n    <div>\n      <div class=\"form-group\" id=\"qjh-menu-feat-courses\">\n        <label class=\"form-switch\">\n          <b>完成所选年级的课程</b>\n          <input\n            type=\"checkbox\"\n            qjh-type=\"toggle\"\n            qjh-key=\"qjh_isTaskCoursesEnabled\"\n          />\n          <i class=\"form-icon\"></i>\n          <button class=\"btn btn-sm mx-2\" type=\"button\" qjh-feat-key=\"courses\">\n            一键完成👉\n          </button>\n        </label>\n      </div>\n\n      <div class=\"form-group\" id=\"qjh-menu-feat-self-courses\">\n        <label class=\"form-switch\">\n          <b>完成所选年级的自学课程</b>\n          <input\n            type=\"checkbox\"\n            qjh-type=\"toggle\"\n            qjh-key=\"qjh_isTaskSelfCourseEnabled\"\n          />\n          <i class=\"form-icon\"></i>\n          <button\n            class=\"btn btn-sm mx-2\"\n            type=\"button\"\n            qjh-feat-key=\"selfCourse\"\n          >\n            一键完成👉\n          </button>\n        </label>\n      </div>\n\n      <div class=\"form-group\">\n        <label class=\"form-switch\">\n          <b>获取每日学分</b>\n          <input\n            type=\"checkbox\"\n            qjh-type=\"toggle\"\n            qjh-key=\"qjh_isTaskGetCreditEnabled\"\n          />\n          <i class=\"form-icon\"></i>\n          <button\n            class=\"btn btn-sm mx-2\"\n            type=\"button\"\n            onclick=\"taskGetCredit\"\n            qjh-feat-key=\"credit\"\n          >\n            一键完成👉\n          </button>\n        </label>\n      </div>\n\n      <div class=\"form-group\">\n        <label class=\"form-switch\">\n          <b>期末考试(推荐和<mark>自动下一题并提交</mark>功能一起使用)</b>\n          <input\n            type=\"checkbox\"\n            qjh-type=\"toggle\"\n            qjh-key=\"qjh_isTaskFinalExaminationEnabled\"\n          />\n          <i class=\"form-icon\"></i>\n        </label>\n      </div>\n\n      <div class=\"form-group\">\n        <label class=\"form-switch\">\n          <b>课程自动填充答案</b>\n          <input\n            type=\"checkbox\"\n            qjh-type=\"toggle\"\n            qjh-key=\"qjh_isTaskSingleCourseEnabled\"\n          />\n          <i class=\"form-icon\"></i>\n        </label>\n      </div>\n\n      <div class=\"form-group\">\n        <label class=\"form-switch\">\n          <b>自动下一题并提交(可以和<mark>课程自动填充答案</mark>配合使用)</b>\n          <input\n            type=\"checkbox\"\n            qjh-type=\"toggle\"\n            qjh-key=\"qjh_isFullAutomaticEmulationEnabled\"\n          />\n          <i class=\"form-icon\"></i>\n        </label>\n      </div>\n\n      <div class=\"form-group\">\n        <label class=\"form-switch\">\n          <b>显示课程视频跳过按钮</b>\n          <input\n            type=\"checkbox\"\n            qjh-type=\"toggle\"\n            qjh-key=\"qjh_isTaskSkipEnabled\"\n          />\n          <i class=\"form-icon\"></i>\n        </label>\n      </div>\n\n      <div class=\"divider\"></div>\n    </div>\n  </div>\n  <div class=\"card-footer text-gray\">\n    本脚本由 WindLeaf233 以\n    <b><a href=\"https://www.gnu.org/licenses/gpl-3.0.en.html\">GPL-3.0</a></b>\n    开源许可在 GitHub 开源,脚本地址:<a\n      href=\"https://github.com/WindLeaf233/QingJiaoHelper\"\n      target=\"_blank\"\n      >GitHub</a\n    >、<a\n      href=\"http://greasyfork.icu/zh-CN/scripts/452984-qingjiaohelper\"\n      target=\"_blank\"\n      >GreasyFork</a\n    >。\n  </div>\n</div>\n";
container.style.display = "none";
document.body.appendChild(container);
function showMenu() {
    container.style.display = "unset";
}