wsxy_Learn

网上学院函数库:自动报名、听课、考试等

Tính đến 05-02-2020. Xem phiên bản mới nhất.

Script này sẽ không được không được cài đặt trực tiếp. Nó là một thư viện cho các script khác để bao gồm các chỉ thị meta // @require https://update.greatest.deepsurf.us/scripts/396002/770309/wsxy_Learn.js

// ==UserScript==
// @name          wsxy_Learn
// @namespace     Vionlentmonkey
// @version       0.8
// @description   网上学院函数库:自动报名、听课、考试等
// ==/UserScript==

// 自动报名高学分课程。2020 年初,高于 1 学分的有且仅有 20 门 3 学分课程。
const autoSignupMaxCredit = async () => {
  // 需要 iframe 提升才会执行
  if (window.top !== window.self) {
  }
  let courses = document.querySelectorAll('#requiredCourseTable .course');
  const waitCourseInfo = JSON.parse(localStorage.getItem('waitCourseInfo'));
  for (let w of waitCourseInfo) {
    // 学分高且未报名。
    // 取消报名的也有 apply_pk,但没有进度点数 jdpoint 。
    if (w.courseCredit > 1 && !w.jdpoint) {
      console.log(w.course_name);
      for (let c of courses) {
        let coursePk = Number(c.getElementsByClassName('coursePk')[0].textContent);
        // 不能作为判断依据。
        if (coursePk === w.course_pk) {
          c.click();
          const btn = document.getElementsByClassName('layui-layer-btn0');
          if (btn.length === 1) {
            btn[0].click();
          }
        }
      }
    }
  }
};

// 自动报名高学时课程
const autoSignupMaxTime = async () => {
  // 需要 iframe 提升才会执行
  if (window.top !== window.self) {
  }
  // 存储所有未报名课程的课时和对应编号
  let timesMap = new Map();
  const waitCourseInfo = JSON.parse(localStorage.getItem('waitCourseInfo'));
  for (let w of waitCourseInfo) {
    // 报名后等于零,undefined 代表未报名
    if (w.jdpoint === undefined) {
      timesMap.set(w.course_pk, w.courseTime);
    }
  }
  let timesArray = [...timesMap.values()];
  let longest = Math.max(...timesArray);
  console.log(longest);
  let maxTimeCourse_pk = 0;
  for (let m of timesMap) {
    if (m[1] === longest) {
      maxTimeCourse_pk = m[0];
    }
  }
  //console.log(maxTimeCourse_pk);
  let courses = document.querySelectorAll('#requiredCourseTable .course');
  for (let c of courses) {
    let coursePk = Number(c.getElementsByClassName('coursePk')[0].textContent);
    // 不能作为判断依据。
    if (coursePk === maxTimeCourse_pk) {
      c.click();
      const btn = document.getElementsByClassName('layui-layer-btn0');
      if (btn.length === 1) {
        btn[0].click();
      }
    }
  }
};

const autoOpenExam = async exams => {
  for await (let exam of exams) {
    const examURL = location.origin + '/sfxzwsxy/' + exam.getAttribute('onclick').split("'")[1];
    // 魔法打开的考卷确认交卷后不能自动关闭,只得如此暴力处理,1分钟后强行关闭。
    let autoExam = GM_openInTab(examURL, true);
    setTimeout(autoExam.close, 60000);
  }
};

const autoOpenTrain = async (isNewPlayer, applyPk) => {
  if (isNewPlayer) {
    console.log(`跳过 applyPk: ${applyPk}`);
  } else {
    const trainURL =
      location.origin +
      '/sfxzwsxy/jypxks/modules/train/ware/course_ware_view.jsp?applyPk=' +
      applyPk +
      '&courseType=1';
    const openClose = GM_openInTab(trainURL, true);
    // 25分钟自动关闭
    setTimeout(openClose.close, 1500000);
  }
};

const autoLearn = async exams => {
  if (window.top !== window.self) {
  }
  // 30 分钟刷新一次
  setInterval(() => {
    location.reload(true);
  }, 1800000);
  const total_hour = Number(localStorage.getItem('total_hour')); //规定需达到的总学时
  const required_hour = Number(localStorage.getItem('required_hour')); //规定需达到的必修学时
  const required_credit = Number(localStorage.getItem('required_credit')); //规定需达到的总学分
  const user_total_hour = Number(localStorage.getItem('user_total_hour')); //用户已获得的总学时
  const user_required_hour = Number(localStorage.getItem('user_required_hour')); //用户已获得的必修学时
  const user_required_credit = Number(localStorage.getItem('user_required_credit')); //用户已获得的总学分

  console.log(`已获得:必修学时:${user_required_hour},学分:${user_required_credit}`);
  // 判断是否已完成。首次在新标签页打开本页显然是未完成,但刷新后可能进入已完成状态。
  if (
    user_total_hour >= total_hour &&
    user_required_hour >= required_hour &&
    user_required_credit >= required_credit
  ) {
    console.log(`本学年任务已经完成`);
    return;
  } else {
    // 全部未完成必修课程。为同时解决学时学分问题,只关注必修课。
    const waitCourseInfo = JSON.parse(localStorage.getItem('waitCourseInfo'));
    //console.log(waitCourseInfo);
    const courses = document.querySelectorAll('#requiredCourseTable .course');
    // 初始化预期学时/学分为已得值
    let pendingCredit = user_required_credit;
    let pendingTime = user_total_hour;
    // 向预期学时/学分添加已报名课程数据
    for (w of waitCourseInfo) {
      // jdpoint 保证已报名,否则无法处理取消报名的问题
      if (w.player === 'old' && w.jdpoint >= 0) {
        pendingCredit += w.courseCredit;
        pendingTime += w.courseTime;
      }
    }
    if (user_required_credit < required_credit && exams.length > 0) {
      console.log('学分未满,有待考试课程');
      await autoOpenExam(exams);
      // 1分钟后考完关闭,2分钟后刷新
      setTimeout(() => {
        location.reload(true);
      }, 120000);
    } else if (pendingTime < total_hour) {
      console.log(
        `已报名:必修学时:${pendingTime.toFixed(1)},学分:${pendingCredit},继续报名。`
      );
      autoSignupMaxTime();
    } else if (pendingTime >= total_hour && pendingCredit < required_credit) {
      // 因为全部学习必修课,出现本状况可能很小,暂不处理
      console.log(
        `已报名:必修学时:${pendingTime.toFixed(1)},学分:${pendingCredit},有待处理。`
      );
    } else if (pendingTime >= total_hour && pendingCredit >= required_credit) {
      console.log(
        `已报名:必修学时:${pendingTime.toFixed(1)},学分:${pendingCredit},已达预期。`
      );
      if (user_required_hour < total_hour) {
        console.log('学时未满,自动打开已报名课程,将定时关闭。');
        let i = 0;
        for await (let c of courses) {
          let applyPk = c.getElementsByClassName('applyPk')[0].textContent;
          let jdjs = c.getElementsByClassName('jdjs')[0].textContent; // 完成进度定性
          // 未报名课程 applyPk === '',取消报名的课程却能直接获取 applyPk,可能不适合使用 for length++ 循环
          if (jdjs === '完成进度') {
            if (i < GM_config.get('batch')) {
              // 部分少见的新型播放器会弹出 confirm,避开为宜
              checkVideoPlayerType(applyPk, autoOpenTrain);
              i++;
            } else {
              // 跳过的课程一样会计数,可能不准
              console.log(`已尝试批量打开${i}个课程`);
              return;
            }
          }
        }
      }
    }
  }
};