刷课辅助

2021/4/10下午4:02:51

// ==UserScript==
// @name        刷课辅助
// @namespace
// @match     *://course.sceouc.cn/CourseStudy.ashx*
// @grant       none
// @version     0.3
// @author      -
// @description 2021/4/10下午4:02:51
// @run-at document-start
// @namespace
// @namespace 
// ==/UserScript==

const hackAddEvent = () => {
  const _addEvent = window.addEventListener;
  const myHackEvent = (ename, evt, opt) => {
    const blackList = ['blur', 'keydown']; // 拦截的事件名称列表
    if (typeof opt !== 'object' || typeof opt !== 'boolean') {
      // 第三个参数如果不传就给个默认
      opt = {
        capture: false,
        once: false,
        passive: false
      };
    }
    if (typeof evt !== 'function') {
      return;
    }
    if (blackList.includes(ename)) {
      // 拦截事件
      console.log(`已拦截${ename}事件`);
      return;
    }
    _addEvent(ename, evt, opt); // 调用原方法
  };
  window.addEventListener = myHackEvent;
};

hackAddEvent();

document.onkeydown = null;

// 加载vue文件
const loadVue = () => {
  return new Promise((resolve, reject) => {
    const script = document.createElement('script');
    script.src = '//lib.baomitu.com/vue/3.2.31/vue.global.prod.min.js';
    script.onload = () => {
      resolve();
    };
    document.head.appendChild(script);
  });
};

const renderEl = pageData => {
  const template = `
  <div id="c_video_ctrl">
    设置
    <div class="wrapper">
      <p>
        <strong>课程信息</strong>
        <div class="-wrapper-box">
          <span>{{courseInfo.name}}</span><br>
          <span>共{{courseInfo.total}}节,当前第{{courseInfo.current}}节</span>
        </div>
      </p>
      <p>
        <strong>视频信息</strong>
        <div class="-wrapper-box" style="display: flex; align-items: center; justify-content: center;">
          <span>
            <input class="input-box" type="number" min="10" :max="videoInfo.duration" step="10" v-model="videoInfo.myTime" /> / {{videoInfo.duration}}
          </span>
          <button type="button" v-if="videoInfo.duration" @click="startTask">刷课</button>
        </div>
        <div class="-wrapper-box" style="padding-top: 15px;">
          <span>{{msg}}</span>
        </div>
      </p>
    </div>
  </div>
  `;
  const vueRoot = document.createElement('div'); // 创建根节点
  vueRoot.id = '__vue-root';
  document.body.appendChild(vueRoot);

  const app = Vue.createApp({
    el: '#c_video_ctrl',
    template: template,
    data() {
      return {
        courseInfo: {}, // 课程信息
        videoInfo: {}, // 视频信息
        msg: ''
      };
    },
    methods: {
      getCourseInfo() {
        const { course, courseList } = this.pageData.courseInfo;
        if (!course) {
          return;
        }
        // 正则匹配《》中的内容
        const reg = /《(.*)》/;
        const result = reg.exec(course.innerText);
        // 正则匹配query参数中的couid
        const queryReg = /id=(\d+)/;
        const courseId = queryReg.exec(course.href)[1];

        let currentIndex = 0;
        const courses = Array.from(courseList).map((item, index) => {
          const { className } = item;
          if (className.includes('current')) currentIndex = index;
          const { href, innerText } = item.children[0];
          const id = item.attributes.olid.value;
          return {
            name: innerText,
            url: href,
            id
          };
        });

        this.courseInfo = {
          name: result[1],
          url: course.href,
          id: courseId,
          current: currentIndex + 1,
          total: courses.length,
          courses
        };
        console.log(this.courseInfo, 'this.courseInfo');
      },
      getVideoInfo() {
        if (typeof CKobject === 'undefined') return;
        const totalTime = CKobject._K_('totalTime');
        if (!totalTime)
          return (this.msg = '未获取到时长信息,请确认是否在视频页面');
        const duration = +totalTime.innerHTML;
        this.videoInfo = {
          duration,
          myTime: duration // 默认设置为最大时间
        };
      },
      startTask() {
        if (!this.videoInfo.myTime) return (this.msg = '请输入刷课时间');
        // debugger;
        if (
          this.videoInfo.myTime > this.videoInfo.duration ||
          this.videoInfo.myTime < 10
        )
          return (this.msg = '请输入正确的刷课时间,不能小于10');

        this.msg = '';
        this.sendData()
          .then(() => {
            this.msg = '刷课成功';
          })
          .catch(() => {
            this.msg = '刷课失败';
          });
      },
      sendData() {
        return new Promise((resolve, reject) => {
          const couid = this.courseInfo.id;
          const olid = this.courseInfo.courses[this.courseInfo.current - 1].id;
          const studyTime = this.videoInfo.myTime;
          fetch(
            `/Ajax/StudentStudy.ashx?couid=${couid}&olid=${olid}&studyTime=${studyTime}&playTime=${
              studyTime * 1000
            }&totalTime=${this.videoInfo.duration * 1000}`
          )
            .then(res => {
              resolve(res);
            })
            .catch(err => {
              reject(err);
            });
        });
      },
      init() {
        this.getCourseInfo();
        this.getVideoInfo();
      }
    },
    mounted() {
      this.init();
    }
  });
  app.config.globalProperties.pageData = pageData;
  app.mount(vueRoot);
};

const renderStyle = function () {
  const styles = `
  #c_video_ctrl {
    width: 50px;
    height: 50px;
    border-radius: 10px;
    position: fixed;
    right: 50px;
    top: 20px;
    z-index: 999;
    text-align: center;
    line-height: 50px;
    font-size: 18px;
    color: #ff8864;
    border: 2px solid #ff8864;
  }
  #c_video_ctrl .wrapper {
    width: 400px;
    height: auto;
    border-radius: 10px;
    position: absolute;
    right: -2px;
    top: -2px;
    z-index: 1000;
    background: #eee;
    display: none;
    color: #000;
    padding-bottom: 30px;
  }
  #c_video_ctrl:hover .wrapper {
    display: block;
  }
  #c_video_ctrl .wrapper p {
    display: flex;
    height: max-content;
    padding: 10px 15px;
    margin: 0;
    flex-wrap: wrap;
    justify-content: space-around;
  }
  #c_video_ctrl .wrapper p strong {
    width: 100%;
    font-size: 18px;
  }
  #c_video_ctrl .wrapper p span {
    font-size: 16px;
    cursor: pointer;
    padding: 0 10px;
    color: #333;
  }
  #c_video_ctrl .wrapper p span:hover {
    color: #ff8864;
  }
  .-wrapper-box {
    line-height: 1.5em;
    width: 100%;
  }
  .input-box{
    min-width: 60px;
    outline: 0;
    font-size: unset;
  }
  button {
    font-size: unset;
    background: blue;
    color: #fff;
    padding: 0 10px;
    height: 100%;
    border-radius: 6px;
    cursor: pointer;
  }
  `;
  const style = document.createElement('style');
  style.innerHTML = styles;
  document.head.appendChild(style);
};

const collectData = _ => {
  const pageData = {}; // 存储页面数据

  return new Promise((resolve, reject) => {
    const courseList = document.querySelectorAll('.olitem'); // 课程列表
    if (!courseList.length) {
      reject();
    }

    const course = document.querySelector('.courseBox > a'); // 课程基本信息
    pageData.courseInfo = {
      course,
      courseList
    };
    resolve(pageData);
  });
};

const myLoad = () => {
  console.log(location.href);
  collectData()
    .then(pageData => {
      loadVue().then(() => {
        renderStyle();
        renderEl(pageData);
      });
    })
    .catch(() => {
      console.log('没有在当前页面上找到课程列表');
    });
};

window.addEventListener('load', myLoad);