N0ts - 美和易思自动考试答题

看文档熬

// ==UserScript==
// @name         N0ts - 美和易思自动考试答题
// @namespace    mail@n0ts.cn
// @version      0.1.9
// @description  看文档熬
// @author       N0ts,Lu
// @match        *://www.51moot.net/*
// @match        *://www.51moot.cn/*
// @grant        none
// @require      https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js
// ==/UserScript==

(function () {
    'use strict';
    createDom();
})();

// 创建隐藏元素
function createDom() {
    // 文本框
    let dom = document.createElement("div");
    dom.classList.add("n0ts");
    dom.innerHTML = `<div class="n0tsTip"></div>
    <div class="n0tsResult"></div>
    <input type="text" class="n0tsInput">`;
    document.body.appendChild(dom);
    // css
    let css = document.createElement("style");
    css.innerHTML = `*::selection {
        background: transparent;
    }

    html,
    body {
        position: relative;
        height: 100%;
    }

    .n0ts {
        position: fixed;
        left: 0;
        bottom: 0;
        opacity: 0;
        padding: 10px;
    }

    .n0ts:hover {
        opacity: 1;
    }

    .n0tsInput {
        outline: none;
        border: none;
        background: transparent;
        color: gray;
        border-bottom: 1px solid gray;
    }

    .n0tsTip,
    .n0tsResult {
        font-size: .6rem;
        color: gray;
    }`;
    document.body.appendChild(css);
    start();
}

// 文件路径
let fileUrl = [];
// 选择是否完成
let selectStatus = false;
// 是否自动答题
let autoGo = false;
// 当前题库数量
let resultCountNum = 0;
// 错误题库数量
let errResult = 0;
// 答案
let result = [];
// 答题间隔 毫秒
let autoTime = 10;
// 计时器
let timeOut;
// 当前答题数
let timeIndex = 0;

// 开始选择
function start() {
    // 文本框
    let inputDom = document.querySelector(".n0tsInput");
    // 提示框
    let tipDom = document.querySelector(".n0tsTip");
    // 结果
    let resultDom = document.querySelector(".n0tsResult");

    // 初始文本
    tipDom.innerText = "题库地址(txt):";

    // 回车监听
    inputDom.onkeydown = function (e) {
        // 是否为回车
        if (e.keyCode != 13) {
            return;
        }

        // 内容存储
        let text = e.target.value;

        // 清空文本框
        inputDom.value = "";

        // fileUrl 清空
        fileUrl = [];

        // 重置程序
        if (text == "reload") {
            fileUrl = "";
            selectStatus = false;
            autoGo = false;
            resultCountNum = 0;
            errResult = 0;
            result = [];
            resultDom.innerText = "";
            return tipDom.innerText = "【已重制】题库地址(txt):";
        }

        // 自动答题
        if (text == "autogo") {
            if (selectStatus) {
                if (!autoGo) {
                    tipDom.innerText = "已开启自动答题,再次输入关闭";
                } else {
                    tipDom.innerText = "已关闭自动答题,再次输入开启";
                }
                autoGo = !autoGo
                return startTimeOut();
            }
        }

        // 多套题库指定
        if (text.includes("=")) {
            text = text.substring(1, text.length);
            tipDom.innerText = "正在获取多套题库内容";
            // 获取题库
            axios.get(`https://api.n0ts.cn/${text}.txt`).then(res => {
                text = ">" + res.data.split(",");
                start2(text);
            }, err => {
                errResult++;
                return tipDom.innerText = `成功!当前题库数量:${resultCountNum},错误题库数量:${errResult}`;
            })
        } else {
            start2(text);
        }
    }
}

function start2(text) {
    // 提示框
    let tipDom = document.querySelector(".n0tsTip");

    // 如果地址采用我的接口
    if (text.includes(">")) {
        text = text.substring(1, text.length);
        text = text.split(",");
        for (let i = 0; i < text.length; i++) {
            fileUrl.push(`https://api.n0ts.cn/${text[i]}.txt`);
        }
    } else {
        // 录入其他地址
        fileUrl.push(text);
    }

    // 如果都填写的话发送请求
    if (fileUrl) {
        tipDom.innerText = "正在获取题库";
        for (let i = 0; i < fileUrl.length; i++) {
            axios.get(fileUrl[i]).then(res => {
                go(res.data);
            }, err => {
                errResult++;
                tipDom.innerText = `失败!当前题库数量:${resultCountNum},错误题库数量:${errResult}`;
            })
        }
    }
}

// 跳转答案提取,自动分析文档类型
function go(res) {
    // 提示框
    let tipDom = document.querySelector(".n0tsTip");

    // word 检测
    go1(res).then(res => {
        result = result.concat(res);
        resultCountNum++;
        tipDom.innerText = `成功!当前题库数量:${resultCountNum},错误题库数量:${errResult}`;
    }, err => {
        // excel 检测
        go2(res).then(res => {
            result = result.concat(res);
            resultCountNum++;
            tipDom.innerText = `成功!当前题库数量:${resultCountNum},错误题库数量:${errResult}`;
        }, err => {
            errResult++;
            tipDom.innerText = `失败!当前题库数量:${resultCountNum},错误题库数量:${errResult}`;
            fileUrl = "";
        });
    });
}

// Word 答案提取
function go1(word) {
    return new Promise(function (resolve, reject) {
        // 获取题目数量
        let wordNum;
        // 提取题目数量语句判断题目数
        let wordNumCache = word.substring(0, word.indexOf("题_共"));
        for (let i = 0; i < wordNumCache.length; i++) {
            let isnum = wordNumCache.substring(wordNumCache.length - i, wordNumCache.length);
            // 如果不是数字则存储
            if (isnum != null && isnum != "" && isNaN(isnum)) {
                wordNum = Number(isnum.substring(1, isnum.length).trim());
                break;
            }
        }

        // 数据判断
        if (word == null || wordNum == null) {
            return reject();
        }

        // 正确选项过滤
        let rightOptionText = word.substring(word.indexOf(`1:`), word.length).trim();
        let rightOption = [];
        for (let i = 1; i < wordNum + 1; i++) {
            let cache;
            // 是否为最后一题
            let lastCache = `${i + 1}:`;
            if (i == wordNum) {
                // 最后一题的处理
                let textCache;
                for (let j = 0; j < 10; j++) {
                    let lastCache = rightOptionText.substring(rightOptionText.indexOf(`${i}:`), rightOptionText.indexOf(`${i}:`) + j).trim();
                    if (textCache == lastCache) {
                        cache = lastCache;
                        break;
                    }
                    textCache = lastCache;
                }
            } else {
                // 提取单独选项
                cache = rightOptionText.substring(rightOptionText.indexOf(`${i}:`), rightOptionText.indexOf(lastCache)).trim();
            }
            // 去掉排序数字
            cache = cache.substring(cache.indexOf(":") + 1, cache.length);
            // 选项分割
            rightOption[i] = cache.split("", cache.length);
        }

        // 题目与答案过滤
        let wordResult = [];
        for (let i = 1; i <= wordNum; i++) {
            // 是否为最后一题,如果是则用 “t” 结尾
            let lastCache = word.indexOf(`第${i + 1}题`);
            if (i == wordNum) {
                lastCache = word.length;
            }
            // 根据 “第几题” 提取全部题目与答案
            let data = word.substring(word.indexOf(`第${i}题`), lastCache).trim();

            // 获取题目,去掉 “第几题“ 一行
            data = data.replace("【多选】", "");
            data = data.replace("【选两项】", "");
            let subject = (data.substring(data.lastIndexOf("】") + 1, data.length)).trim();

            // 去掉选项
            subject = subject.substring(0, subject.lastIndexOf("A.")).trim();

            // 存入结果与答案
            wordResult[i - 1] = {
                subject,
                rightOption: rightOption[i]
            };
        }

        // 检验结果
        if (wordResult.length != wordNum || wordResult.length == 0) {
            return reject();
        }

        // 启用答题
        selectStatus = true;

        // 返回结果
        return resolve(wordResult);
    })
}

// Excel 答案提取
function go2(excelData) {
    return new Promise(function (resolve, reject) {
        // 根据 “\t” 分割内容
        let excel = excelData.split("\t");

        // 数据检查
        if(excel.length <= 1) {
            return reject();
        }

        // 题目与答案索引
        let subjectIndex;
        let excelResultIndex;

        // 获取标题
        let title = [];
        for (let i = 0; i < excel.length; i++) {
            if (excel[i].includes("题干")) {
                subjectIndex = i;
            } else if (excel[i].includes("答案")) {
                excelResultIndex = excelResultIndex ? excelResultIndex : i;
            }
            if (excel[i].indexOf("\n") >= 0) {
                title.push(excel[i].substring(0, excel[i].indexOf("\n")).trim());
                break;
            }
            title.push(excel[i]);
        }

        // 格式纠正与数量计算
        let excelNum = 0;
        let indexCache = 0;
        for (let i = 0; i < excel.length; i++) {
            if (indexCache == title.length - 1) {
                indexCache = 0;
                excelNum++;
                excel.splice(i, 1, excel[i].substring(0, excel[i].indexOf("\n")).trim(), excel[i].substring(excel[i].indexOf("\n")).trim());
            } else {
                indexCache++;
            }
        }

        // 题目库中删除标题
        excelNum--;
        excel.splice(0, title.length);

        // 分割题目
        let excelResult = [];
        for (let i = 0; i < excelNum; i++) {
            let rCache = [];
            for (let j = 0; j < title.length; j++) {
                if (excel[j + i * title.length]) {
                    rCache.push(excel[j + i * title.length].trim());
                }
            }
            excelResult.push(rCache);
        }

        // 题目与答案过滤
        for (let i = 0; i < excelResult.length; i++) {
            excelResult[i] = {
                subject: excelResult[i][subjectIndex],
                rightOption: excelResult[i][excelResultIndex].split("", excelResult[i][excelResultIndex].length)
            }
        }

        // 检验结果
        if (excelResult.length != excelNum || excelResult.length == 0) {
            return reject();
        }

        // 启用答题
        selectStatus = true;

        // 返回结果
        return resolve(excelResult);
    })
}

// 鼠标选择题目触发
let titleList = document.querySelectorAll(".test-title-details-list-tit");
titleList.forEach(item => {
    item.onmouseup = function () {
        selectText();
    }
});

// 自动答题
// 启用/停用计时器
function startTimeOut() {
    // 提示框
    let tipDom = document.querySelector(".n0tsTip");
    // 结果
    let resultDom = document.querySelector(".n0tsResult");

    // 获取题目
    let details = document.querySelectorAll(".test-title-details-list-tit > h4");

    // 判断是否启用
    if (autoGo) {
        timeOut = setInterval(() => {
            if (timeIndex == details.length) {
                setTimeout(() => {
                    autoGo = false;
                    tipDom.innerText = "答题完毕!请检查空题!";
                    resultDom.innerText = "";
                    clearInterval(timeOut);
                    timeIndex = 0;
                }, 0);
            }
            selectText(details[timeIndex]);
            timeIndex++;
        }, autoTime);
    } else {
        clearInterval(timeOut);
        timeIndex = 0;
    }
}

// 填写答案
// 传入题目则根据题目填写
// 不传入则根据鼠标选择文字模糊搜索题目
function selectText(text) {
    // 提示框
    let tipDom = document.querySelector(".n0tsTip");
    // 结果
    let resultDom = document.querySelector(".n0tsResult");

    // 答案获取完毕后才允许进入
    if (!selectStatus) {
        return;
    }

    // 题目选项
    let selectItem;

    // 自动答题
    if (typeof (text) != "undefined" && text.innerText) {
        selectItem = document.querySelectorAll(`#${text.parentNode.parentNode.id} li > div`);
        text = text.innerText;
        if (text.length >= 40) {
            let n = Number(text.length * .2) / 2;
            if (text.includes("(")) {
                text = text.substring(n, text.indexOf("("));
            } else if (text.includes("\n")) {
                text = text.substring(n, text.indexOf("\n"));
            } else if (text.includes("【")) {
                text = text.substring(n, text.indexOf("【"));
            } else {
                text = text.substring(n, text.length - n);
            }
        } else {
            text = text.substring(4, text.length - 4);
        }
    }

    // 鼠标点击或选择题目
    try {
        // 获取文字
        if (!text) {
            text = window.getSelection();
            selectItem = document.querySelectorAll(`#${text.focusNode.parentNode.parentNode.parentNode.getAttribute('id')} li > div`);
        }
        // 没有文字则获取全部内容
        if (text.toString() == "") {
            text = text.focusNode.data;
            if (text.length >= 40) {
                let n = Number(text.length * .2) / 2;
                if (text.includes("(")) {
                    text = text.substring(n, text.indexOf("("));
                } else if (text.includes("\n")) {
                    text = text.substring(n, text.indexOf("\n"));
                } else if (text.includes("【")) {
                    text = text.substring(n, text.indexOf("【"));
                } else {
                    text = text.substring(n, text.length - n);
                }
            } else {
                text = text.substring(4, text.length - 4);
            }
        }
    } catch {
        return;
    }

    // 清空内容
    tipDom.innerText = "";
    resultDom.innerText = "";

    // 遍历答案
    for (let i = 0; i < result.length - 1; i++) {
        // 匹配题目
        if (result[i].subject.indexOf(text) > -1) {
            // 显示题目
            tipDom.innerText = "题目:" + result[i].subject;
            // 如果存在选择就取消
            for (let j = 0; j < selectItem.length; j++) {
                if (selectItem[j].className.includes("layui-form-checked")) {
                    selectItem[j].click();
                }
            }
            // 显示答案 && 自动点击
            resultDom.innerText = "答案:"
            for (let j = 0; j < result[i].rightOption.length; j++) {
                resultDom.innerText += result[i].rightOption[j] + " ";
                switch (result[i].rightOption[j]) {
                    case "A":
                        selectItem[0].click();
                        break;
                    case "B":
                        selectItem[1].click();
                        break;
                    case "C":
                        selectItem[2].click();
                        break;
                    case "D":
                        selectItem[3].click();
                        break;
                    case "E":
                        selectItem[4].click();
                        break;
                    default:
                        break;
                }
            }
            break;
        }
        // 未找到题目
        resultDom.innerText = "未找到,请重试!"
    }
}