超星网课挂科模式

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

// ==UserScript==
// @name         超星网课挂科模式
// @namespace    https://userscript.snomiao.com
// @version      0.2
// @description  自动签到,自动统计未完成作业、未完成任务等、提前解锁章节,查看章节统计。我是热爱学习的好孩子,(配合超星网课助手可自动观看课程、自动完成作业等)
// @author       snomiao@gmail.com
// @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);