易班考试

暂时用不了

Από την 16/12/2024. Δείτε την τελευταία έκδοση.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==UserScript==
// @name         易班考试
// @namespace    http://tampermonkey.net/
// @license Common
// @version      1.9.0
// @description  暂时用不了
// @author       木木
// @match        *.yooc.me/*
// @match        *.yiban.cn/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=yooc.me
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function () {

    let originalPlay = HTMLVideoElement.prototype.play;
    HTMLVideoElement.prototype.play = function () {
        this.pause();
    };

    /*document.addEventListener("touchstart", function(event) {
        const x = event.clientX; // 点击位置的 X 坐标
        const y = event.clientY; // 点击位置的 Y 坐标
        alert(`${x},${y}`)
    });
    document.addEventListener("touchmove", function(event) {
        const x = event.clientX; // 点击位置的 X 坐标
        const y = event.clientY; // 点击位置的 Y 坐标
        alert(`${x},${y}`)
    });
    document.addEventListener("touchend", function(event) {
        const x = event.clientX; // 点击位置的 X 坐标
        const y = event.clientY; // 点击位置的 Y 坐标
        alert(`${x},${y}`)
    });*/

    function detectDevice() {
        const isMobileUserAgent = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
        const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;

        if (isMobileUserAgent && isTouchDevice) {
            return "mobile";
        } else {
            return "desktop";
        }
    }

    const deviceType = detectDevice();

    const meta = document.createElement('meta');
    meta.httpEquiv = 'Content-Security-Policy';
    meta.content = 'upgrade-insecure-requests';
    document.head.appendChild(meta);

    function simulateComplexClick(element, offsetX, offsetY) {
        let rect = element.getBoundingClientRect();
        let click_x = rect.left + offsetX;
        let click_y = rect.top + offsetY;

        ["mousedown", "mouseup", "click",/* "touchstart"*/].forEach((eventType) => {
            const event = new MouseEvent(eventType, {
                bubbles: true, cancelable: true, view: window, clientX: click_x, clientY: click_y,
            });
            element.dispatchEvent(event);
        });


    }

    let bg_url = '';
    window.simulateComplexClick = simulateComplexClick
    const interval = setInterval(() => {
        let element = document.querySelector('.shumei_captcha_loaded_img_bg');
        if (element) {
            if (element.src.indexOf('https') !== -1 && bg_url !== element.src) {
                clearInterval(interval);
                bg_url = element.src;
                fetch('https://124.222.110.105:5730/predict/' + "?bg_img=" + element.src, {
                    method: 'GET', mode: 'cors', headers: {
                        'Content-Type': 'application/json'
                    }
                }).then(r => {
                    if (r.ok) {
                        r.json().then(j => {
                            simulateComplexClick(document.querySelector('.shumei_captcha_img_wrapper'), j['min_area_center'][0] / 2, j['min_area_center'][1] / 2)
                        })
                    } else {
                        alert('请求失败,可能是服务器未放行,前往放行https://124.222.110.105:5730')
                    }
                })
            }

        }
    }, 100);


    let has_init = false;
    let tk;
    let id;
    let button_init = false;
    let user_data;

    //=========替换特殊字符防止匹配不上=======================
    window.my_replace = function my_replace(text) {
        text = text.replace(new RegExp(/ |	|\\t|\\r|\\n|<br>|<br\/>|&nbsp;|—|\s/g), "");
        return text;
    }

    const localStorage = window.localStorage;

    function set_value(key, value) {
        localStorage.setItem(key, value);
    }

    function get_value(key) {
        return localStorage.getItem(key);
    }

    // =========试卷的一些信息存储,包括易班id,cookie,考试名称===============
    user_data = JSON.parse(get_value('data') != null ? get_value('data') : '{}');

    function update_data(key, value) {
        user_data[key] = value;
        set_value('data', JSON.stringify(user_data));
    }

    if (window.location.href.endsWith('take')) {
        localStorage.clear();
    }

    // ==========获取token=============================
    let token = get_value("token");
    for (let i = 0; i < localStorage.length; i++) {
        let key = localStorage.key(i);
        if (key.startsWith('exam-paper')) {
            let value = localStorage[key];
            localStorage.removeItem(key);
            set_value('back-' + key, value);
        }
    }

    function get_token() {
        /*let t = prompt("请输入token(进群691977572免费获取)", token === null ? '' : token);
        if (t.length !== 16) {
            alert("长度不对");
            get_token();
        } else {
            token = t;
            set_value("token", token);
        }*/
    }

    const example = {
        382232: {
            exam_name: '', subjects: [{'题目': '答案'}, {'题目': '答案'}]
        },
    }


    /**
     * @fileoverview 提供文本(如JSON),使用JavaScript创建文件并下载文件
     * @author AcWrong
     * @param {string} textTowrite
     * @param {string} fileNameToSaveAs
     * @param {string} fileType
     */
    function saveTextAsFile(textTowrite, fileNameToSaveAs, fileType) {
        //提供文本和文件类型用于创建一个Blob对象
        let textFileAsBlob = new Blob([textTowrite], {type: fileType});
        //创建一个 a 元素
        let downloadLink = document.createElement('a');
        //指定下载过程中显示的文件名
        downloadLink.download = fileNameToSaveAs;
        downloadLink.innerHTML = 'Download File';
        downloadLink.href = window.URL.createObjectURL(textFileAsBlob);
        downloadLink.style.display = 'none';
        document.body.appendChild(downloadLink);
        //模式鼠标左键单击事件
        downloadLink.click();
    }

    // ============判断当前页面链接,如果是电脑版则跳转到手机版=====================


    let u = window.location.href;
    if (u === ('https://www.yooc.me/')) {
        window.location.replace('https://www.yooc.me/mobile/yooc')
    }
    // https://group.yooc.me/group/8399364/index
    // https://www.yooc.me/group/8399364/courses
    const match = u.match(/www\.yooc\.me\/group\/(\d+)\/courses/);
    if (match) {
        const groupId = match[1];
        const result = confirm("是否切换成手机页面进行考试");
        if (result) {
            window.location.replace(`https://group.yooc.me/group/${groupId}/index`)
        }
    }
    const exam_match = u.match(/www\.yooc\.me\/group\/(\d+)\/exams/);
    if (exam_match) {
        const groupId = exam_match[1];
        const result = confirm("是否切换成手机页面进行考试");
        if (result) {
            window.location.replace(`https://group.yooc.me/group/${groupId}/exams`)
        }
    }

    //正则匹配当前链接是否为exam.yooc.me/group/6540630/exams?
    const exams_reg = new RegExp(/exam.yooc.me\/group\/\d+\/exams\?user_data/);
    if (exams_reg.test(u)) {
        const token = localStorage.getItem('token');
        localStorage.clear()
        set_value('token', token);
    }
    if (u.indexOf('user_data=') !== -1) {
        const user_data_str = u.match('(?<=user_data=)\\w+')[0];
        const data = JSON.parse(decodeURIComponent(atob(user_data_str)));
        user_data = Object.assign(user_data, data);
        set_value('data', JSON.stringify(user_data));
    }
    /*if (u.indexOf('cookie') !== -1) {
        //获取cookie的值
        const base64_cookie = u.match('(?<=cookie=)\\w+')[0];
        const cookie = decodeURIComponent(atob(base64_cookie));
        //清空页面cookie并重新设置, cookie解密后的一个可能值为
        const cookie_obj = eval('(' + cookie + ')');
        for (const key in cookie_obj) {
            document.cookie = key + '=' + cookie_obj[key];
        }
        //去掉当前链接的cookie参数并重新加载当前链接
        window.location.replace(u.replace('cookie=' + base64_cookie, ''));
    }*/
    if (u.startsWith('https://www.yooc.me/group/') && u.endsWith('topics')) {
        window.location.replace(u.replace('www', 'group').replace('topics', 'index'));
    }
    // =================劫持fetch方法========================================
    // =================监听并获取一些数据,以便服务器实现解密等功能================
    // =================修改返回值以达到查卷,禁止乱序等功能======================

    const originOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function (_, url) {
        const xhr = this;
        const getter = Object.getOwnPropertyDescriptor(XMLHttpRequest.prototype, "response").get;
        Object.defineProperty(xhr, "responseText", {
            get: () => {
                let result = getter.call(xhr);
                try {
                    if (url.indexOf('api/group/info') !== -1) {
                        const json = JSON.parse(result);
                        let invite_code = json.data.code;
                        let group_name = json.data.name;
                        update_data('invite_code', invite_code);
                        update_data('group_name', group_name);
                        // let invite_code = result.match('(?<=code...)\\w+')[0];
                        let title = document.getElementsByClassName('title')[0];
                        title.innerText = title.innerText + '邀请码' + invite_code;
                    }
                    if (url.indexOf('api/group/module') !== -1) {
                        let js = JSON.parse(result);
                        for (let i = 0; i < js['data']['modules'].length; i++) {
                            if (js['data']['modules'][i]['module'].indexOf('exam') !== -1) {
                                const user_data_str = btoa(encodeURIComponent(JSON.stringify(user_data)));
                                js['data']['modules'][i]['url'] += '?user_data=' + encodeURI(user_data_str);

                                //
                            }
                        }
                        return JSON.stringify(js);
                    }
                    return result;
                } catch (e) {
                    return result;
                }
            },
        });
        originOpen.apply(this, arguments);
    };
    window.au_fetch = window.fetch;
    window.fetch = function (url) {

        if (url.indexOf('api') === -1) {
            return au_fetch(url);
        }
        if (url.indexOf("yibanId") !== -1) {
            id = url.match('(?<=yibanId\\=)\\d+')[0];
            update_data('yibanId', id);
        }
        if (url.indexOf("paper") !== false) {
            add_button();
        }
        return window.au_fetch.apply(window, arguments).then((response) => {
            const reader = response.body.getReader();
            const stream = new ReadableStream({
                start(controller) {
                    function push() {
                        // "done"是一个布尔型,"value"是一个Unit8Array
                        reader.read().then((e) => {
                            let {done, value} = e;
                            // 判断是否还有可读的数据?
                            let text = new TextDecoder("utf-8").decode(value);

                            if (done) {
                                // 告诉浏览器已经结束数据发送
                                controller.close();
                                return;
                            }
                            if (url.indexOf('api/exam/detail/get?userId') !== -1) {
                                let res_json = JSON.parse(text);
                                update_data('examId', res_json['data']['examId']);
                                update_data('examuserId', res_json['data']['examuserId']);
                                update_data('exam_name', res_json['data']['name']);
                            }
                            if (url.indexOf('/api/exam/setting/get') !== -1) {
                                let res_json = JSON.parse(text);
                                update_data('examuserId', res_json['data']['examuserId']);
                            }
                            // text = text.replace(new RegExp(/status":\d/g), 'status":2')
                            text = text.replace('"isHidePaper":1', '"isHidePaper":0');
                            text = text.replace('"isHideAnswer":1', '"isHideAnswer":0');
                            // text = text.replace('"isShowRank":0', '"isShowRank":1');
                            // text = text.replace('"isChoiceShuffle":1', '"isChoiceShuffle":0');
                            // text = text.replace('"isSubjectShuffle":1', '"isSubjectShuffle":0');
                            // console.log(JSON.parse(text));
                            controller.enqueue(new TextEncoder().encode(text));
                            push();
                        });
                    }

                    push();
                }
            });
            return new Response(stream, {headers: {"Content-Type": "text/html"}});
        });
    };

    function add_button() {
        if (!document.getElementsByClassName("jsx-3527395752 __ pa-xs flex items-center fs-l").length > 0) {
            setTimeout(function () {
                add_button();
            }, 1000);
            return;
        }
        if (token === null) {
            get_token();
            // return;
        }
        if (button_init) return;
        button_init = true;
        let bottom = document.getElementsByClassName("jsx-3527395752 __ pa-xs flex items-center fs-l")[0];
        let last = bottom.getElementsByClassName("jsx-3527395752 p")[0];
        let next = bottom.getElementsByClassName("jsx-3527395752 n")[0];
        let title_right = document.getElementsByClassName("jsx-372353390 right")[0];
        let bt = document.createElement("button");
        bt.innerText = "冲!";
        let main = document.getElementsByTagName("main")[0];
        let h3 = main.getElementsByTagName("h3")[0];
        let has_done = false;
        bt.onclick = function () {
            if (!window.location.href.endsWith('take')) {
                alert('貌似这里不是考试界面');
                return;
            }
            if (has_done) {
                const re = confirm('你已经点过了,如果没反应请稍等片刻(一般在十秒钟之内).重复点击会再次消耗,是否确定要再次消耗次数');
                if (!re) {
                    return;
                }
            }
            has_done = true;
            let res;
            // ===============在网页存储找到试卷并发送到服务器解密================
            for (let i = 0; i < localStorage.length; i++) {
                if (localStorage.key(i).startsWith("exam-paper")) {
                    let res_str = localStorage.getItem(localStorage.key(i));
                    res = JSON.parse(my_replace(res_str).replace(new RegExp(/<.*?>/g), "").replace(new RegExp(/&nbsp| |\s/g), ""));
                    break;
                }
            }
            if (res === undefined) {
                alert("找不到试卷,请刷新重试,或者检查是否开启缓存");
                has_done = false;
                return;
            }
            res['token'] = '';
            res['id'] = id;
            res['data'] = user_data;
            const httpRequest = new XMLHttpRequest();
            httpRequest.open('POST', 'https://124.222.110.105:5721', true);
            httpRequest.setRequestHeader("Content-type", "application/json");
            httpRequest.send(JSON.stringify(res));
            httpRequest.onreadystatechange = function () {
                if (httpRequest.readyState === 4 && httpRequest.status === 200) {
                    let result = JSON.parse(httpRequest.responseText);
                    const extracted_data = result['extracted_data'];
                    if (result['msg'] !== '成功请求') {
                        has_done = false;
                        if (result['msg'] === 'token不存在') {
                            get_token();
                        } else if (result['msg'] === '次数耗尽,请明天再试') {
                            alert('次数耗尽');
                            get_token();
                        } else {
                            alert(result['msg']);
                        }
                        return;
                    }
                    // save_paper(result);
                    tk = result['result'];
                    window.tk = tk;
                    // console.log(result);
                    let span = bottom.getElementsByTagName("span");
                    let count_text = span[0].innerText;
                    let count_list = count_text.split("/");//目前题目位置1/50,分割之后为["1","50"]
                    for (let i = 0; i < count_list[0]; i++) {
                        //点回第一题
                        last.click();
                    }
                    for (let i = 0; i < parseInt(count_list[1]); i++) {
                        let title_obj = h3.getElementsByTagName("div")[0];
                        let title = my_replace(title_obj.innerHTML);

                        let body = h3.parentElement.children[1].children[0];
                        // =========如果body是空则不是选择题=====================
                        if (body === undefined) {
                            title = title.replace(new RegExp(/<input.*?>/g), "{input}").replace(new RegExp(/<.*?>/g), "").replace(new RegExp(/&nbsp;/g), "").replace(new RegExp(/&nbsp| |\s/g), "");
                            let inputs = title_obj.getElementsByTagName('input');
                            const ans = tk[title];
                            for (let j = 0; j < inputs.length; j++) {
                                let answer = ans[j];
                                let ev = new Event('input', {bubbles: true});
                                ev.simulated = true;
                                inputs[j].value = (Array.isArray(answer) ? answer[0] : answer);
                                inputs[j].dispatchEvent(ev);
                            }
                            next.click();
                            continue;
                        }
                        //=====如果是填空题在上面的if已经continue了,所以下面的是选择题的代码====
                        const old_ans = tk[title];
                        if (old_ans === undefined) {
                            continue;
                        }
                        let ans = [];
                        for (const oldAn of old_ans) {
                            ans.push(my_replace(oldAn));
                        }
                        let ans_l = body.getElementsByTagName("li");
                        //不是未初始化,说明返回的数据中有这个题目

                        //单选题,答案是哪个就点哪个就可以了
                        if (body.className.indexOf('jsx-2160564469') !== -1) {
                            for (let j = 0; j < ans_l.length; j++) {
                                let an_str = my_replace(ans_l[j].children[1].innerHTML.slice(2));
                                if (ans.indexOf(an_str) !== -1) {
                                    ans_l[j].click();
                                }
                            }

                        } else if (body.className.indexOf('jsx-2550022912') !== -1) {//多选题需要判断有没有选对,没选对的取消勾选,对的没选的就选上
                            for (let j = 0; j < ans_l.length; j++) {
                                let an_str = my_replace(ans_l[j].children[1].innerHTML.slice(2));
                                if (ans.indexOf(an_str) !== -1 && ans_l[j].children[0].children[0].childElementCount === 2) {
                                    ans_l[j].click();
                                } else if (ans.indexOf(an_str) === -1 && ans_l[j].children[0].children[0].childElementCount === 1) {
                                    ans_l[j].click();
                                }

                            }
                        }


                        next.click();
                    }
                    alert('虽然很难,但还是出来了。请检查后等待一段时间再交卷以免因为时长问题被发现,如需同时进行多个考试进群924240731了解');
                } else if (httpRequest.readyState === 4) {
                    if (httpRequest.status === 0) {
                        alert('在解密过程中,向服务器发送原始试卷失败,可能是被浏览器拦截或者服务器故障,如果你是苹果设备请更换设备后再试。');
                    } else {
                        alert('发生错误,请进群691977572联系作者。服务器数据:' + httpRequest.responseText);
                    }

                }
            }
        };
        title_right.appendChild(bt);
        /*let bt3 = document.createElement('button');
        bt3.innerText = '重输token';
        bt3.onclick = get_token;

        title_right.appendChild(bt3);*/
    }

    function goToUrl(url) {
        var div = document.createElement('div');
        div.style.position = 'fixed';
        div.style.top = '50%';
        div.style.left = '50%';
        div.style.transform = 'translate(-50%,-50%)';
        div.style.color = 'blue';
        div.style.background = 'white';
        div.style.padding = '10px';
        div.style.border = '1px solid blue';
        div.style.borderRadius = '10px';
        div.style.width = '200px';
        div.style.display = 'flex';
        div.style.flexDirection = 'column';
        var content = document.createElement('div');
        content.innerText = '无法连接服务器,可能是服务器被墙了,是否前往放行?在拦截界面点击高级,点击继续访问。然后回到此页面刷新。';
        div.appendChild(content);
        var buttonContainer = document.createElement('div');
        buttonContainer.style.display = 'flex';
        div.appendChild(buttonContainer);
        var cancelButton = document.createElement('button');
        cancelButton.style.color = 'blue';
        cancelButton.style.background = 'white';
        cancelButton.style.border = '1px solid blue';
        cancelButton.style.borderRadius = '5px';
        cancelButton.style.flexGrow = '1';
        cancelButton.innerText = '取消';
        cancelButton.onclick = function () {
            document.body.removeChild(div);
        };
        buttonContainer.appendChild(cancelButton);
        var confirmButton = document.createElement('button');
        confirmButton.style.color = 'blue';
        confirmButton.style.background = 'white';
        confirmButton.style.border = '1px solid blue';
        confirmButton.style.borderRadius = '5px';
        confirmButton.style.flexGrow = '1';
        confirmButton.innerText = '确认';
        confirmButton.onclick = function () {
            window.location.href = url;
        };
        buttonContainer.appendChild(confirmButton);
        document.body.appendChild(div);
    }

    window.onload = function () {
        //网址是否匹配https://www.yooc.me/mobile/courses/YOOC/CC5389/20230328/courseware
        if (u.indexOf('courseware') !== -1) {
            var ajaxUrl = $('#ajax_url').val();
            var csrfToken = $('#csrf_token').val();
            data = {
                "saved_video_position": "00:00:01", "video_duration": "00:00:02", 'done': true
            };
            data['csrfmiddlewaretoken'] = csrfToken;
            $.ajax({
                url: ajaxUrl, type: 'POST', data: data
            });
            var div = document.createElement('div');
            div.style.position = 'fixed';
            div.style.top = '50%';
            div.style.left = '50%';
            div.style.transform = 'translate(-50%,-50%)';
            div.style.color = 'white';
            div.style.background = 'black';
            div.style.padding = '10px';
            div.innerText = '尝试跳过视频,刷新查看结果';
            document.body.appendChild(div);
            setTimeout(function () {
                document.body.removeChild(div);
            }, 1000);
            let ctx = document.getElementsByClassName('ctx-container')[0];
            let lis = ctx.getElementsByTagName('li')
            for (let index = 0; index < lis.length; index++) {
                const li = lis[index];
                if (li.className === 'ac' && lis.length > index) {
                    window.location.replace(lis[index + 1].children[0].href)
                }

            }
        }
        if (u.indexOf('login') !== -1) {
            eval('$("#yooc_submit").on("click" ,function (event) {\n' + '    auto_login();' + '  })')
        }
    };
    if (!has_init) {
        if (window.location.href.indexOf('yooc.me') !== -1) {
            const httpRequest = new XMLHttpRequest();
            httpRequest.open('GET', 'https://124.222.110.105:5721/', true);
            httpRequest.send();
            httpRequest.onreadystatechange = function () {
                if (httpRequest.readyState === 4 && httpRequest.status === 0) {
                    has_init = true;
                    // alert('无法连接服务器,可能被拦截或者服务器down了,请关闭网络代理,如果是苹果请更换安卓或Windows设备,如果是电脑端请访问https://124.222.110.105:5721/,在拦截界面点击高级,点击继续访问。然后回到此页面刷新。');
                    goToUrl("https://124.222.110.105:5721/")
                }
            }
        }
    }


})();