Greasy Fork

Greasy Fork is available in English.

hyw自动答题助手

hyw自动答题脚本

目前为 2024-11-07 提交的版本。查看 最新版本

// ==UserScript==
// @name         hyw自动答题助手
// @namespace    http://tampermonkey.net/
// @version      0.3.6
// @description  hyw自动答题脚本
// @author       小马
// @license MIT
// @match        https://hyw.shixizhi.huawei.com/*
// @grant        GM_addStyle
// @grant        unsafeWindow
// @grant        GM_xmlhttpRequest
// @run-at       document-end
// @require      https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js
// ==/UserScript==

(function () {
    'use strict';

    let questionBank = [];

    // 添加面板样式
    GM_addStyle(`
        .answer-panel {
            position: fixed;
            top: 20px;
            right: 20px;
            background: white;
            padding: 15px;
            border: 1px solid #ccc;
            border-radius: 5px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            z-index: 2147483647;  /* 最大z-index值 */
            min-width: 200px;
            font-family: Arial, sans-serif;
            user-select: none;
            -webkit-user-select: none;
            -moz-user-select: none;
        }

        .answer-panel h3 {
            margin: 0 0 10px 0;
            padding: 0;
            font-size: 16px;
            color: #333;
        }

        .answer-panel select,
        .answer-panel input,
        .answer-panel button {
            margin: 5px 0;
            padding: 5px;
            width: 100%;
            box-sizing: border-box;
        }

        .answer-panel button {
            background: #007bff;
            color: white;
            border: none;
            border-radius: 3px;
            padding: 8px;
            margin: 5px 0;
            cursor: pointer;
        }

        .answer-panel button:hover {
            background: #0056b3;
        }

        #status {
            margin-top: 10px;
            color: #666;
            font-size: 14px;
            word-break: break-all;
        }

        /* 确保面板始终可见 */
        .answer-panel * {
            display: block;
            visibility: visible !important;
            opacity: 1 !important;
        }
    `);

    // 创建控制面板
    function createPanel() {
        try {
            // 先检查是否已存在面板
            const existingPanel = document.querySelector('.answer-panel');
            if (existingPanel) {
                existingPanel.remove();
            }

            const panel = document.createElement('div');
            panel.className = 'answer-panel';
            panel.innerHTML = `
                <div>
                    <h3>自动答题助手</h3>
                    <select id="examType">
                        <option value="security">保密考试</option>
                        <option value="functional">职能考试</option>
                    </select>
                    <input type="file" id="fileInput" accept=".xlsx,.xls">
                    <button id="startBtn">开始答题</button>
                    <button id="stopBtn">停止答题</button>
                    <div id="status">等待上传题库...</div>
                </div>
            `;

            // 确保面板被添加到 body 的最后
            document.body.appendChild(panel);

            // 添加拖拽相关变量
            let isDragging = false;
            let currentX;
            let currentY;
            let initialX;
            let initialY;
            let xOffset = 0;
            let yOffset = 0;

            // 拖拽开始
            function dragStart(e) {
                // 如果点击的是select、input或button元素,不启动拖拽
                if (e.target.tagName.toLowerCase() === 'select' ||
                    e.target.tagName.toLowerCase() === 'input' ||
                    e.target.tagName.toLowerCase() === 'button') {
                    return;
                }

                if (e.type === "mousedown") {
                    initialX = e.clientX - xOffset;
                    initialY = e.clientY - yOffset;
                } else if (e.type === "touchstart") {
                    initialX = e.touches[0].clientX - xOffset;
                    initialY = e.touches[0].clientY - yOffset;
                }

                if (e.target === panel || panel.contains(e.target)) {
                    isDragging = true;
                }
            }

            // 拖拽过程
            function drag(e) {
                if (isDragging) {
                    e.preventDefault();

                    if (e.type === "mousemove") {
                        currentX = e.clientX - initialX;
                        currentY = e.clientY - initialY;
                    } else if (e.type === "touchmove") {
                        currentX = e.touches[0].clientX - initialX;
                        currentY = e.touches[0].clientY - initialY;
                    }

                    xOffset = currentX;
                    yOffset = currentY;

                    setTranslate(currentX, currentY, panel);
                }
            }

            // 设置面板位置
            function setTranslate(xPos, yPos, el) {
                el.style.transform = `translate3d(${xPos}px, ${yPos}px, 0)`;
            }

            // 拖拽结束
            function dragEnd() {
                initialX = currentX;
                initialY = currentY;
                isDragging = false;
            }

            // 添加拖拽事件监听
            panel.addEventListener('mousedown', dragStart, false);
            document.addEventListener('mousemove', drag, false);
            document.addEventListener('mouseup', dragEnd, false);

            panel.addEventListener('touchstart', dragStart, false);
            document.addEventListener('touchmove', drag, false);
            document.addEventListener('touchend', dragEnd, false);

            // 阻止select的mousedown事件冒泡
            document.getElementById('examType').addEventListener('mousedown', (e) => {
                e.stopPropagation();
            });

            // 原有的事件绑定
            document.getElementById('fileInput').addEventListener('change', (e) => {
                const file = e.target.files[0];
                if (file) processExcel(file);
            });

            document.getElementById('startBtn').addEventListener('click', startAutoAnswer);
            document.getElementById('stopBtn').addEventListener('click', stopAutoAnswer);
        } catch (error) {
            console.error('创建控制面板失败:', error);
            // 可以尝试使用更简单的备用面板
            try {
                const simplePanel = document.createElement('div');
                simplePanel.className = 'answer-panel';
                simplePanel.innerHTML = `
                    <div>
                        <h3>自动答题助手(简易版)</h3>
                        <input type="file" id="fileInput" accept=".xlsx,.xls">
                        <button id="startBtn">开始答题</button>
                        <button id="stopBtn">停止答题</button>
                        <div id="status">等待上传题库...</div>
                    </div>
                `;
                document.body.appendChild(simplePanel);
            } catch (backupError) {
                console.error('创建备用面板也失败:', backupError);
            }
        }
    }

    // 更新状态显示
    function updateStatus(message) {
        document.getElementById('status').textContent = message;
    }

    let isRunning = false;

    // 停止自动答题
    function stopAutoAnswer() {
        isRunning = false;
        updateStatus('已停止答题');
    }

    // 开始自动答题
    async function startAutoAnswer() {
        if (questionBank.length === 0) {
            updateStatus('请先上传题库!');
            return;
        }

        isRunning = true;
        updateStatus('开始自动答题...');

        while (isRunning) {
            try {
                const questionInfo = getCurrentQuestionInfo();
                if (!questionInfo.question) {
                    updateStatus('未检测到题目,可能已完成答题');
                    isRunning = false;
                    break;
                }

                console.log('当前题目:', questionInfo.question);

                const answerInfo = findAnswer(questionInfo.question);
                if (answerInfo) {
                    const selected = selectAnswer(answerInfo, questionInfo.isMultipleChoice);
                    if (selected) {
                        updateStatus(`已答题: ${questionInfo.question.substring(0, 20)}...`);
                        // 减少答题后的等待时间为500ms
                        await new Promise(resolve => setTimeout(resolve, 200));

                        if (!clickNext(true)) {
                            updateStatus('无法找到下一题按钮,停止答题');
                            isRunning = false;
                            break;
                        }
                    } else {
                        updateStatus('答案选择失败');
                        if (!clickNext(false)) break;
                    }
                } else {
                    updateStatus('未找到匹配答案');
                    if (!clickNext(false)) break;
                }

                // 减少题目之间的等待时间为500ms
                await new Promise(resolve => setTimeout(resolve, 200));

            } catch (error) {
                console.error('答题过程出错:', error);
                updateStatus('答题过程出错,已停止');
                isRunning = false;
                break;
            }
        }
    }

    // 处理Excel文件上传
    async function handleFileUpload(e) {
        const file = e.target.files[0];
        const reader = new FileReader();

        reader.onload = function (e) {
            const data = new Uint8Array(e.target.result);
            const workbook = XLSX.read(data, { type: 'array' });
            const firstSheet = workbook.Sheets[workbook.SheetNames[0]];
            questionBank = XLSX.utils.sheet_to_json(firstSheet);
            document.getElementById('status').innerText = `已加载 ${questionBank.length} 道题目`;
        };

        reader.readAsArrayBuffer(file);
    }

    // 处理Excel数据结构
    function processExcel(file) {
        const reader = new FileReader();
        reader.onload = function (e) {
            const data = new Uint8Array(e.target.result);
            const workbook = XLSX.read(data, { type: 'array' });
            const firstSheet = workbook.Sheets[workbook.SheetNames[0]];
            const jsonData = XLSX.utils.sheet_to_json(firstSheet);

            // 获取当前选择的考试类型
            const examType = document.getElementById('examType').value;

            // 根据不同的考试类型处理数据
            if (examType === 'security') {
                // 保密考试题库格式
                questionBank = jsonData.map(row => ({
                    sequence: row['序号'],
                    type: row['试题类别'],
                    questionId: row['试题类型'],
                    question: row['试题题目'],
                    options: row['选项'],
                    answer: row['正确答案']
                }));
            } else if (examType === 'functional') {
                // 职能考试题格式
                questionBank = jsonData.map(row => ({
                    sequence: row['题库'],
                    type: row['题型'],
                    questionId: '',
                    question: row['题目'],
                    options: `${row['选项A']}\n${row['选项B']}\n${row['选项C']}\n${row['选项D']}\n${row['选项E'] || ''}\n${row['选项F'] || ''}\n${row['选项G'] || ''}\n${row['选项H'] || ''}`.trim(),
                    answer: row['正确答案']
                }));
            }

            updateStatus(`已导入 ${questionBank.length} 道题目`);
        };
        reader.readAsArrayBuffer(file);
    }

    // 查找答案
    function findAnswer(currentQuestion) {
        try {
            // 获取当前页面的所有选项文本
            const currentOptions = Array.from(document.querySelectorAll('.option-list-item'))
                .map(option => option.textContent.trim());
            console.log('当前页面选项:', currentOptions);

            // 在题库中查找匹配的题目
            const matchedQuestions = questionBank.filter(item =>
                item.question.includes(currentQuestion) ||
                currentQuestion.includes(item.question)
            );
            console.log('匹配到的题目:', matchedQuestions);

            if (matchedQuestions.length === 0) {
                return null;
            }

            // 如果只有一个匹配项,直接返回
            if (matchedQuestions.length === 1) {
                return {
                    answer: matchedQuestions[0].answer,
                    type: matchedQuestions[0].type,
                    options: matchedQuestions[0].options
                };
            }

            // 如果有多个匹配项,通过比对选项找到最匹配的题目
            let bestMatch = null;
            let highestMatchScore = 0;

            for (const question of matchedQuestions) {
                // 将题库中的选项按分隔符分割并清理
                const bankOptions = question.options.split(/[\n^]/)
                    .map(opt => opt.trim())
                    .filter(opt => opt)
                    .map(opt => opt.replace(/^[A-Z]\s*[..、]\s*/, '').trim()); // 移除选项标记

                // 计算选项匹配分数
                let matchScore = 0;
                let matchedOptionsCount = 0;

                // 对每个当前页面的选项进行匹配度计算
                for (const currentOpt of currentOptions) {
                    const cleanCurrentOpt = currentOpt.replace(/^[A-Z]\s*[..、]\s*/, '').trim();

                    // 在题库选项中寻找最佳匹配
                    const bestOptionMatch = bankOptions.find(bankOpt => {
                        // 完全匹配得3分
                        if (bankOpt === cleanCurrentOpt) {
                            return true;
                        }
                        // 包含关系得2分
                        if (bankOpt.includes(cleanCurrentOpt) || cleanCurrentOpt.includes(bankOpt)) {
                            return true;
                        }
                        // 部分词语匹配得1分
                        const bankWords = bankOpt.split(/\s+/);
                        const currentWords = cleanCurrentOpt.split(/\s+/);
                        return bankWords.some(word => currentWords.includes(word));
                    });

                    if (bestOptionMatch) {
                        matchedOptionsCount++;
                        if (bestOptionMatch === cleanCurrentOpt) {
                            matchScore += 3;
                        } else if (bestOptionMatch.includes(cleanCurrentOpt) || cleanCurrentOpt.includes(bestOptionMatch)) {
                            matchScore += 2;
                        } else {
                            matchScore += 1;
                        }
                    }
                }

                // 计算最终匹配分数(考虑匹配的选项数量和匹配质量)
                const finalScore = matchScore * (matchedOptionsCount / currentOptions.length);

                // 更新最佳匹配
                if (finalScore > highestMatchScore) {
                    highestMatchScore = finalScore;
                    bestMatch = question;
                }
            }

            // 如果找到了足够好的匹配(设置一个阈值)
            if (bestMatch && highestMatchScore >= currentOptions.length * 1.5) { // 阈值可以调整
                return {
                    answer: bestMatch.answer,
                    type: bestMatch.type,
                    options: bestMatch.options
                };
            }

            // 如果没有找到足够好的匹配,返回null
            return null;

        } catch (error) {
            console.error('查找答案时出错:', error);
            return null;
        }
    }

    // 获取当前题目信息
    function getCurrentQuestionInfo() {
        try {
            // 修改选择器以匹配实际DOM结构
            const questionElement = document.querySelector('.main-title .content');
            if (!questionElement) {
                console.log('未找到题目元素');
                return { question: '', isMultipleChoice: false };
            }

            const question = questionElement.textContent.trim();

            // 判断是否为多选题 - 检查题类型标签
            const typeElement = document.querySelector('.type-name');
            const isMultipleChoice = typeElement && typeElement.textContent.includes('多选题');

            return { question, isMultipleChoice };
        } catch (error) {
            console.error('获取题目信息出错:', error);
            return { question: '', isMultipleChoice: false };
        }
    }

    // 选择答案
    function selectAnswer(answerInfo, isMultipleChoice) {
        try {
            if (!answerInfo) return false;
            const options = document.querySelectorAll('.option-list-item');
            let selected = false;
            console.log('answerInfo:', answerInfo);

            // 使用 ^ 作为分隔符分割选项
            const allOptions = answerInfo.options.split('^').map(opt => opt.trim());
            console.log('allOptions', allOptions);

            if (isMultipleChoice) {
                // 多选题处理:答案可能是多个字母组合(如"ABC")
                const correctAnswers = answerInfo.answer.split('').map(letter => {
                    return allOptions.find(opt =>
                        opt.startsWith(letter + '.') || // 中文点号
                        opt.startsWith(letter + '.') || // 英文点号
                        opt.startsWith(letter + '、') || // 顿号
                        opt.startsWith(letter + ' 、') || // 空格+顿号
                        opt.match(new RegExp(`^${letter}\\s*[..、]`)) // 通用格式
                    );
                }).filter(Boolean); // 移除未找到的选项

                console.log('correctAnswers:', correctAnswers);

                // 遍历页面上的选项
                options.forEach((option) => {
                    const optionText = option.textContent.trim();
                    // 检查当前选项是否是正确答案之一
                    const isCorrectOption = correctAnswers.some(correctAnswer => {
                        const correctContent = correctAnswer.replace(/^[A-Z]\s*[..、]\s*/, '').trim();
                        return optionText.includes(correctContent) || correctContent.includes(optionText);
                    });

                    const input = option.querySelector('input[type="checkbox"]');
                    if (input && isCorrectOption && !input.checked) {
                        input.click();
                        selected = true;
                    }
                });
            } else {
                // 单选题处理(保持原有逻辑)
                const correctAnswerContent = allOptions.find(opt =>
                    opt.startsWith(answerInfo.answer + '.') ||
                    opt.startsWith(answerInfo.answer + '.') ||
                    opt.startsWith(answerInfo.answer + '、') ||
                    opt.startsWith(answerInfo.answer + ' 、') ||
                    opt.match(new RegExp(`^${answerInfo.answer}\\s*[..、]`))
                );

                if (!correctAnswerContent) return false;

                options.forEach((option) => {
                    const optionText = option.textContent.trim();
                    const correctContent = correctAnswerContent.replace(/^[A-Z]\s*[..、]\s*/, '').trim();

                    if (optionText.includes(correctContent) || correctContent.includes(optionText)) {
                        const input = option.querySelector('input[type="radio"]');
                        if (input && !input.checked) {
                            input.click();
                            selected = true;
                        }
                    }
                });
            }

            return selected;
        } catch (error) {
            console.error('选择答案出错:', error);
            return false;
        }
    }

    // 点击下一题
    function clickNext(answered) {
        try {
            // 获取下一题按钮
            const nextButton = Array.from(document.querySelectorAll('.subject-btns .subject-btn'))
                .find(button => button.textContent.trim() === '下一题');

            // 点击下一题按钮
            if (nextButton) {
                nextButton.click();
                return true;
            }
            return false;
        } catch (error) {
            console.error('点击下一题按钮出错:', error);
            return false;
        }
    }

    // 修改初始化部分
    function init() {
        // 等待页面完全加载
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', () => setTimeout(createPanel, 1000));
        } else {
            setTimeout(createPanel, 1000);
        }
    }

    // 使用 window.onload 确保所有资源都加载完成
    window.addEventListener('load', () => {
        setTimeout(init, 1000);
    });
})();