Greasy Fork is available in English.

强国学习

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

As of 2021-03-12. See the latest version.

// ==UserScript==
// @name         强国学习
// @namespace    雪导.
// @version      1.2
// @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 = async 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')
  }


  const getBtnDom = async () => {
    return new Promise(resolve => {
      setTimeout(() => {
        let nextAll = document.querySelectorAll('.ant-btn')
        let next = nextAll[0]

        if (nextAll.length == 2) {
          //俩按钮,说明有个按钮是交卷。
          next = nextAll[1]
        }
        console.log('btn按钮状态', next)
        resolve(next)
      }, 2000)
    })
  }

  // 等待
  const wait = time => {
    return new Promise(resolve => setTimeout(resolve, time))
  }

  const doit = async () => {
    console.log('===========开始答题===========')

    const next = await getBtnDom()
    console.log('next', next)

    if (next.disabled) {
      document.querySelector('.tips').click()
      if (
        document.querySelector('.ant-popover-inner-content').textContent ===
        '请观看视频'
      ) {
        console.log('需要观看视频')
        window.location.reload()
        return
      }

      //所有提示
      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
      console.log('问题类型qType', qType)

      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)
            await wait(500)
            doit()
            break
          } else {
            //怕有没空白的情况。  看视频。。
            console.log('填空题,看视频?')
            window.location.href = 'http://www.baidu.com'
          }

          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 //选项按钮的内容
                console.log('cButtonText', cButtonText)
                console.log('tipText', tipText)
                //循环对比点击
                if (
                  cButtonText.indexOf(tipText) > -1 ||
                  tipText.indexOf(cButtonText) > -1
                ) {
                  cButton.click()
                  setTimeout(() => {
                    console.log('延时500下一步')
                    doit()
                  })
                }
              }
            }
          }
          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) {
              //循环对比后点击 答案是否包含正确答案
              for (let js = 0; js < buttons.length; js++) {
                let cButton = buttons[js]
                let cButtonText = cButton.textContent
                //通过判断是否相互包含,来确认是不是此选项
                if (
                  cButtonText.indexOf(tipText) > -1 ||
                  tipText.indexOf(cButtonText) > -1
                ) {
                  console.log('延时500选择')
                  await wait(500)
                  cButton.click()
                  await wait(500)
                  console.log('下一步')
                  doit()
                  break
                }
              }

              // 循环对比答案,若不纯在包含答案  则走 这套比对答案逻辑
              console.log('循环比对答案 【相似度】')
              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
                //通过判断是否相互包含,来确认是不是此选项
                xiangsidu.push(strSimilarity2Percent(tipText, cButtonText))
              }

              max_xiangsidu = max(xiangsidu)
              index = xiangsidu.findIndex(item => item === max_xiangsidu)
              console.log(`几个答案相似度【${max_xiangsidu}】`)
              console.log(`找最相似的答案【${index}】`)
              buttons[index].click()
              await wait(500)
              document.querySelector('.ant-btn').click()
              await wait(500)
              doit()
            }
          }
          break
        default:
          break
      }
    } else {
      // 可以点击
      if (
        next.textContent != '再练一次' &&
        next.textContent != '再来一组' &&
        next.textContent != '查看解析'
      ) {
        next.click()
        doit()
      } else {
        // 结束
        console.log('答题结束');
      }
    }
  }

  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
}