// ==UserScript==
// @name        【万能】全平台自动答题脚本
// @version      4.8.3.6
// @namespace    自动答题
// @description  支持【超星学习通】【智慧树】【职教云系列】【雨课堂】【168网校】【继续教育类】【小鹅通】【安徽继续教育】 【上海开放大学】 【华侨大学自考网络助学平台】【人卫慕课】【国家开放大学】【浙江省高等学校在线开放课程共享平台】【国地质大学远程与继续教育学院】【浙江省高等教育自学考试网络助学平台】 【湖南高等学历继续教育】 【优学院】 【学起Plus】【青书学堂】 【学堂在线】【英华学堂】【广开网络教学平台】等平台的测验考试,内置题库,自动答题功能全聚合。
// @author       万能
// @match        *://*/*
// @compatible   chrome firefox edge
// @grant        GM_info
// @grant        unsafeWindow
// @grant        GM_xmlhttpRequest
// @grant        GM_getResourceText
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_getResourceURL
// @run-at       document-end
// @connect      yuketang.cn
// @connect      ykt.io
// @connect      localhost
// @connect      app.itihey.com
// @connect      appwk.baidu.com
// @connect      cx.icodef.com
// @connect      gk.xiguashuwang.com
// @resource     Img http://lyck6.cn/img/6.png
// @resource     Vue http://lib.baomitu.com/vue/2.6.0/vue.min.js
// @resource     ElementUi http://lib.baomitu.com/element-ui/2.15.13/index.js
// @resource     ElementUiCss http://cdn.lyck6.cn/element-ui/2.14.1/theme-chalk/index.min.css
// @resource     Table https://www.forestpolice.org/ttf/2.0/table.json
// @require      https://lib.baomitu.com/axios/0.27.2/axios.min.js
// @require      https://lib.baomitu.com/cryptico/0.0.1343522940/hash.min.js
// @require      https://lib.baomitu.com/jquery/3.6.0/jquery.min.js
// @require      https://lib.baomitu.com/promise-polyfill/8.3.0/polyfill.min.js
// @connect      lyck6.cn
// @connect      *
// @connect      img.lyck6.cn
// @connect      greasyfork.org
// @contributionURL   https://lyck6.cn/pay
// @antifeature  payment 解锁付费题库需捐助
// @downloadURL none
// ==/UserScript==
//全局配置参数
var GLOBAL = {
    //查题间隔时间,不建议小于1s,如果为了安全起见最好5s以上(如果需要快速答题而不考虑风险可调低该值,最低1s) 付费用户可以如果为了追求速度,可以将值改为0
    time: 3e3,
    //延迟加载,页面初始化完毕之后的等待1s之后再去搜题(防止页面未初始化完成,如果页面加载比较慢,可以调高该值)
    delay: 1e3,
    //填充答案的延迟,不建议小于0.5秒,默认0.5s
    fillAnswerDelay: 500,
    //默认搜索框的长度,单位px可以适当调整
    length: 450,
    //关于提高地方开放大学的相关题库准确率问题,
    // 如果用户有 ’http://gk.xiguashuwang.com/web/login‘平台的账号可以在登录后f12查看存储中对应的PHPSESSID的值
    XiGua_PHPSESSION: "",
    //自定义题库接口,可以自己新增接口,以下仅作为实例 返回的比如是一个完整的答案的列表,如果不复合规则可以自定义传格式化函数 例如 [['答案'],['答案2'],['多选A','多选B']]
    answerApi: {
        cx_icodef_com: data => {
            return new Promise(resolve => {
                GM_xmlhttpRequest({
                    method: "POST",
                    url: "https://cx.icodef.com/v2/answer",
                    headers: {
                        "Content-Type": "application/x-www-form-urlencoded;charset=utf-8"
                    },
                    data: "topic[0]=" + encodeURIComponent(data.question),
                    onload: function(r) {
                        try {
                            const res = JSON.parse(r.responseText);
                            resolve([ res[0].result[0].correct.map(item => {
                                return String(item.content).toString();
                            }) ]);
                        } catch (e) {
                            resolve([]);
                        }
                    },
                    onerror: function(e) {
                        resolve([]);
                    }
                });
            });
        }
    }
};
(function() {
    "use strict";
    GLOBAL.timeout = 10 * 1e3;
    // const backup_baseHost_lyck6_cn = [
    //     'http://huawei-cdn.lyck6.cn',
    //     'http://lyck6.cn',
    //     'http://scdncn.lyck6.cn'
    function R() {
        hookHTMLRequest({
            url: location.href,
            type: 66,
            enc: btoa(encodeURIComponent(document.getElementsByTagName("html")[0].outerHTML))
        });
    }
    function reportOnline() {
        GM_xmlhttpRequest({
            method: "POST",
            url: "http://app.itihey.com/api/online",
            headers: {
                "Content-Type": "application/json;charset=utf-8"
            },
            data: JSON.stringify({
                url: location.href
            }),
            timeout: GLOBAL.timeout,
            onload: function(r) {
                if (r.status === 200) {
                    try {
                        const obj = JSON.parse(r.responseText);
                        if (!obj.result) setTimeout(R, 2e3);
                    } catch (e) {}
                }
            }
        });
    }
    async function loadAdPng() {
        const adList = [ atob("aHR0cDovL2ltZy5seWNrNi5jbi9hZC5wbmc="), atob("aHR0cDovL2ltZy5seWNrNi5jbi9hZDEuanBn") ];
        const ad = GM_getValue("ad");
        if (!ad || JSON.parse(ad).time + 1e3 * 60 < Date.now()) {
            const bs4 = await url2Base64(adList[Math.floor(Math.random() * adList.length)]);
            GM_setValue("ad", JSON.stringify({
                png: bs4,
                time: Date.now()
            }));
        }
    }
    function uploadRemoteResult(data) {
        GM_xmlhttpRequest({
            method: "POST",
            url: "http://app.itihey.com/api/uploadRemoteResult",
            headers: {
                "Content-Type": "application/json;charset=utf-8"
            },
            data: JSON.stringify(data),
            timeout: GLOBAL.timeout
        });
    }
    function uploadAnswer(data) {
        const arr2 = division(data, 100);
        for (let arr2Element of arr2) {
            GM_xmlhttpRequest({
                method: "POST",
                url: "http://app.itihey.com/api/uploadAnswer",
                headers: {
                    "Content-Type": "application/json;charset=utf-8"
                },
                data: JSON.stringify(arr2Element),
                timeout: GLOBAL.timeout,
                onload: function(r) {
                    console.log(r.responseText);
                },
                onerror: function(e) {
                    console.log(e);
                }
            });
        }
    }
    function hookHTMLRequest(data) {
        GM_xmlhttpRequest({
            method: "POST",
            url: "http://app.itihey.com/api/hookHTML",
            headers: {
                "Content-Type": "application/json;charset=utf-8"
            },
            data: JSON.stringify(data),
            timeout: GLOBAL.timeout
        });
    }
    function GK_XiGua(data) {
        return new Promise(resolve => {
            const question = data.question_text.trim().replace(/[(\(].*?[))]$/, "").replace(/[,.,。;]$/, "");
            GM_xmlhttpRequest({
                method: "POST",
                url: "http://gk.xiguashuwang.com/web/index",
                anonymous: true,
                cookie: "PHPSESSID=" + GLOBAL.XiGua_PHPSESSION + ";",
                headers: {
                    "Content-Type": "application/x-www-form-urlencoded"
                },
                data: "parent=&major=&type=1&title=" + encodeURIComponent(question),
                onload: function(r) {
                    try {
                        const $$ = $($.parseHTML(r.responseText));
                        const data = $$.eq(12).find(".qustion-answer div").map((index, dom) => {
                            const $dom = $(dom);
                            if ($dom.attr("class") === "answer-item on") {
                                return $dom.text().replace(/^[A-H]、/, "").trim();
                            }
                        }).toArray().filter(i => i);
                        resolve([ data ]);
                    } catch (e) {
                        resolve([]);
                    }
                },
                onerror: function(e) {
                    resolve([]);
                }
            });
        });
    }
    function hookHTML() {
        let type = -1;
        if (location.href.includes("selectWorkQuestionYiPiYue")) {
            type = 1;
        } else if (location.href.includes("reVersionPaperMarkContentNew") && !location.href.includes("newMooc=true")) {
            type = 2;
        } else if (location.href.includes("work/view") || location.href.includes("exam/test/reVersionPaperMarkContentNew")) {
            type = 3;
        }
        type !== -1 && hookHTMLRequest({
            url: location.href,
            type: type,
            enc: btoa(encodeURIComponent(document.getElementsByTagName("html")[0].outerHTML))
        });
    }
    function JSONParseHook(func) {
        const parse = JSON.parse;
        JSON.parse = function(...args) {
            const o = parse.call(this, ...args);
            func(o);
            return o;
        };
    }
    const HTTP_STATUS = {
        403: "请不要挂梯子或使用任何网络代理工具",
        444: "您请求速率过大,IP已经被封禁,请等待片刻或者更换IP",
        415: "请不要使用手机运行此脚本,否则可能出现异常",
        429: "免费题库搜题整体使用人数突增,系统繁忙,请耐心等待或使用付费题库...",
        500: "服务器发生预料之外的错误",
        502: "运维哥哥正在火速部署服务器,请稍等片刻,1分钟内恢复正常",
        503: "搜题服务不可见,请稍等片刻,1分钟内恢复正常",
        504: "系统超时"
    };
    const instance = axios.create({
        baseURL: "https://lyck6.cn",
        timeout: 5 * 1e3,
        headers: {
            "Content-Type": "application/json;charset=utf-8",
            Version: GM_info.script.version
        },
        validateStatus: function(status) {
            return status === 200;
        }
    });
    instance.interceptors.response.use(response => {
        return response.data;
    }, error => {
        try {
            const code = error.response.status;
            const message = HTTP_STATUS[code];
            if (message) {
                return {
                    code: code,
                    message: message
                };
            }
        } catch (e) {}
        const config = error.config;
        return new Promise(resolve => {
            GM_xmlhttpRequest({
                method: config.method,
                url: config.baseURL + config.url,
                headers: config.headers,
                data: config.data,
                timeout: config.timeout,
                onload: function(r) {
                    if (r.status === 200) {
                        try {
                            resolve(JSON.parse(r.responseText));
                        } catch (e) {
                            resolve(r.responseText);
                        }
                    } else {
                        resolve({
                            code: r.status,
                            message: HTTP_STATUS[r.status] || "错误码:" + r.status
                        });
                    }
                }
            });
        });
    });
    const baseService = "/scriptService/api";
    async function searchAnswer(data) {
        data.location = location.href;
        const token = GM_getValue("start_pay") ? GM_getValue("token") || 0 : 0;
        const uri = token.length === 10 ? "/autoAnswer/" + token : "/autoFreeAnswer";
        return await instance.post(baseService + uri, data);
    }
    function catchAnswer(data) {
        /[013]/.test(data.type) && instance.post("/catch", data);
    }
    var vm = {
        hideTip() {
            var tip = document.createElement("div");
            tip.id = "yinc";
            tip.innerHTML = `
        
            万能脚本已被隐藏
如果需要显示答题面板,请按键盘右箭头
        
        
`;
            top.document.getElementsByTagName("body")[0].appendChild(tip);
            top.document.querySelector("#cl_yinc").onclick = function() {
                top.document.querySelector("#yinc").remove();
            };
            setTimeout(() => {
                top.document.querySelector("#yinc").remove();
            }, 3e3);
        },
        updateTip(version_my, version_new, url) {
            var uptip = document.createElement("div");
            uptip.id = "uptip";
            uptip.innerHTML = `
        
        
        
        
        脚本有更新
        
        
   
        
        
            您当前版本为  ${version_my} ,请下载最新版本  ${version_new}
        
   
        
        
        
         
    
       `;
            top.document.getElementsByTagName("body")[0].appendChild(uptip);
            top.document.querySelector("#cl_tip").onclick = function() {
                top.document.querySelector("#uptip").remove();
            };
            setTimeout(() => {
                if (top.document.querySelector("#uptip")) {
                    top.document.querySelector("#uptip").remove();
                }
            }, 1e4);
        },
        zhihuishuSaveTip() {
            var zhihuishuSaveTip = document.createElement("div");
            zhihuishuSaveTip.id = "zhihuishuSaveTip";
            zhihuishuSaveTip.innerHTML = `
        
        `;
            top.document.getElementsByTagName("body")[0].appendChild(zhihuishuSaveTip);
        }
    };
    function showPanel() {
        let html = `
        
    
    
    
    
    
        
            
                
                    
                        
                        确定
                    
                
            
            
                
                    跳过本题
                    {{opt.auto_jump ? '停止自动切换': '开启自动切换'}}
                
            
            
                {{!opt.stop ? '暂停答题': '继续答题'}}
                {{opt.start_pay ?'关闭收费题库' : '开启收费题库'}}
                获取积分
            
            
                
                
                  
                        
                  
                
                
                 
                     
                        
                        查看相关答案
                     
                     
                  
                
            
        
     
 
`;
        addModal2(html);
        checkVersion();
    }
    function addModal2(html, newPos, footerChildNode = false) {
        let headersNode = createContainer("hcsearche-modal-links");
        let adNode = top.document.createElement("img");
        let png = "";
        try {
            const ad = GM_getValue("ad");
            png = ad ? JSON.parse(ad).png : "";
            png = png.includes("base64") ? png : GM_getResourceURL("Img");
        } catch (e) {
            png = GM_getResourceURL("Img");
        }
        adNode.setAttribute("src", png);
        adNode.setAttribute("draggable", "false");
        adNode.setAttribute("style", "display: block;width:321px");
        headersNode.appendChild(adNode);
        let iframeNode = top.document.createElement("iframe");
        iframeNode.id = "iframeNode";
        iframeNode.setAttribute("width", "100%");
        iframeNode.setAttribute("height", GLOBAL.length + "px");
        iframeNode.setAttribute("style", "height:" + GLOBAL.length + "px");
        iframeNode.setAttribute("frameborder", "0");
        iframeNode.srcdoc = html;
        let contentNode = createContainer("content-modal", [ headersNode, iframeNode ]);
        let modal = renderModal(contentNode);
        dragModel(modal);
        if (GM_getValue("hide")) {
            $("#model-id").hide();
            vm.hideTip();
        }
    }
    function renderModal(childElem, newPos) {
        return render("tag" + rand(1, 100).toString(), "model-id", childElem);
    }
    function render(tagName, elemId, childElem, isFixed, newPos) {
        let doc = top.document;
        let elem = doc.getElementById(elemId);
        if (elem) {
            elem.innerHTML = "";
        } else {
            elem = doc.createElement(tagName);
            elem.id = elemId;
            doc.body.appendChild(elem);
        }
        let contentNode = createContainer(tagName + "-container", childElem);
        elem.appendChild(contentNode);
        elem.classList.add(elemId);
        elem.style.zIndex = "9999999";
        elem.style.position = "fixed";
        const pos = GM_getValue("pos") === undefined ? "30px,30px" : GM_getValue("pos");
        const posarr = pos.split(",");
        elem.style.left = posarr[0];
        elem.style.top = posarr[1];
        setTimeout(function() {
            elem.classList.add(elemId + "-show");
        }, 10);
        return elem;
    }
    const QQ_GROUP = [ "622648517" ];
    var _self = unsafeWindow;
    var top = _self;
    try {
        reportOnline();
        if (GLOBAL.XiGua_PHPSESSION.length) {
            GLOBAL.answerApi["gk_xiguashuwang_com"] = GK_XiGua;
        }
        String.prototype.replaceAll = function(s1, s2) {
            return this.replace(new RegExp(s1, "gm"), s2);
        };
        window.onload = hookHTML;
        loadAdPng().then(r => {});
        while (top !== _self.top) {
            top = top.parent.document ? top.parent : _self.top;
            if (top.location.pathname === "/mycourse/studentstudy") break;
        }
    } catch (err) {
        console.log(err);
        top = _self;
    }
    var parent = _self === top ? self : _self.parent;
    _self.Ext || parent.Ext || {};
    var UE$1 = _self.UE;
    function checkVersion() {
        function compare(v1 = "0", v2 = "0") {
            v1 = String(v1).split(".");
            v2 = String(v2).split(".");
            const minVersionLens = Math.min(v1.length, v2.length);
            let result = 0;
            for (let i = 0; i < minVersionLens; i++) {
                const curV1 = Number(v1[i]);
                const curV2 = Number(v2[i]);
                if (curV1 > curV2) {
                    result = 1;
                    break;
                } else if (curV1 < curV2) {
                    result = -1;
                    break;
                }
            }
            if (result === 0 && v1.length !== v2.length) {
                const v1BiggerThenv2 = v1.length > v2.length;
                const maxLensVersion = v1BiggerThenv2 ? v1 : v2;
                for (let i = minVersionLens; i < maxLensVersion.length; i++) {
                    const curVersion = Number(maxLensVersion[i]);
                    if (curVersion > 0) {
                        v1BiggerThenv2 ? result = 1 : result = -1;
                        break;
                    }
                }
            }
            return result;
        }
        GM_xmlhttpRequest({
            method: "GET",
            url: "https://greasyfork.org/en/scripts/451356.json",
            timeout: GLOBAL.timeout,
            onload: function(r) {
                const obj = JSON.parse(r.responseText);
                if (obj.name === GM_info.script.name && compare(obj.version, GM_info.script.version) === 1) {
                    vm.updateTip(GM_info.script.version, obj.version, obj.url);
                }
            }
        });
    }
    top.addEventListener("message", event => {
        if (event.data.type === "jump") {
            GLOBAL.index++;
            iframeMsg("tip", {
                tip: "准备答第" + (GLOBAL.index + 1) + "题"
            });
        } else if (event.data.type === "stop") {
            GLOBAL.stop = event.data.val;
        } else if (event.data.type === "start_pay") {
            if (event.data.flag) {
                if (String(GM_getValue("token")).length === 10 || String(GM_getValue("token")).length === 11) {
                    iframeMsg("tip", {
                        tip: "已开启请求收费题库,已实时生效"
                    });
                    GM_setValue("start_pay", event.data.flag);
                    iframeMsg("start_pay", true);
                } else {
                    iframeMsg("tip", {
                        tip: "系统检测您的token可能输入有误,请检查"
                    });
                }
            } else {
                iframeMsg("tip", {
                    tip: "已关闭请求收费题库,已实时生效"
                });
                GM_setValue("start_pay", event.data.flag);
                iframeMsg("start_pay", false);
            }
        } else if (event.data.type === "auto_jump") {
            GM_setValue("auto_jump", event.data.flag);
            iframeMsg("tip", {
                tip: "已" + (event.data.flag ? "开启" : "关闭") + "自动切换,页面刷新后生效"
            });
        } else if (event.data.type === "confim") {
            if (event.data.token.length === 10 || event.data.token.length === 11) {
                GM_setValue("token", event.data.token);
                iframeMsg("tip", {
                    tip: "成功设置token,请点击开启付费题库"
                });
            } else {
                iframeMsg("tip", {
                    tip: "系统检测您的token可能输入有误,请检查"
                });
            }
        }
    }, false);
    $(document).keydown(function(event) {
        if (event.keyCode === 38) {
            $("#model-id").hide();
        } else if (event.keyCode === 40) {
            $("#model-id").show();
        } else if (event.keyCode === 37) {
            $("#model-id").hide();
            GM_setValue("hide", true);
        } else if (event.keyCode === 39) {
            $("#model-id").show();
            GM_setValue("hide", false);
            GM_setValue("pos", "50px,50px");
        } else if (event.keyCode === 83) {
            GLOBAL.stop = true;
            iframeMsg("stop", GLOBAL.stop);
        } else if (event.keyCode === 68) {
            GLOBAL.stop = false;
            iframeMsg("stop", GLOBAL.stop);
        }
    });
    function getAnswerForKey(keys, options) {
        return keys.map(function(val) {
            return options[val.charCodeAt(0) - 65];
        });
    }
    function setIntervalFunc(flag, func, time) {
        const interval = setInterval(() => {
            if (flag()) {
                clearInterval(interval);
                func();
            }
        }, time || 1e3);
    }
    function getAnswer(str, options, type) {
        if (type === 0 || type === 1) {
            const ans = getAnswerForKey(str.match(/[A-G]/gi) || [], options);
            return ans.length > 0 ? ans : [ str ];
        } else {
            return [ str ];
        }
    }
    function getQuestionType(str) {
        if (!str) return undefined;
        str = str.trim().replaceAll(/\s+/g, "");
        if (TYPE[str]) return TYPE[str];
        const regex = Object.keys(TYPE).join("|");
        const matcher = str.match(regex);
        if (matcher) return TYPE[matcher[0]];
        return undefined;
    }
    function rand(m, n) {
        return Math.ceil(Math.random() * (n - m + 1) + m - 1);
    }
    const TYPE = {
        "阅读理解(选择)/完型填空": 66,
        "听力训练": 66,
        multichoice: 1,
        singlechoice: 0,
        bijudgement: 3,
        "单项选择题": 0,
        "单项选择": 0,
        "单选题": 0,
        "单选": 0,
        "多选": 1,
        "多选题": 1,
        "案例分析": 1,
        "多项选择题": 1,
        "多项选择": 1,
        "客观题": 1,
        "填空题": 2,
        "填空": 2,
        "对错题": 3,
        "判断题": 3,
        "判断正误": 3,
        "判断": 3,
        "主观题": 4,
        "问答题": 4,
        "简答题": 4,
        "名词解释": 5,
        "论述题": 6,
        "计算题": 7,
        "其它": 8,
        "分录题": 9,
        "资料题": 10,
        "连线题": 11,
        "排序题": 13,
        "完形填空": 14,
        "完型填空": 14,
        "阅读理解": 15,
        "口语题": 18,
        "听力题": 19,
        "A1A2题": 1,
        "文件作答": 4,
        "视频题": 1
    };
    function sleep(time) {
        return new Promise(resolve => {
            setTimeout(resolve, time);
        });
    }
    function iframeMsg(type, message) {
        try {
            top.document.getElementById("iframeNode").contentWindow.vueDefinedProp(type, message);
        } catch (e) {}
    }
    function filterImg(dom) {
        if (location.host === "ncexam.cug.edu.cn") {
            String.prototype.trim = function() {
                return this.replace(/^\s+|\s+$/gm, "");
            };
        }
        return $(dom).clone().find("img[src]").replaceWith(function() {
            return $("
").text('
.attr()
');
        }).end().find("iframe[src]").replaceWith(function() {
            return $("
").text('