考试宝快捷键 (Kaoshibao Shortcuts)

考试宝刷题辅助:功能可独立开关 (音效/视觉反馈/箭头翻页/VIP破解/净化/智能回车),支持A-Z自定义按键,支持自定义音效

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

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

(I already have a user script manager, let me install it!)

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.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         考试宝快捷键 (Kaoshibao Shortcuts)
// @namespace    http://tampermonkey.net/
// @version      2.0
// @description  考试宝刷题辅助:功能可独立开关 (音效/视觉反馈/箭头翻页/VIP破解/净化/智能回车),支持A-Z自定义按键,支持自定义音效
// @match        *://*.kaoshibao.com/*
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // ==========================================
    // 1. 配置管理 & 初始化
    // ==========================================
    const generateDefaultKeys = () => {
        const keys = { submit: 'Enter', prev: 'ArrowLeft', next: 'ArrowRight', forceUnlock: 'v' };
        for (let i = 0; i < 26; i++) {
            const char = String.fromCharCode(65 + i);
            keys[`op_${char}`] = i < 5 ? (i + 1).toString() : '';
        }
        return keys;
    };

    const DEFAULT_CONFIG = {
        features: { cleanUI: true, vipUnlock: true, autoClose: true, smartEnter: true, scriptNav: true, audioFeedback: false, keyVisual: false },
        keys: generateDefaultKeys(),
        audioCustom: { correct: '', wrong: '' },
        uiPos: { top: '', left: '' }
    };

    let userConfig = JSON.parse(localStorage.getItem('ksb_script_config')) || DEFAULT_CONFIG;
    userConfig.features = { ...DEFAULT_CONFIG.features, ...userConfig.features };
    userConfig.keys = { ...DEFAULT_CONFIG.keys, ...userConfig.keys };
    userConfig.audioCustom = { ...DEFAULT_CONFIG.audioCustom, ...userConfig.audioCustom };
    userConfig.uiPos = userConfig.uiPos || { top: '', left: '' };

    const saveConfig = () => {
        localStorage.setItem('ksb_script_config', JSON.stringify(userConfig));
        if (confirm('设置已保存。是否立即刷新页面以确保所有更改生效?')) location.reload();
    };

    const resetConfig = () => {
        if (confirm('确定要恢复默认设置吗?')) {
            localStorage.removeItem('ksb_script_config');
            location.reload();
        }
    };

    // ==========================================
    // 2. 核心功能模块
    // ==========================================
    // --- 音效系统 ---
    const defaultAudioUrls = {
        correct: 'https://img.tukuppt.com/newpreview_music/01/66/41/63c0e76601774734.mp3',
        wrong: 'https://img.tukuppt.com/newpreview_music/09/00/60/5c89396f017e881994.mp3'
    };
    const audioCtx = { correct: new Audio(), wrong: new Audio() };

    function updateAudioSource() {
        audioCtx.correct.src = userConfig.audioCustom.correct || defaultAudioUrls.correct;
        audioCtx.wrong.src = userConfig.audioCustom.wrong || defaultAudioUrls.wrong;
    }
    updateAudioSource();

    function checkAnswerAndPlaySound() {
        if (!userConfig.features.audioFeedback) return;
        const wrongIcon = document.querySelector('img[src*="FkA2c88PrD8eR23UlL1ejyer5axl"]');
        const correctIcon = document.querySelector('img[src*="FjteOgY4lCD4RSWPILZpiI0tHLIt"]');
        if (correctIcon?.offsetParent) {
            audioCtx.correct.currentTime = 0;
            audioCtx.correct.play().catch(console.error);
        } else if (wrongIcon?.offsetParent) {
            audioCtx.wrong.currentTime = 0;
            audioCtx.wrong.play().catch(console.error);
        }
    }

    // --- 按键视觉反馈 ---
    function showKeyIndicator(text) {
        if (!userConfig.features.keyVisual) return;
        let div = document.getElementById('ksb-key-indicator');
        if (div) div.remove();
        div = document.createElement('div');
        div.id = 'ksb-key-indicator';
        div.textContent = text;
        div.style.cssText = `position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 80px; font-weight: bold; color: rgba(64, 158, 255, 0.8); z-index: 99999; pointer-events: none; text-shadow: 0 0 20px rgba(255,255,255,0.8); opacity: 0; transition: all 0.4s ease;`;
        document.body.appendChild(div);
        requestAnimationFrame(() => {
            div.style.opacity = '1';
            div.style.transform = 'translate(-50%, -50%) scale(1.2)';
        });
        setTimeout(() => {
            div.style.opacity = '0';
            div.style.transform = 'translate(-50%, -50%) scale(0.8)';
            setTimeout(() => div.remove(), 400);
        }, 300);
    }

    // --- 界面净化 (CSS) ---
    function applyCleanUI() {
        if (!userConfig.features.cleanUI) return;
        if (!document.getElementById('ksb-clean-style')) {
            const style = document.createElement('style');
            style.id = 'ksb-clean-style';
            style.textContent = `
                .header, .new-footer, .right-float-window, .advertisement, .ad-box, .vip-quanyi, .vip-tips, .breadcrumb, .lock-icon, [class*="lock"], [class*="vip-mask"], .hide-ai-analysis, .hide-ai-analysis-text, .check-all-btn-row { display: none !important; }
                .app-main { padding-top: 20px !important; }
                .answer-analysis, .answer-analysis-row, .answer-detail, .deepseek-row .content, .answer-box-detail p, .answer-box-detail span { color: #222 !important; opacity: 1 !important; filter: none !important; text-shadow: none !important; -webkit-text-fill-color: #222 !important; -webkit-line-clamp: 999 !important; line-clamp: 999 !important; max-height: none !important; height: auto !important; overflow: visible !important; white-space: pre-wrap !important; text-overflow: clip !important; user-select: text !important; }
                .answer-analysis-row, .answer-analysis { -webkit-box-orient: vertical !important; }
                [class*="mask"], [class*="blur"] { display: none !important; pointer-events: none !important; }
                .hide-height { height: auto !important; max-height: none !important; overflow: visible !important; }
            `;
            document.head.appendChild(style);
        }
    }

    // --- VIP 破解 (DOM操作) ---
    function unlockVIP() {
        if (!userConfig.features.vipUnlock) return;

        // 1. 常规移除
        ['.vip-quanyi', '.vip-tips', '.vip-mask', '.open-vip-btn', '[class*="pay"]', '.hide-ai-analysis', '.hide-ai-analysis-text', '.check-all-btn-row'].forEach(sel => {
            document.querySelectorAll(sel).forEach(el => el.remove());
        });

        // 2. 精准移除“开通VIP”
        const vipNodes = document.evaluate("//*[contains(text(), '开通VIP查看完整解析')]", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
        for (let i = 0; i < vipNodes.snapshotLength; i++) {
            let node = vipNodes.snapshotItem(i);
            let target = node.closest('button') || node.closest('.el-button') || node.closest('div[class*="vip"]') || node.closest('.check-all-btn-row') || node.parentElement;
            if (target && !target.classList.contains('app-main') && !target.classList.contains('answer-analysis')) target.remove();
            else node.remove();
        }

        // 3. 移除“深度解题”
        const deepResult = document.evaluate("//*[contains(text(), '深度解题')]", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
        for (let i = 0; i < deepResult.snapshotLength; i++) {
            let el = deepResult.snapshotItem(i);
            let wrapper = el.closest('.deepseek-row') || el.closest('.answer-box-detail > div') || el.parentElement;
            if (wrapper && !wrapper.classList.contains('app-main')) wrapper.remove();
        }

        // 4. 清理残留图标
        document.querySelectorAll('i, img, svg').forEach(icon => {
            const parentText = icon.parentElement?.innerText || '';
            if (parentText.includes('VIP') || parentText.includes('解析')) {
                const style = window.getComputedStyle(icon);
                if (style.position === 'absolute' || style.position === 'fixed') icon.remove();
            }
        });
    }

    // --- 自动弹窗处理 ---
    function checkDialog() {
        if (userConfig.features.autoClose) document.querySelector(".el-message-box__btns .el-button--primary")?.click();
    }

    // --- DOM 辅助 ---
    const clickByXPath = (xpath) => {
        const res = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
        for (let i = 0; i < res.snapshotLength; i++) {
            let el = res.snapshotItem(i);
            if (el.offsetParent !== null) { el.click(); return true; }
        }
        return false;
    };
    const clickText = (text) => clickByXPath(`//*[contains(text(), '${text}')]`);
    const selectOption = (char) => {
        if (!clickByXPath(`//*[normalize-space(text())='${char}']`)) {
            clickByXPath(`//*[starts-with(normalize-space(text()), '${char} ') or starts-with(normalize-space(text()), '${char}.')]`);
        }
    };

    // ==========================================
    // 3. 全局监听
    // ==========================================
    let observerTimer = null;
    const observer = new MutationObserver((mutations) => {
        if (mutations.some(m => m.addedNodes.length > 0)) {
            if (observerTimer) clearTimeout(observerTimer);
            observerTimer = setTimeout(() => {
                unlockVIP();
                applyCleanUI();
            }, 100); // 防抖,避免卡顿
        }
    });
    observer.observe(document.body, { childList: true, subtree: true });

    document.addEventListener('click', (e) => {
        if (userConfig.features.audioFeedback && (e.target.closest('.option') || e.target.textContent.includes('提交'))) {
            setTimeout(checkAnswerAndPlaySound, 200);
            setTimeout(checkAnswerAndPlaySound, 600);
        }
    });

    document.addEventListener('keydown', (e) => {
        if (['INPUT', 'TEXTAREA', 'SELECT'].includes(document.activeElement.tagName) || document.activeElement.isContentEditable) return;

        const k = e.key;
        const map = userConfig.keys;
        let isHandled = false;

        // 选项快捷键
        for (let i = 0; i < 26; i++) {
            const char = String.fromCharCode(65 + i);
            if (map[`op_${char}`] === k) { selectOption(char); isHandled = true; break; }
        }

        if (!isHandled) {
            if (k === map.submit) {
                isHandled = true;
                if (!userConfig.features.smartEnter) clickText('提交答案');
                else if (clickText('提交答案')) setTimeout(() => { unlockVIP(); setTimeout(unlockVIP, 150); }, 50);
                else if (!clickText('下一题')) clickText('交卷');
            } else if (k === map.prev && userConfig.features.scriptNav) {
                isHandled = true; showKeyIndicator('←'); clickText('上一题');
            } else if (k === map.next && userConfig.features.scriptNav) {
                isHandled = true; showKeyIndicator('→'); clickText('下一题');
            } else if (k === map.forceUnlock) {
                isHandled = true; unlockVIP(); applyCleanUI(); showKeyIndicator('🔓');
            }
        }

        if (isHandled) { e.stopPropagation(); e.stopImmediatePropagation(); e.preventDefault(); }
    }, true);

    // ==========================================
    // 4. 设置面板 UI
    // ==========================================
    function createSettingsUI() {
        if (document.getElementById('ksb-panel')) return;

        const style = document.createElement('style');
        style.textContent = `
            #ksb-btn { position: fixed; bottom: 20px; right: 20px; z-index: 9999; background: #409EFF; color: white; border-radius: 50%; width: 45px; height: 45px; text-align: center; line-height: 45px; cursor: move; box-shadow: 0 4px 15px rgba(0,0,0,0.3); font-size: 22px; transition: transform 0.2s; user-select: none; }
            #ksb-btn:hover { transform: scale(1.1); background: #66b1ff; }
            #ksb-panel { display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 550px; background: white; z-index: 10000; padding: 20px; border-radius: 10px; box-shadow: 0 10px 40px rgba(0,0,0,0.3); font-family: system-ui, sans-serif; max-height: 85vh; overflow-y: auto; color: #333; }
            .ksb-mask { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 9998; backdrop-filter: blur(2px); }
            .ksb-title { font-size: 20px; font-weight: bold; margin-bottom: 20px; padding-bottom: 10px; border-bottom: 1px solid #eee; }
            .ksb-sec-title { margin: 15px 0 10px; font-weight: bold; font-size: 14px; color: #409EFF; background: #ecf5ff; padding: 8px 10px; border-radius: 6px; }
            .ksb-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; padding: 6px 10px; border-radius: 4px; transition: background 0.2s; }
            .ksb-row:hover { background: #f9f9f9; }
            .ksb-checkbox { transform: scale(1.3); cursor: pointer; accent-color: #409EFF; }
            .ksb-input, .ksb-input-long { padding: 6px; border: 1px solid #dcdfe6; border-radius: 4px; outline: none; }
            .ksb-input { width: 100px; text-align: center; font-family: monospace; font-weight: bold; }
            .ksb-input-long { width: 250px; font-size: 12px; }
            .ksb-input:focus, .ksb-input-long:focus { border-color: #409EFF; box-shadow: 0 0 0 2px rgba(64,158,255,0.2); }
            .ksb-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
            .ksb-btns { margin-top: 25px; text-align: right; border-top: 1px solid #eee; padding-top: 15px; }
            .ksb-btn { padding: 8px 20px; border: none; border-radius: 4px; cursor: pointer; margin-left: 10px; font-size: 14px; }
            .ksb-save { background: #67C23A; color: white; }
            .ksb-close { background: #909399; color: white; }
            .ksb-sub-row { margin-left: 20px; border-left: 2px solid #eee; padding-left: 10px; font-size: 13px; color: #666; }
        `;
        document.head.appendChild(style);

        const btn = document.createElement('div'); btn.id = 'ksb-btn'; btn.innerHTML = '⚙️'; btn.title = '设置';
        if (userConfig.uiPos.top) Object.assign(btn.style, { bottom: 'auto', right: 'auto', top: userConfig.uiPos.top, left: userConfig.uiPos.left });

        let isDragging = false, startX, startY, initLeft, initTop;
        btn.onmousedown = (e) => {
            isDragging = false; startX = e.clientX; startY = e.clientY;
            const rect = btn.getBoundingClientRect(); initLeft = rect.left; initTop = rect.top;
            const onMove = (mv) => {
                if (!isDragging && (Math.abs(mv.clientX - startX) > 5 || Math.abs(mv.clientY - startY) > 5)) isDragging = true;
                if (isDragging) Object.assign(btn.style, { bottom: 'auto', right: 'auto', left: `${initLeft + mv.clientX - startX}px`, top: `${initTop + mv.clientY - startY}px` });
            };
            const onUp = () => {
                document.removeEventListener('mousemove', onMove); document.removeEventListener('mouseup', onUp);
                if (isDragging) { userConfig.uiPos = { top: btn.style.top, left: btn.style.left }; saveConfig(); }
            };
            document.addEventListener('mousemove', onMove); document.addEventListener('mouseup', onUp);
        };

        const mask = document.createElement('div'); mask.className = 'ksb-mask';
        const panel = document.createElement('div'); panel.id = 'ksb-panel';
        btn.onclick = () => { if (!isDragging) { panel.style.display = 'block'; mask.style.display = 'block'; } };

        const renderSwitch = (k, l) => `<div class="ksb-row"><label for="kf-${k}" style="flex:1;cursor:pointer;">${l}</label><input type="checkbox" id="kf-${k}" class="ksb-checkbox" ${userConfig.features[k]?'checked':''}></div>`;
        const renderKey = (k, n) => `<div class="ksb-row"><label>${n}</label><input type="text" class="ksb-input" id="kk-${k}" value="${userConfig.keys[k]}" readonly></div>`;

        let html = `<div class="ksb-title">考试宝助手 v2.0 设置</div><div class="ksb-sec-title">核心功能</div>${renderSwitch('smartEnter', '🧠 智能回车')}${renderSwitch('vipUnlock', '🔓 强力VIP破解')}${renderSwitch('cleanUI', '🧹 界面净化')}${renderSwitch('autoClose', '🚫 自动关弹窗')}${renderSwitch('scriptNav', '🎮 脚本翻页')}<div class="ksb-sec-title">增强体验</div>${renderSwitch('audioFeedback', '🎵 答题音效')}`;
        html += `<div id="ksb-audio-custom-wrapper" style="display:${userConfig.features.audioFeedback?'block':'none'};"><div class="ksb-row ksb-sub-row"><label>正确音效(URL)</label><input class="ksb-input-long" id="kac-correct" value="${userConfig.audioCustom.correct}"></div><div class="ksb-row ksb-sub-row"><label>错误音效(URL)</label><input class="ksb-input-long" id="kac-wrong" value="${userConfig.audioCustom.wrong}"></div></div>${renderSwitch('keyVisual', '👀 按键视觉反馈')}`;
        html += `<div class="ksb-sec-title">按键映射</div><div class="ksb-grid">${renderKey('submit', '提交/确认')}${renderKey('prev', '上一题')}${renderKey('next', '下一题')}${renderKey('forceUnlock', '强制破解')}</div><div class="ksb-sec-title">选项快捷键 (A-Z)</div><div class="ksb-grid">`;
        for (let i=0; i<26; i++) html += renderKey(`op_${String.fromCharCode(65+i)}`, `选项 ${String.fromCharCode(65+i)}`);
        html += `</div><div class="ksb-btns"><button class="ksb-btn" id="ksb-reset" style="float:left;background:#f56c6c;color:white;">重置</button><button class="ksb-btn ksb-close">取消</button><button class="ksb-btn ksb-save">保存配置</button></div>`;

        panel.innerHTML = html; document.body.append(btn, mask, panel);

        const close = () => { panel.style.display = 'none'; mask.style.display = 'none'; };
        mask.onclick = close; panel.querySelector('.ksb-close').onclick = close; panel.querySelector('#ksb-reset').onclick = resetConfig;

        document.getElementById('kf-audioFeedback')?.addEventListener('change', e => document.getElementById('ksb-audio-custom-wrapper').style.display = e.target.checked ? 'block' : 'none');
        panel.querySelector('.ksb-save').onclick = () => {
            Object.keys(userConfig.features).forEach(k => { const el = document.getElementById(`kf-${k}`); if (el) userConfig.features[k] = el.checked; });
            userConfig.audioCustom = { correct: document.getElementById('kac-correct').value.trim(), wrong: document.getElementById('kac-wrong').value.trim() };
            saveConfig();
        };

        panel.querySelectorAll('.ksb-input').forEach(inp => {
            inp.onfocus = () => { inp.style.borderColor = '#409EFF'; inp.value = '...'; };
            inp.onblur = () => { inp.style.borderColor = '#dcdfe6'; inp.value = userConfig.keys[inp.id.replace('kk-', '')] || ''; };
            inp.onkeydown = (e) => {
                e.preventDefault(); e.stopPropagation();
                let key = ['Backspace','Delete'].includes(e.key) ? '' : (e.key === ' ' ? 'Space' : e.key);
                userConfig.keys[inp.id.replace('kk-', '')] = key; inp.value = key; inp.blur();
            };
        });
    }

    setTimeout(() => { applyCleanUI(); createSettingsUI(); unlockVIP(); setInterval(unlockVIP, 1500); setInterval(checkDialog, 1500); }, 500);
})();