Greasy Fork

Greasy Fork is available in English.

强国学习

强国学习自动答题,目前实现 每日答题,每周答题,专项答题

当前为 2021-03-12 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         强国学习
// @namespace    雪导.
// @version      1.1
// @description  强国学习自动答题,目前实现 每日答题,每周答题,专项答题
// @author       雪导
// @match        https://pc.xuexi.cn/points/exam-practice.html*
// @match        https://pc.xuexi.cn/points/exam-weekly-detail.html*
// @match        https://pc.xuexi.cn/points/exam-paper-detail.html*
// @match        https://pc.xuexi.cn/points/my-study.html*
// @match        https://pc.xuexi.cn/points/exam-index.html*
// @match        https://pc-api.xuexi.cn/open/api/score/today/queryrate
// @grant        none
// ==/UserScript==
window.onload = function () {
  console.log('加载成功')
  //移除顶部
  const header = document.querySelector('#app > div > div.layout-header')
  if (header != null) {
    header.parentNode.removeChild(header)
    console.log('移除header乱七八糟的dom')
  }
  //移除底部
  const footer = document.querySelector('#app > div > div.layout-footer')
  if (footer != null) {
    footer.parentNode.removeChild(footer)
    console.log('移除footer乱七八糟的dom')
  }

  // setInterval(doit,500);

  const doit = () => {
    console.log('===========开始答题===========')
    let nextAll = document.querySelectorAll('.ant-btn')
    let next = nextAll[0]

    if (nextAll.length == 2) {
      //俩按钮,说明有个按钮是交卷。
      next = nextAll[1]
    }
    console.log(next)
    if (next.disabled) {
      document.querySelector('.tips').click()

      //所有提示
      let allTips = document.querySelectorAll('font[color=red]')

      //单选多选时候的按钮
      let buttons = document.querySelectorAll('.q-answer')

      //填空时候的那个textbox,这里假设只有一个填空
      let textboxs = document.querySelectorAll('input')
      //问题类型
      let qType = document.querySelector('.q-header').textContent

      qType = qType.substr(0, 3)

      switch (qType) {
        case '填空题':
          //第几个填空
          let mevent = new Event('input', { bubbles: true })
          if (textboxs.length > 1) {
            //若不止是一个空
            //填空数量和提示数量是否一致
            if (allTips.length == textboxs.length) {
              for (
                let i = 0;
                i < allTips.length;
                i++ //数量一致,则一一对应。
              ) {
                let tip = allTips[i]
                let tipText = tip.textContent
                if (tipText.length > 0) {
                  //通过设置属性,然后立即让他冒泡这个input事件.
                  //否则1,setattr后,内容消失.
                  //否则2,element.value=124后,属性值value不会改变,所以冒泡也不管用.
                  textboxs[i].setAttribute('value', tipText)
                  textboxs[i].dispatchEvent(mevent)
                  //  break;
                }
              }
            } else {
              //若填空数量和提示数量不一致,那么,应该都是提示数量多。

              if (allTips.length > textboxs.length) {
                let lineFeed = document.querySelector('.line-feed').textContent //这个是提示的所有内容,不仅包含红色答案部分。
                let n = 0 //计数,第几个tip。
                for (
                  let j = 0;
                  j < textboxs.length;
                  j++ //多个填空
                ) {
                  let tipText = allTips[n].textContent
                  let nextTipText = ''
                  do {
                    tipText += nextTipText
                    if (n < textboxs.length - 1) {
                      n++
                      nextTipText = allTips[n].textContent
                    } else {
                      nextTipText = '结束了,没有了。'
                    }
                  } while (lineFeed.indexOf(tipText + nextTipText))

                  textboxs[j].setAttribute('value', tipText)
                  textboxs[j].dispatchEvent(mevent)
                }
              } else {
                //提示数量少于填空数量,则我无法分析, 回头研究,暂时放弃作答,刷新题库浏览器
                location.reload()
              }
            }
          } else if (textboxs.length == 1) {
            //只有一个空,直接把所有tips合并。
            let tipText = ''
            for (let i = 0; i < allTips.length; i++) {
              tipText += allTips[i].textContent
            }
            textboxs[0].setAttribute('value', tipText)
            textboxs[0].dispatchEvent(mevent)
            break
          } else {
            //怕有没空白的情况。  看视频。。
            history.go(0)
          }

          break
        case '多选题':
          //循环选项列表。用来点击
          for (let js = 0; js < buttons.length; js++) {
            let cButton = buttons[js]
            for (
              let i = 0;
              i < allTips.length;
              i++ //循环提示列表。
            ) {
              let tip = allTips[i]
              let tipText = tip.textContent
              if (tipText.length > 0) {
                //提示内容长度大于0
                let cButtonText = cButton.textContent //选项按钮的内容
                //循环对比点击
                if (
                  cButtonText.indexOf(tipText) > -1 ||
                  tipText.indexOf(cButtonText) > -1
                ) {
                  cButton.click()
                  break
                }
              }
            }
          }
          break
        case '单选题':
          //单选,所以所有的提示,其实是同一个。有时候,对方提示会分成多个部分。
          //case 块里不能直接用let。所以增加了个if。
          if (true) {
            //把红色提示组合为一条
            let tipText = ''
            for (let i = 0; i < allTips.length; i++) {
              tipText += allTips[i].textContent
            }

            if (tipText.length > 0) {
              //循环对比后点击
              let xiangsidu = [] // 相似度
              let max_xiangsidu = 0
              let index = 0

              for (let js = 0; js < buttons.length; js++) {
                let cButton = buttons[js]
                let cButtonText = cButton.textContent
                console.log('cButtonText', cButtonText)
                console.log('tipText', tipText)
                //通过判断是否相互包含,来确认是不是此选项
                if (
                  cButtonText.indexOf(tipText) > -1 ||
                  tipText.indexOf(cButtonText) > -1
                ) {
                  cButton.click()
                  break
                } else {
                  // 没有找到,判断 内容相似程度
                  xiangsidu.push(strSimilarity2Percent(tipText, cButtonText))
                }
              }
              console.log(xiangsidu)
              max_xiangsidu = max(xiangsidu)
              index = xiangsidu.findIndex(item => item === max_xiangsidu)
              buttons[index].click()
            }
            // 选择完后,点击确定
            console.log(document.querySelector('.ant-btn').click())
            document.querySelector('.ant-btn').click()
          }
          break
        default:
          break
      }

      document.querySelector('.ant-btn').click()
      setTimeout(() => {
        doit()
      }, 2500)
    } else {
      if (
        next.textContent != '再练一次' &&
        next.textContent != '再来一组' &&
        next.textContent != '查看解析'
      ) {
        next.click()
      } else {
        // 结束
      }
    }
  }

  let btn = document.querySelector('.ant-btn')
  if (btn) {
    if (btn.textContent === '确 定') {
      doit()
    } else {
      location.reload()
    }
  }
}

// ============= 工具 =============
const strSimilarity2Number = (s, t) => {
  var n = s.length,
    m = t.length,
    d = []
  var i, j, s_i, t_j, cost
  if (n == 0) return m
  if (m == 0) return n
  for (i = 0; i <= n; i++) {
    d[i] = []
    d[i][0] = i
  }
  for (j = 0; j <= m; j++) {
    d[0][j] = j
  }
  for (i = 1; i <= n; i++) {
    s_i = s.charAt(i - 1)
    for (j = 1; j <= m; j++) {
      t_j = t.charAt(j - 1)
      if (s_i == t_j) {
        cost = 0
      } else {
        cost = 1
      }
      d[i][j] = Minimum(
        d[i - 1][j] + 1,
        d[i][j - 1] + 1,
        d[i - 1][j - 1] + cost
      )
    }
  }
  return d[n][m]
}
const Minimum = (a, b, c) => {
  return a < b ? (a < c ? a : c) : b < c ? b : c
}
// 判断两个字符串 相似度
const strSimilarity2Percent = (s, t) => {
  var l = s.length > t.length ? s.length : t.length
  var d = strSimilarity2Number(s, t)
  return (1 - d / l).toFixed(4)
}
/**
 * 获取数组中的 最大值
 * @param {*} arr
 * @returns
 */
const max = arr => {
  var num = arr[0]
  for (var i = 0; i < arr.length; i++) {
    if (num < arr[i]) {
      num = arr[i]
    }
  }
  return num
}