【粪仔】智慧树知道网课辅助[最新版]

【用于学习编程使用】

// ==UserScript==
// @name         【粪仔】智慧树知道网课辅助[最新版]
// @namespace    粪仔 & Shadow
// @version      3.9.0.2
// @description  【用于学习编程使用】
// @author       wyn665817 & Shadow
// @match        *://*.zhihuishu.com/*
// @match        *://*.zhihuishu.com/videoStudy*
// @match        *://*.zhihuishu.com/portals_h5*
// @match        *://*.zhihuishu.com/live*
// @match        *://*.zhihuishu.com/examh5*
// @match        *://*.zhihuishu.com/stuExamWeb*
// @connect      up.026wk.xyz
// @connect      aa.6hck.xyz
// @run-at       document-start
// @grant        unsafeWindow
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_getResourceText
// @grant        GM_setClipboard
// @grant        GM_setValue
// @grant        GM_getValue
// @license      MIT
// @resource css https://unpkg.com/bulma@0.9.4/css/bulma.min.css
// @resource css https://unpkg.com/bootstrap@3.3.7/dist/css/bootstrap.min.css
// @license      MIT
// @antifeature  ads
// ==/UserScript==
 

const qrCode = ``;

enableWebpackHook();

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

const config = {
    awaitTime: 5000,
    stopTimer: false,
    questionCount: 0,
    finishCount: 0,
    questionType: {
        '判断题': 10,
        '单选题': 20,
        '多选题': 25,
        '填空题': 30,
        '问答题': 40,
    },
    apiKey: GM_getValue('apiKey', '') // 获取保存的 API Key
};

function answerQuestion(questionBody, questionIndex) {
    const questionTitle = questionBody.querySelector('.subject_describe div,.smallStem_describe p').__Ivue__._data.shadowDom.textContent;
    appendToTable(questionTitle, "", questionIndex);
    const questionType = questionBody.querySelector(".subject_type").innerText.match(/【(.+)】|$/)[1];
    let type = config.questionType[questionType];
    type = type !== undefined ? type : -1;

    GM_xmlhttpRequest({
        method: "GET",
        url: `https://up.026wk.xyz/tkapi4.php?question=${encodeURIComponent(questionTitle)}${config.apiKey ? `&key=${config.apiKey}` : ''}`,
        onload: xhr => {
            const res = JSON.parse(xhr.responseText);
            const msg = res.msg;
            let answerString = res.answer;
            const displayMsg = res.msgs;
            // Get the msgs parameter from the API response
            updateMsg(displayMsg); // Update the floating window with the msgs content
            if (msg === "暂无答案") {
                answerString = "暂无答案";
                changeAnswerInTable(answerString, questionIndex, true);
            } else {
                const isSelect = chooseAnswer(type, questionBody, answerString);
                changeAnswerInTable(answerString, questionIndex, isSelect);
            }
            document.querySelectorAll('.switch-btn-box > button')[1].click();
        },
        onerror: err => console.log(err)
    });
}

function chooseAnswer(questionType, questionBody, answerString) {
    let isSelect = false;
    const answers = answerString.split(/[\u0001,#,=,===,;,---]+/).map(a => a.trim()).filter(Boolean);

    if (questionType === 10) { // 判断题
        const firstOptionText = questionBody.querySelector(".node_detail");
        const firstOption = questionBody.querySelector(".nodeLab");
        const correctAnswer = /(^|,)(正确|是|对|√|T|ri|right|true)(,|$)/.test(firstOptionText.innerText);
        const givenAnswer = /(^|,)(正确|是|对|√|T|ri|right|true)(,|$)/.test(answerString);
        (correctAnswer === givenAnswer ? firstOption : questionBody.querySelectorAll(".nodeLab")[1]).click();
        isSelect = true;
    }

    if (questionType === 20) { // 单选题
        const regexPattern = new RegExp("^(" + answerString + ")$");
        questionBody.querySelectorAll(".node_detail").forEach((option, i) => {
            if (regexPattern.test(option.innerText)) {
                questionBody.querySelectorAll(".nodeLab")[i].click();
                isSelect = true;
            }
        });
    }

    if (questionType === 25) { // 多选题
        questionBody.querySelectorAll(".node_detail").forEach((option, i) => {
            if (answers.includes(option.innerText.trim())) {
                questionBody.querySelectorAll(".nodeLab")[i].click();
                isSelect = true;
            }
        });
    }

    if (questionType === 30) { // 填空题
        const blanks = questionBody.querySelectorAll(".blankInput");
        answers.forEach((answer, i) => {
            if (answer) {
                blanks[i].value = answer;
                isSelect = true;
            }
        });
    }

    if (questionType === 40) { // 问答题
        const answerArea = questionBody.querySelector("textarea");
        if (answerArea) {
            answerArea.value = answerString;
            isSelect = true;
        }
    }
    return isSelect;
}

function appendToTable(questionTitle, answerString, questionIndex) {
    const table = document.querySelector("#record-table tbody");
    table.innerHTML += `<tr><td>${questionIndex}</td><td>${questionTitle}</td><td id="answer${questionIndex}">正在搜索...</td></tr>`;
}

function changeAnswerInTable(answerString, questionIndex, isSelect) {
    const answerCell = document.querySelector(`#answer${questionIndex}`);
    answerCell.innerHTML = answerString;
    if (answerString === "暂无答案") {
        answerCell.insertAdjacentHTML('beforeend', `<p style="color:red">扫码关注公众号防止失联反馈交流群348740961</p>`);
    }
    if (!isSelect) {
        answerCell.insertAdjacentHTML('beforeend', `<p style="color:red">未匹配答案,请根据搜索结果手动选择答案</p>`);
    }
}

function enableWebpackHook() {
    const originCall = Function.prototype.call;
    Function.prototype.call = function (...args) {
        const result = originCall.apply(this, args);
        if (args[2]?.default?.version === '2.5.2') {
            args[2]?.default?.mixin({
                mounted: function () {
                    this.$el['__Ivue__'] = this;
                }
            });
        }
        return result;
    }
}

function makeElementDraggable(el) {
    el.style.position = 'absolute';
    el.onmousedown = function (event) {
        let shiftX = event.clientX - el.getBoundingClientRect().left;
        let shiftY = event.clientY - el.getBoundingClientRect().top;

        function moveAt(pageX, pageY) {
            el.style.left = pageX - shiftX + 'px';
            el.style.top = pageY - shiftY + 'px';
        }

        function onMouseMove(event) {
            moveAt(event.pageX, event.pageY);
        }

        document.addEventListener('mousemove', onMouseMove);

        el.onmouseup = function () {
            document.removeEventListener('mousemove', onMouseMove);
            el.onmouseup = null;
        };
    };

    el.ondragstart = function () {
        return false;
    };
}

function updateMsg(msg) {
    document.getElementById('display-msg').innerText = msg;
}

function saveApiKey() {
    const apiKeyInput = document.getElementById('api-key-input');
    const newApiKey = apiKeyInput.value.trim();
    if (newApiKey) {
        GM_setValue('apiKey', newApiKey);
        config.apiKey = newApiKey; // Update the local config as well
    }
}

function clearApiKey() {
    GM_setValue('apiKey', '');
    config.apiKey = ''; // Clear the local config as well
    document.getElementById('api-key-input').value = ''; // Clear the input field
}

unsafeWindow.onload = (() => (async () => {
    GM_addStyle(GM_getResourceText("css"));
    const cssStyle = `
    <div id="floating-window" class="modal is-active" style="display: block;width: 420px; height: 550px;">
        <div class="modal-card" style="width: 100%; height: 100%;">
            <header class="modal-card-head">
                <p class="modal-card-title">智慧树小助手</p>
                <button class="delete" aria-label="close"></button>
            </header>
            <section class="modal-card-body">
                <div class="field">
                    <label class="label">API Key</label>
                    <div class="control">
                        <input class="input" id="api-key-input" type="text" placeholder="选择性请输入API Key" value="${config.apiKey}">
                        <button class="button is-primary" id="save-api-key-btn">保存</button>
                        <button class="button is-danger" id="clear-api-key-btn">清除</button>
                    </div>
                </div>
                <div id="qr-code" class="has-text-centered"></div>
                <div class="content has-text-centered">扫码关注公众号防止失联</div>
                <div id="display-msg" class="content"></div>
                <div class="content">
                    <table class="table is-bordered is-striped is-narrow" id="record-table">
                        <thead>
                            <tr>
                                <th>#</th>
                                <th>题目</th>
                                <th>答案</th>
                            </tr>
                        </thead>
                        <tbody></tbody>
                    </table>
                </div>
            </section>
        </div>
    </div>`;

    document.body.insertAdjacentHTML('beforeend', cssStyle);
    makeElementDraggable(document.getElementById('floating-window'));

    const img = new Image();
    img.src = "http://up.026wk.xyz/12.png";
    img.width = 200;
    img.height = 200;
    document.querySelector("#qr-code").appendChild(img);

    // Save API Key button event listener
    document.getElementById('save-api-key-btn').addEventListener('click', saveApiKey);
    // Clear API Key button event listener
    document.getElementById('clear-api-key-btn').addEventListener('click', clearApiKey);

    await sleep(config.awaitTime);

    const questionBodyAll = document.querySelectorAll(".examPaper_subject.mt20");
    if (questionBodyAll.length === 0) return;

    config.questionCount = questionBodyAll.length;

    answerQuestion(questionBodyAll[0], 1);
    let finishCount = 1;
    const interval = setInterval(() => {
        if (finishCount < questionBodyAll.length) {
            answerQuestion(questionBodyAll[finishCount], finishCount + 1);
            finishCount += 1;
        } else {
            clearInterval(interval);
        }
    }, 3000);
}))();