unipus iTEST助手

自动翻译文章题目材料, 解析听力材料无限听, 解除切屏限制, 解除右键菜单与划词限制

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         unipus iTEST助手
// @namespace    http://blog.z31.xyz/
// @version      4.0.9
// @description  自动翻译文章题目材料, 解析听力材料无限听, 解除切屏限制, 解除右键菜单与划词限制
// @author       simonkimi
// @match        *://*/*
// @grant        none
// @run-at       document-body
// @require      https://cdn.staticfile.org/blueimp-md5/2.16.0/js/md5.min.js
// @require      https://bowercdn.net/c/jquery-1.11.1-1.11.1/dist/jquery.min.js
// ==/UserScript==


(async () => {
    const SETTING = {
        appid: "20200604000485252",  // 左边20200604000485252替换自己申请的APPID
        key: "pNz3PfHcz_65fwPV_SYh",  // 左边pNz3PfHcz_65fwPV_SYh替换自己申请的key

        listenTest: 1,  // 听力开关
        singleSelect: 1,  // 单选题翻译开关
        select10from15: 1, // 15选10开关
        article: 1,  // 文章翻译开关
        articleChoice: 1, // 文章选项翻译开关
        disableSelect: 1,  // 解除复制粘贴限制开关
        translateHelper: 1,  // 翻译助手开关
        AJAX: $.ajax,  // 提前储存AJAX
        FETCH: window.fetch  // 储存fetch
    }
    const NOTHING_HAPPENED = () => {}

    const RANDOM_CLASS = {  // 对所有的随机化处理
        BTN: getRandomString(getRandomInt(3, 10)),
        BTN_P: getRandomString(getRandomInt(3, 10)),
        BTN_G: getRandomString(getRandomInt(3, 10)),
        TRANSLATE_TEXT: getRandomString(getRandomInt(3, 10)),

        FLOAT_BTN: getRandomString(getRandomInt(3, 10)),
        FLOAT_CONTAINER: getRandomString(getRandomInt(3, 10)),
        FLOAT_FORM: getRandomString(getRandomInt(3, 10)),
        FLOAT_FORM_ITEM: getRandomString(getRandomInt(3, 10)),
        FLOAT_FORM_OR: getRandomString(getRandomInt(3, 10)),
        FLOAT_FORM_TR: getRandomString(getRandomInt(3, 10))
    }

    fuckAJAX();

    await delay(5000);
    if (isItest()) {
        initCSS();
        debug();
        fuckITEST();
        SETTING.disableSelect === 1 ? enableSelect() : null;
        SETTING.singleSelect === 1 ? singleSelect() : null;
        SETTING.select10from15 === 1 ? select10from15() : null;
        SETTING.article === 1 ? article() : null;
        SETTING.articleChoice === 1 ? articleChoice() : null;
        SETTING.listenTest === 1 ? listenTest() : null;
    } else {
        window.fetch = SETTING.FETCH
        fetch = SETTING.FETCH
    }

    function isItest() {
        try {
            ITEST
            return true;
        } catch (e) {
            return false
        }
    }


    function fuckITEST() {
        setInterval(function () {
            window.onblur = NOTHING_HAPPENED;
            window.onfocus = NOTHING_HAPPENED;
        }, 5 * 1000)
    }

    function fuckAJAX() {
        window.fetch = () => new Promise((resolve) => {resolve()})
        fetch = () => new Promise((resolve) => {resolve()})
        Object.defineProperty($, 'ajax', {
            get: () => (obj) => {
                const data = JSON.parse(obj.data)
                const allow = data.filter(value => {
                    const action = value.action
                    return ['pairwork_exam', 'res_download_start', 'next_ques_click', 'pre_ques_click',
                        'ans_submit', 'exam_end', 'ans_card_click', 'ans_auto_submit', 'exam_begin', 'res_download_end',
                        'down_audio_error'].includes(action);
                })
                if (allow.length === 1 && data.length === 1) {
                    console.log("上报信息成功")
                    SETTING.AJAX(obj)
                } else {
                    console.log("上报信息不在白名单内, 已被阻断!")
                    obj.success()
                }

            },
            set: () => {console.log("无事发生")}
        })
    }


    function initCSS() {
        $('body').append(`
        <style>
            * {
                -moz-user-select: text !important;
            }
            .${RANDOM_CLASS.BTN} {
                font-size: 8px;
                color: #fff;
                padding: 3px 3px;
                border-radius: 20px;
                cursor: pointer;
                opacity: 0.1;
                width: 25px;
            }
            .${RANDOM_CLASS.BTN_P} {
                background-color: rgb(248,248,248);
                border: 1px solid #000000;
                color: #000000;
            }
            .${RANDOM_CLASS.BTN_P}:hover {
                background-color: rgb(248,248,248);
                border: 1px solid rgb(248,248,248);
            }
            .${RANDOM_CLASS.BTN_G} {
                background-color: #909399;
                border: 1px solid #909399;
            }
            .${RANDOM_CLASS.BTN_G}:hover {
                background-color: rgb(166, 169, 173);
                border: 1px solid rgb(166, 169, 173);
            }
            .${RANDOM_CLASS.TRANSLATE_TEXT} {
                color: #909399;
            }
            ::selection {
                background-color: rgb(248,248,248);
                color: #909399;
            }
        </style>
        `)
    }


    function getRandomString(len = 10) {
        const str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
        const maxPos = str.length;
        let pwd = '';
        for (let i = 0; i < len; i++) {
            pwd += str.charAt(Math.floor(Math.random() * maxPos));
        }
        return pwd;
    }

    function getRandomInt(minNum, maxNum) {
        return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
    }


    /**
     * async风格的延迟
     * @param time
     * @returns {Promise<unknown>}
     */
    async function delay(time) {
        return new Promise(resolve => {
            setTimeout(resolve, time)
        })
    }

    function getBaiduAPIKey() {
        return {appid: SETTING.appid, key: SETTING.key}
    }


    /**
     * 是否为本地网页, 解除本地网页限制
     */
    function debug() {
        if (window.location.href.indexOf("localhost") !== -1) {
            $("head").append(`<script> src="https://cdn.staticfile.org/blueimp-md5/2.16.0/js/md5.min.js"</script>`)
            $('.goup').removeClass('dis').on('click', function () {
                $('.itest-ques-set').each(function () {
                    const $this = $(this);
                    if ($this.css('display') === 'block' && $this.parent().css('display') === 'block') {
                        if ($this.prev().is('.itest-ques-set')) {
                            $this.prev().css('display', 'block')
                            $this.css('display', 'none')
                        } else if ($this.parent().prev().is('.itest-section')) {
                            $this.parent().prev().css('display', 'block')
                            $this.parent().css('display', 'none')
                        }
                        return false;
                    }

                })
            })
            $('.goto').removeClass('dis').on('click', function () {
                $('.itest-ques-set').each(function () {
                    const $this = $(this);
                    if ($this.css('display') === 'block' && $this.parent().css('display') === 'block') {
                        if ($this.next().is('.itest-ques-set')) {
                            $this.next().css('display', 'block')
                            $this.css('display', 'none')
                        } else if ($this.parent().next().is('.itest-section')) {
                            $this.parent().next().css('display', 'block')
                            $($this.parent().next().find(".itest-ques-set")[0]).css('display', 'block')
                            $this.parent().css('display', 'none')
                        }
                        return false;
                    }
                })
            })
        }
    }

    /**
     * 解除选择复制粘贴限制
     */
    function enableSelect() {
        function hackClass(className) {
            for (const i of document.getElementsByClassName(className)) {
                hackItem(i);
            }
        }

        function hackItem(item) {
            item.onpaste = () => true;
            item.oncontextmenu = () => true;
            item.onselectstart = () => true;
            item.ondragstart = () => true;
            item.oncopy = () => true;
            item.onbeforecopy = () => true;
            Object.defineProperty(item, 'onpaste', {get: () => (event) => false})
            Object.defineProperty(item, 'oncontextmenu', {get: () => (event) => false})
            Object.defineProperty(item, 'onselectstart', {get: () => (event) => false})
            Object.defineProperty(item, 'ondragstart', {get: () => (event) => false})
            Object.defineProperty(item, 'oncopy', {get: () => (event) => false})
            Object.defineProperty(item, 'onbeforecopy', {get: () => (event) => false})
        }

        hackClass('itest-ques');
        hackClass('itest-direction');
        hackItem(document.body);
        hackItem(document);
    }

    /**
     * 单选题注入
     */
    function singleSelect() {
        const SINGLE = getRandomString(getRandomInt(5, 10));
        $('.row').each(function () {
            if ($(this).find('.option').length !== 0) {
                $(this).prepend(`<div class="${RANDOM_CLASS.BTN} ${RANDOM_CLASS.BTN_P} ${SINGLE}">${getRandomString(4)}</div>`)
            }
        })

        $(`.${SINGLE}`).on('click', function () {
            if ($(this).is(`.${RANDOM_CLASS.BTN_P}`)) {
                $(this).parent().find('span').each(function () {
                    $(this).remove();
                })
                const $this = $(this).parent();
                const $head = $this.children('.option-head')
                // 题目
                const head = $head.text().replace("\n", '')
                translateAPI(head, 'en', 'zh').then(value => {
                    $head.append(`<span class="${RANDOM_CLASS.TRANSLATE_TEXT}"><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;${value[0].dst}</span>`)
                })
                // 题干
                const selectOption = [];
                $this.children('.option').each(function () {
                    selectOption.push($(this).find('label'))
                })
                const p = selectOption.map(value => value.text().replace('\n', '')).join('\n');
                translateAPI(p, 'en', 'zh').then(value => {
                    for (let i = 0; i < value.length; i++) {
                        selectOption[i].append(`<span class="${RANDOM_CLASS.TRANSLATE_TEXT}"><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;${value[i].dst}</span>`)
                    }
                    $(this).removeClass(RANDOM_CLASS.BTN_P).addClass(RANDOM_CLASS.BTN_G);
                })
            } else {
                $(this).removeClass(RANDOM_CLASS.BTN_G).addClass(RANDOM_CLASS.BTN_P);
                $(this).parent().find('span').each(function () {
                    $(this).remove();
                })
            }
        })
    }

    function select10from15() {
        const SELECT10_ART = getRandomString(getRandomInt(5, 10));
        $('.itest-15xuan10>.xxcontent').each(function () {
            $(this).prepend(`<a class="${RANDOM_CLASS.BTN} ${RANDOM_CLASS.BTN_P} ${SELECT10_ART}" >${getRandomString(4)}</a>`)
        })
        $(`.${SELECT10_ART}`).on('click', function () {
            // 文章翻译
            $(this).parent().find('span').each(function () {
                $(this).remove();
            })
            const $button = $(this);
            if ($(this).is(`.${RANDOM_CLASS.BTN_P}`)) {
                const $passage = $(this).next();
                const passage = $passage.text().replace(/\s+/g, ' ');
                translateAPI(passage, 'en', 'zh').then(value => {
                    value.forEach(i => {
                        $passage.append(`<span class="${RANDOM_CLASS.TRANSLATE_TEXT}"><br><br>${i.dst}</span>`)
                    })
                    $button.removeClass(RANDOM_CLASS.BTN_P).addClass(RANDOM_CLASS.BTN_G);
                })
            } else {
                $(this).removeClass(RANDOM_CLASS.BTN_G).addClass(RANDOM_CLASS.BTN_P);
                $(this).parent().find('span').each(function () {
                    $(this).remove();
                })
            }

        })

        const SELECT10_WORD = getRandomString(getRandomInt(5, 10));
        $('.itest-15xuan10>.xx').each(function () {
            $(this).prepend(`<a class="${RANDOM_CLASS.BTN} ${RANDOM_CLASS.BTN_P} ${SELECT10_WORD}" style="float: left">${getRandomString(4)}</a>`)
        })

        $(`.${SELECT10_WORD}`).on('click', function () {
            // 单词翻译
            $(this).parent().find('span').each(function () {
                $(this).remove();
            })
            const $button = $(this);
            if ($(this).is(`.${RANDOM_CLASS.BTN_P}`)) {
                const $words = $(this).parent().find('a')
                const wordList = [];
                $words.each(function () {
                    wordList.push($(this));
                })
                const data = wordList.map(value => value.text().replace(/\s+/g, ' ')).join('\n');
                translateAPI(data, 'en', 'zh').then(value => {
                    for (let i = 0; i < value.length; i++) {
                        wordList[i].append(`<span class="${RANDOM_CLASS.TRANSLATE_TEXT}"><br/>${value[i].dst}</span>`)
                    }
                    $button.removeClass(RANDOM_CLASS.BTN_P).addClass(RANDOM_CLASS.BTN_G);
                })
            } else {
                $(this).removeClass(RANDOM_CLASS.BTN_G).addClass(RANDOM_CLASS.BTN_P);
                $(this).parent().find('span').each(function () {
                    $(this).remove();
                })
            }


        })
    }

    function article() {
        const ART = getRandomString(getRandomInt(5, 10));
        $('.con-left>.article').each(function () {
            $(this).prepend(`<a class="${RANDOM_CLASS.BTN} ${RANDOM_CLASS.BTN_P} ${ART}" >${getRandomString(4)}</a>`)
        })
        $(`.${ART}`).on('click', function () {
            const $button = $(this);
            if ($(this).is(`.${RANDOM_CLASS.BTN_P}`)) {
                $(this).parent().find('span').each(function () {
                    $(this).remove();
                })
                const $article = $(this).parent().find("p, div");
                const outerData = []

                $article.each(function () {
                    $(this).children('br:even').remove();
                    $(this).append('<br>')
                    const $brs = [];
                    $(this).children('br').each(function () {
                        $brs.push($(this));
                    })
                    const text = $(this).html()
                        .split('<br>')
                        .map(value => value.trim().replace(/\s+/g, ' ').replace('&nbsp;', ''))
                        .filter(value => value.length !== 0);
                    outerData.push({
                        $brs,
                        text
                    })
                });

                (async () => {
                    for (let {text, $brs} of outerData) {
                        for (let i = 0; i < text.length; i++) {
                            const data = await translateAPI(text[i], 'en', 'zh')
                            $brs[i].before(`<span class="${RANDOM_CLASS.TRANSLATE_TEXT}"><br/>${data[0].dst}</span>`)
                            await delay(1000);
                        }
                        $button.removeClass(`${RANDOM_CLASS.BTN_P}`).addClass(`${RANDOM_CLASS.BTN_G}`);
                    }
                })()

            } else {
                $(this).removeClass(`${RANDOM_CLASS.BTN_G}`).addClass(`${RANDOM_CLASS.BTN_P}`).html(getRandomString(4));
                $(this).parent().find('span').each(function () {
                    $(this).remove();
                })
            }
        })
    }


    function articleChoice() {
        const ART_CHO = getRandomString(getRandomInt(5, 10));
        $('.itest-need-layout').each(function () {
            if ($(this).find('span').length !== 0) {
                $(this).prepend(`<a class="${RANDOM_CLASS.BTN} ${RANDOM_CLASS.BTN_P} ${ART_CHO}" >${getRandomString(4)}</a>`)
            }
        })

        $(`.${ART_CHO}`).on('click', function () {
            if ($(this).is(`.${RANDOM_CLASS.BTN_P}`)) {
                const $button = $(this);
                const $spanList = []
                $button.parent().find('span').each(function () {
                    $spanList.push($(this))
                })
                const text = $spanList.map(value => value.text())
                    .map(value => value.trim().replace(/\s+/g, ' ').replace('&nbsp;', ''))
                    .join('\n');
                translateAPI(text, 'en', 'zh').then(value => {
                    for (let i = 0; i < value.length; i++) {
                        $spanList[i].append(`<label class="${RANDOM_CLASS.TRANSLATE_TEXT}"><br/>${value[i].dst}</label>`)
                    }
                    $button.removeClass(`${RANDOM_CLASS.BTN_P}`).addClass(`${RANDOM_CLASS.BTN_G}`);
                })
            } else {
                $(this).removeClass(`${RANDOM_CLASS.BTN_G}`).addClass(`${RANDOM_CLASS.BTN_P}`);
                $(this).parent().find('label').each(function () {
                    $(this).remove();
                })
            }


        })
    }

    /**
     * 封装的翻译api
     * @param obj
     * @returns {Promise<unknown>}
     */
    async function translateAjaxApi(obj) {
        return new Promise((resolve, reject) => {
            SETTING.AJAX({
                type: "post",
                async: false,
                url: "https://api.fanyi.baidu.com/api/trans/vip/translate",
                dataType: "jsonp",
                data: obj,
                success(data) {
                    resolve(data)
                },
                error() {
                    reject();
                }
            });
        })
    }


    /**
     * 调用翻译api
     * @param context_list
     * @param from
     * @param to
     * @returns {Promise<unknown>}
     */
    async function translateAPI(context_list, from, to) {
        const trans_salt = (new Date).getTime();
        const {appid, key} = getBaiduAPIKey()
        const trans_str = appid + context_list + trans_salt + key;
        const trans_sign = md5(trans_str);

        for (let i = 0; i < 5; i++) {
            const data = await translateAjaxApi({
                q: context_list,
                from,
                to,
                appid: appid,
                salt: trans_salt,
                sign: trans_sign
            })
            if (data.error_code) {
                const errmsg = {
                    "52001": "请求超时",
                    "52002": "系统错误",
                    "52003": "未授权用户",
                    "54003": "访问频率受限",
                    "54004": "账户余额不足",
                    "58002": "服务当前已关闭",
                    "90107": "认证未通过或未生效"
                }
                if (parseInt(data.error_code) !== 54003) {


                    if (errmsg[data.error_code.toString()]) {
                        alert(`iTEST助手:翻译错误代码${data.error_code} 信息:${errmsg[data.error_code.toString()]}`)
                    } else {
                        alert(`iTEST助手:翻译错误代码${data.error_code} 信息未知, 您的API一定出了问题!!`)
                    }
                    throw new Error(`翻译:错误代码${data.error_code}`)

                } else {
                    await delay(1000);
                    continue
                }
            }
            return data.trans_result;
        }
        alert("公共翻译接口不堪重负, 您可以更换自己api!")
        throw new Error("公共翻译接口不堪重负, 您可以更换自己api!");
    }

    function listenTest() {
        const LISTEN = getRandomString(getRandomInt(5, 10))

        $('.itest-hear-reslist-duration+.itest-ques').each(function () {
            $(this).children('.itest-danxuan').prepend(`<a class="${RANDOM_CLASS.BTN} ${RANDOM_CLASS.BTN_P} ${LISTEN}">${getRandomString(4)}</a>`)
        })

        $(`.${LISTEN}`).on('click', function () {
            const $button = $(this)
            if ($button.is(`.${RANDOM_CLASS.BTN_P}`)) {
                const $urlNode = $button.parent().parent().parent().find('.itest-hear-reslist');
                if ($urlNode.length !== 0) {
                    const mp3List = JSON.parse($urlNode.html());
                    for (const url of mp3List) {
                        if (url.indexOf('http') !== -1) {
                            $button.parent().append(`<audio style="opacity: 0.1;" controls="controls" src=${url}>`)
                        }
                    }
                }
                $button.removeClass(RANDOM_CLASS.BTN_P).addClass(RANDOM_CLASS.BTN_G)
            } else {
                $button.parent().find("audio").remove();
                $button.removeClass(RANDOM_CLASS.BTN_G).addClass(RANDOM_CLASS.BTN_P)
            }
        })
    }
})()