Greasy Fork

超星网课挂科模式

自动签到,自动统计未完成作业、未完成任务等、提前解锁章节,查看章节统计。我是热爱学习的好孩子,(配合超星网课助手可自动观看课程、自动完成作业等)

目前为 2022-01-27 提交的版本。查看 最新版本

// ==UserScript==
// @name         超星网课挂科模式
// @namespace    https://userscript.snomiao.com
// @version      0.2
// @description  自动签到,自动统计未完成作业、未完成任务等、提前解锁章节,查看章节统计。我是热爱学习的好孩子,(配合超星网课助手可自动观看课程、自动完成作业等)
// @author       [email protected]
// @match        *://*.chaoxing.com/*
// @match        *://mobilelearn.chaoxing.com/*
// @match        *://mooc1-1.chaoxing.com/*
// @match        *://i.mooc.chaoxing.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addValueChangeListener
// ==/UserScript==

// 课程列表
// http://mooc1-1.chaoxing.com/visit/interaction
//
//
// 开发中
//
///////////////////////////////
// 防多开
// var genId = '' + Math.random()

///////////////////////////////
// 异步组件
var 睡 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
var 异步防抖函数 = (异步函数, 间隔时间 = 1000) => {
    var 上次执行时间 = null;
    return async (...参列) => {
        if (上次执行时间 != null && 上次执行时间 + 间隔时间 >= +new Date())
            return null;
        上次执行时间 = +new Date();
        return await 异步函数(参列);
    };
};

// 对象操作
var 深合并 = (t, s) =>
    (t instanceof Object &&
        ([...Object.keys(s)].forEach((k) => (t[k] = 深合并(t[k], s[k]))), t)) ||
    (undefined === s && t) ||
    s;
var 键值对列转对象 = (键值对列) =>
    键值对列.reduce((a, b) => ((a[b[0]] = b[1]), a), {});
var 全部提取 = (str, reg) => {
    if (!reg.global) return null;
    var coll = [];
    while (((e) => e && coll.push(e))(reg.exec(str)));
    return coll;
};
var 提取为表 = (html, 正则表达式, 键值对函数) => ({
    ...键值对列转对象(全部提取(html, 正则表达式).map(键值对函数)),
});

///////////////////////////////
// FETCH
var GETHTML = async (url) => await (await fetch(url)).text();

// 存储
var 加载状态 = () => {
    try {
        var r = JSON.parse(GM_getValue('超星网课挂科模式'));
    } catch (e) {}
    console.log('加载状态', r);
    return r || {};
};
var 保存状态 = (json = {}) => {
    GM_setValue('超星网课挂科模式', JSON.stringify(json));
};
var 更新状态 = (函数) => 保存状态(函数(加载状态()));
var 合并状态 = (函数) => {
    var 状态 = 加载状态();
    保存状态(深合并(状态, 函数(状态)));
};

///////////////////////////////
// 更新状态

// 课程列表
var 取课程表地址 = () => `http://mooc1-1.chaoxing.com/visit/interaction`;
var 更新课程表 = (html) =>
    合并状态((状态) => ({
        课程表: 提取为表(
            html,
            /<a .*?courseId=(\d+).*?clazzid=(\d+).*?title="(.*?)"[\s\S]*?<p title="(.*?)">/g,
            ([_, courseId, clazzid, title, 教师]) => [
                clazzid,
                { courseId, classId: clazzid, title, 教师 },
            ]
        ),
    }));

// 作业列表
var 取作业表地址 = ({
    classId,
    courseId,
    isdisplaytable,
    mooc,
    ut,
    enc,
    cpi,
    openc,
}) =>
    `https://mooc1-1.chaoxing.com/work/getAllWork?classId=${classId}&courseId=${courseId}&isdisplaytable=${isdisplaytable}&mooc=${mooc}&ut=${ut}&enc=${enc}&cpi=${cpi}&openc=${openc}`;
var 更新作业表 = (html) =>
    合并状态((状态) => ({
        课程表: 提取为表(html, /id="cid" value="(\d+)"/g, ([_, classId]) => [
            classId,
            { t检查作业表: +new Date() },
        ]),
        作业表: 提取为表(
            html,
            /<a.*?href="(.*?courseId=(\d+).*?classId=(\d+).*?workId=(\d+)(?:.*?workAnswerId=(\d+))?.*?)".*?title="(.*?)"[\s\S]*?开始时间.*>(.*?)<[\s\S]*?截止时间.*?>(.*?)<[\s\S]*?作业状态[\s\S]*?<strong>\s*(.*?)\s*?</g,
            ([
                _,
                url,
                courseId,
                classId,
                workId,
                workAnswerId,
                title,
                开始时间,
                截止时间,
                作业状态,
            ]) => [
                workId,
                {
                    url,
                    courseId,
                    classId,
                    workId,
                    workAnswerId,
                    title,
                    开始时间,
                    截止时间,
                    作业状态,
                },
            ]
        ),
    }));
// 签到列表
var 取签到表地址 = ({ courseId, classId }) =>
    `https://mobilelearn.chaoxing.com/widget/pcpick/stu/index?courseId=${courseId}&jclassId=${classId}`;
var 更新签到表 = (html) => {
    var 课程表 = 提取为表(
        html,
        /id="puid" value="(.*?)"[\s\S]*?id="courseId" value="(.*?)"[\s\S]*?id="classId" value="(.*?)"[\s\S]*?id="fid" value="(.*?)"[\s\S]*?id="activeId" value="(.*?)"[\s\S]*?id="appType" value="(.*?)"/g,
        ([_, puid, courseId, classId, fid, activeId, appType]) => [
            classId,
            {
                puid,
                courseId,
                classId,
                fid,
                activeId,
                appType,
                t更新签到表: +new Date(),
            },
        ]
    );
    var { puid, courseId, classId, fid, activeId, appType } =
        Object.values(课程表)[0];
    合并状态((状态) => ({
        课程表,
        签到表: 提取为表(
            html,
            /activeDetail\((\d+),2,null\)[\s\S]*?<dd class="green">(.*?)<[\s\S]*?<a.*?>(.*?)</g,
            ([_, activeId, type, title]) => [
                activeId,
                {
                    activeId,
                    type,
                    title,
                    puid,
                    courseId,
                    classId,
                    fid,
                    activeId,
                    appType,
                },
            ]
        ),
    }));
};

// 更新签到状态
// 普通签到
var 取普通签到地址 = ({ activeId, classId, fid = '', courseId }) =>
    `https://mobilelearn.chaoxing.com/widget/sign/pcStuSignController/preSign?activeId=${activeId}&classId=${classId}&fid=${fid}&courseId=${courseId}`;
// 手势签到
var 取手势签到地址 = ({ courseId, classId, activeId }) =>
    `https://mobilelearn.chaoxing.com/widget/sign/pcStuSignController/signIn?&courseId=${courseId}&classId=${classId}&activeId=${activeId}`;
var 更新签到状态 = (html) =>
    合并状态((状态) => ({
        签到表: 提取为表(
            html,
            /id="activeId".*?"(.*?)"[\s\S]*?id="activeStatus".*?"(.*?)"[\s\S]*?id="courseId".*?"(.*?)"[\s\S]*?id="classId".*?"(.*?)"[\s\S]*?id="puid".*?"(.*?)"[\s\S]*?id="createUid".*?"(.*?)"[\s\S]*?id="fid".*?"(.*?)"[\s\S]*?<span.*?>(.*?)<\/span><em id="st">(.*?)</g,
            ([
                _,
                activeId,
                activeStatus,
                courseId,
                classId,
                puid,
                createUid,
                fid,
                statusText,
                succTimeRaw,
            ]) => [
                activeId,
                {
                    activeId,
                    activeStatus,
                    courseId,
                    classId,
                    puid,
                    createUid,
                    fid,
                    statusText,
                    succTimeRaw,
                    t签到成功: +new Date(),
                },
            ]
        ),
    }));
// 二维码、位置、拍照签到
// https://mobilelearn.chaoxing.com/pptSign/stuSignajax
var 更新签到状态2 = (html, href) =>
    合并状态((状态) => ({
        签到表: 提取为表(
            html + '~' + href,
            /(.*?)~.*?activeId=(\d+)/g,
            ([_, status, activeId]) => [
                activeId,
                { activeId, status, t签到成功: +new Date() },
            ]
        ),
    }));

// 更新课程状态
var 取课程首页地址 = () =>
    `https://mooc1-1.chaoxing.com/mycourse/studentcourse?courseId=${courseId}&clazzid=${clazzid}&vc=${vc}&cpi=${cpi}&enc=${enc}`;
var 更新课程首页 = (html) =>
    合并状态((状态) => ({
        课程表: 提取为表(
            html,
            /id="cid" value="(\d+)"[\s\S]*?(?:(待完成任务点))?/g,
            ([_, activeId, 待完成任务点]) => [
                activeId,
                { 待完成状态: !!待完成任务点, t完成状态: +new Date() },
            ]
        ),
    }));

var 更新当前页面状态 = () => {
    if (location.pathname == '/visit/interaction')
        更新课程表(document.body.innerHTML, location.href);
    if (location.pathname == '/mycourse/studentcourse')
        更新课程首页(document.body.innerHTML, location.href);
    //
    if (location.pathname == '/work/getAllWork')
        更新作业表(document.body.innerHTML, location.href);
    //
    if (location.pathname == '/widget/pcpick/stu/index')
        更新签到表(document.body.innerHTML, location.href);
    if (location.pathname == '/widget/sign/pcStuSignController/preSign')
        更新签到状态(document.body.innerHTML, location.href);
    if (location.pathname == '/widget/sign/pcStuSignController/signIn')
        更新签到状态(document.body.innerHTML, location.href);
    if (location.pathname == '/pptSign/stuSignajax')
        更新签到状态2(document.body.innerHTML, location.href);
};

var 自动签到 = async () => {
    var 状态 = 加载状态();
    // 4分半钟检查一次签到
    var 未知签到课程列 = [...Object.values(状态.课程表 || {})].filter(
        (课程) =>
            (课程.t检查签到表 || 课程.t更新签到表 || 0) + 270e3 <= +new Date()
    );
    合并状态(() => ({
        课程表: 键值对列转对象(
            未知签到课程列.map(({ classId }) => [
                classId,
                { t检查签到表: +new Date() },
            ])
        ),
    }));
    var 课程;
    while ((课程 = 未知签到课程列.shift())) {
        const 地址 = 取签到表地址(课程);
        console.debug('正在检查签到列表', 课程, 地址);
        更新签到表(await GETHTML(地址));
        await 睡(3e3);
    }

    // 5 秒尝试一次签到
    var 未完成签到列 = [...Object.values(状态.签到表 || {})]
        .filter((签到) => (签到.t尝试签到 || 0) + 5e3 <= +new Date())
        .filter(
            (签到) => !(签到.succTime || 签到.succTimeRaw || 签到.t签到成功)
        );
    合并状态(() => ({
        签到表: 键值对列转对象(
            未完成签到列.map(({ activeId }) => [
                activeId,
                { t尝试签到: +new Date() },
            ])
        ),
    }));
    var 签到;
    while ((签到 = 未完成签到列.shift())) {
        let 地址;
        if (签到.title.match('手势')) {
            地址 = 取手势签到地址(签到);
        } else {
            地址 = 取普通签到地址(签到);
        }
        console.debug('正在签到', 签到, 地址);
        // window.open(地址 + "#自动关闭")
        更新签到状态(await GETHTML(地址));
        await 睡(3e3);
    }
};
///////////////////////////////////////////////////////////////

var main = async () => {
    更新当前页面状态();
    var 状态 = 加载状态();

    var 未完成作业 = [...Object.values(状态.作业表)].filter(
        (e) => e.作业状态 != '已完成'
    );

    if (
        location.hostname == 'mobilelearn.chaoxing.com' &&
        decodeURI(location.hash) == '#巡逻自动签到'
    ) {
        console.log('[超星网课挂科模式] 启动巡逻自动签到');
        var 间隔自动签到 = 异步防抖函数(自动签到, 270e3); // 最快四分半钟检查一次签到
        setInterval(async () => {
            document.body.innerHTML =
                new Date(+new Date()) + '#自动签到运行中...';
            if (null !== (await 间隔自动签到())) {
                console.log(new Date(), '4分半钟后重新运行');
            }
        }, 1e3);
    }
    if (decodeURI(location.hash) == '#自动签到') {
        var iframe = document.createElement('iframe');
        iframe.src = 'https://mobilelearn.chaoxing.com/';
        document.body.appendChild(a);
    }
    if (decodeURI(location.hash) == '#自动巡逻') {
        console.log('3分钟后重新运行');
        setTimeout(main, 180e3);
    }
    if (decodeURI(location.hash) == '#自动关闭') {
        window.close();
    }
};

window.addEventListener('load', main);