Greasy Fork

Greasy Fork is available in English.

hyw自动答题助手

hyw自动答题脚本

当前为 2024-11-07 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         hyw自动答题助手
// @namespace    http://tampermonkey.net/
// @version      0.3.7
// @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: 500px;
            right: 500px;
            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 {
            if (!currentQuestion) {
                console.log('当前题目为空');
                return null;
            }

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

            // 在题库中查找匹配的题目
            const matchedQuestions = questionBank.filter(item => {
                // 确保 item 和 item.question 都存在且不为空
                if (!item || !item.question) {
                    console.log('题库中存在无效题目:', item);
                    return false;
                }
                return item.question.includes(currentQuestion) ||
                    currentQuestion.includes(item.question);
            });
            console.log('匹配到的题目:', matchedQuestions);

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

            // 如果只有一个匹配项,验证必要属性后返回
            if (matchedQuestions.length === 1) {
                const question = matchedQuestions[0];
                if (!question.answer || !question.options) {
                    console.log('匹配题目缺少必要属性:', question);
                    return null;
                }
                return {
                    answer: question.answer,
                    type: question.type,
                    options: question.options
                };
            }

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

            for (const question of matchedQuestions) {
                // 确保题目包含必要属性
                if (!question.options) {
                    console.log('题目缺少选项:', question);
                    continue;
                }

                // 将题库中的选项按分隔符分割并清理
                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 => {
                        if (!bankOpt) return false;
                        // 完全匹配得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) {
                if (!bestMatch.answer || !bestMatch.options) {
                    console.log('最佳匹配题目缺少必要属性:', bestMatch);
                    return null;
                }
                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);

            // 同时处理 ^ 和 \n 分隔符
            const allOptions = answerInfo.options
                .split(/[\n^]/)  // 使用正则表达式同时匹配\n和^
                .map(opt => opt.trim())
                .filter(opt => opt); // 过滤掉空字符串

            console.log('allOptions', allOptions);

            if (isMultipleChoice) {
                // 多选题处理:答案可能是多个字母组合(如"ABC")
                const correctAnswers = answerInfo.answer.split('').map(letter => {
                    // 尝试在选项中找到以该字母开头的选项
                    const matchedOption = allOptions.find((opt, index) => {
                        // 如果选项没有字母前缀,则使用索引作为选项序号(A=0, B=1, 等)
                        if (!opt.match(/^[A-Z]/)) {
                            return index === (letter.charCodeAt(0) - 'A'.charCodeAt(0));
                        }
                        // 否则匹配选项前缀
                        return opt.startsWith(letter + '.') ||
                            opt.startsWith(letter + '.') ||
                            opt.startsWith(letter + '、') ||
                            opt.startsWith(letter + ' 、') ||
                            opt.match(new RegExp(`^${letter}\\s*[..、]`));
                    });
                    console.log('matchedOption', matchedOption);

                    return matchedOption;
                }).filter(Boolean);



                // 遍历页面上的选项
                options.forEach((option, index) => {
                    const optionText = option.textContent.trim();
                    // 检查当前选项是否是正确答案之一
                    const isCorrectOption = correctAnswers.some(correctAnswer => {
                        // 如果正确答案没有字母前缀,直接比较内容
                        const correctContent = correctAnswer.replace(/^[A-Z]\s*[..、]\s*/, '').trim();
                        const cleanOptionText = optionText.replace(/^[A-Z]\s*[..、]\s*/, '').trim();
                        return cleanOptionText === correctContent ||
                            cleanOptionText.includes(correctContent) ||
                            correctContent.includes(cleanOptionText);
                    });

                    const input = option.querySelector('input[type="checkbox"]');
                    if (input && isCorrectOption && !input.checked) {
                        input.click();
                        selected = true;
                    }
                });
            } else {
                // 单选题处理
                let correctAnswerContent;

                // 检查是否是标准的选项格式(A. B. C. 等)
                const hasStandardPrefix = allOptions.every(opt =>
                    opt.match(/^[A-Z][..、\s]/) || // 匹配 A. A. A、 A [空格]
                    opt.match(/^[A-Z][\s]*[..、]/) // 匹配 A  . A  . A  、
                );

                if (!hasStandardPrefix) {
                    // 如果不是标准格式,直接使用索引
                    const index = answerInfo.answer.charCodeAt(0) - 'A'.charCodeAt(0);
                    if (index >= 0 && index < allOptions.length) {
                        correctAnswerContent = allOptions[index];
                        console.log('根据索引找到的正确答案内容:', correctAnswerContent);
                    }
                } else {
                    // 如果是标准格式,查找匹配的选项
                    correctAnswerContent = allOptions.find(opt =>
                        opt.match(new RegExp(`^${answerInfo.answer}[..、\\s]`)) || // 匹配 A. A. A、 A [空格]
                        opt.match(new RegExp(`^${answerInfo.answer}\\s*[..、]`)) // 匹配 A  . A  . A  、
                    );
                }

                if (!correctAnswerContent) {
                    console.log('未找到正确答案内容');
                    return false;
                }

                // 清理选项内容时保留原始文本
                const cleanContent = (text) => {
                    // 如果文本以标准选项格式开头,才移除前缀
                    if (text.match(/^[A-Z][..、\s]/) || text.match(/^[A-Z][\s]*[..、]/)) {
                        return text.replace(/^[A-Z]\s*[..、]\s*/, '').trim();
                    }
                    return text.trim();
                };

                // 比较选项时使用清理后的内容
                options.forEach((option, index) => {
                    const optionText = option.textContent.trim();
                    const cleanOptionText = cleanContent(optionText);
                    const cleanCorrectContent = cleanContent(correctAnswerContent);

                    console.log(`比较选项 ${index + 1}:`, {
                        correctContent: cleanCorrectContent,
                        cleanOptionText: cleanOptionText,
                        isMatch: cleanOptionText === cleanCorrectContent
                    });

                    // 只在完全相等时选中选项
                    if (cleanOptionText === cleanCorrectContent) {
                        const input = option.querySelector('input[type="radio"]');
                        if (input && !input.checked) {
                            console.log('找到完全匹配选项,点击选择');
                            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);
    });
})();