Greasy Fork

Greasy Fork is available in English.

优学院答题助手 V14.9

首次运行提示配置API | 自定义AI接口 | 双核驱动 | 全自动刷课 | 智能避让

当前为 2025-11-26 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         优学院答题助手 V14.9
// @namespace    https://thewinds.me/
// @version      14.9
// @description  首次运行提示配置API | 自定义AI接口 | 双核驱动 | 全自动刷课 | 智能避让
// @author       Winds
// @license      CC-BY-NC-4.0
// @match        *://*.ulearning.cn/*
// @connect      homeworkapi.ulearning.cn
// @connect      workers.dev
// @connect      *
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

/*
 * Author: Winds (https://thewinds.me)
 * Ref: 墨青 (https://blog.blackcyan.top/)
 * * 【版权声明】
 * 本脚本采用 CC-BY-NC-4.0 协议进行授权。
 * 您可以自由使用、修改和分发本脚本,但【严禁用于任何商业用途】。
 * 禁止将本脚本打包出售、植入广告或用于其他盈利性行为。
 */

(function() {
    'use strict';

    // ================= 配置区域 =================
    // 从油猴存储中读取用户配置的 URL,默认为空
    const USER_API_URL = GM_getValue('UL_AI_URL', '');

    const CONFIG = {
        // 动态获取 API 地址
        get aiBaseUrl() { return USER_API_URL; },

 interval: 2000,

 selectors: {
     listContainer: ".table-homework",
     writeBtn: ".item-operation .button-red-solid",
     nextPageBtn: ".pagination-wrap .next",
     questionContainer: ".question-choice, .question-gap-filling, .question-short-answer",
     titleContext: ".title",
     question: ".question-title",
     textInput: ".ul-textarea__inner",
     choiceItem: ".choice-item",
     choiceText: ".choice-title",
     choiceIndex: ".index",
     choiceClickArea: ".ul-radio, .ul-checkbox",
     judgeTrueIcon: ".icon-zhengque",
     judgeFalseIcon: ".icon-cuowu1",
     checkedSelector: ".is-checked, .is-active, input:checked",
     startQuizBtn: ".ul-button--primary",
     bottomBarNum: ".number .answered",
     submitBtn: ".ul-button--primary",
     modalConfirmBtn: ".ul-message-box .ul-button--primary",
     headerBackBtn: ".header-back, .goback, .icon-fanhui",
     resultPageMarker: ".homework-result-report, .score-panel",
     itemName: ".item-name",
     checkboxClass: ".ul-checkbox"
 }
    };

    let API_ANSWERS = {};
    let HAS_FETCHED_API = false;

    // ================= 首次运行检查 =================
    function checkFirstRun() {
        if (!GM_getValue('UL_HAS_INIT', false)) {
            const tips = "🎉 欢迎使用优学院答题助手 V14.9!\n\n" +
            "请输入您的 AI API 地址:\n" +
            "👉 如果填入:脚本将使用 AI 回答填空题和主观题。\n" +
            "👉 如果留空:脚本将【自动跳过】所有主观题,只做客观题。\n\n" +
            "(您之后可以点击面板上的 ⚙️ 按钮随时修改)";

            const input = prompt(tips, "");
            if (input !== null) {
                GM_setValue('UL_AI_URL', input.trim());
                GM_setValue('UL_HAS_INIT', true);
                location.reload(); // 刷新以生效
            }
        }
    }

    // ================= UI 样式 =================
    GM_addStyle(`
    #ai-panel {
    position: fixed; top: 20px; right: 20px; width: 320px; height: 420px;
    background: #fff; box-shadow: 0 4px 20px rgba(0,0,0,0.15);
    border-radius: 12px; z-index: 99999; font-family: sans-serif;
    border: 1px solid #ebeef5; display: flex; flex-direction: column;
    transition: all 0.3s; overflow: hidden;
    }
    #ai-header {
    padding: 12px 15px; background: #2c3e50; color: white; /* 深色 V14.9 */
    height: 44px; box-sizing: border-box; font-weight: 600;
    display: flex; justify-content: space-between; align-items: center; cursor: move;
    }
    #ai-content { padding: 15px; overflow-y: auto; flex-grow: 1; display: flex; flex-direction: column; }

    /* 链接样式 */
    .author-link { color: #2c3e50; text-decoration: none; font-weight: bold; transition: color 0.2s; }
    .author-link:hover { color: #e74c3c; text-decoration: underline; }
    .ref-link { color: #95a5a6; text-decoration: none; transition: color 0.2s; }
    .ref-link:hover { color: #333; text-decoration: underline; }

    .mode-badge { display:inline-block; padding:2px 6px; border-radius:4px; font-size:12px; color:white; margin-bottom:5px; }
    .badge-api { background: #27ae60; }
    .badge-ai { background: #2980b9; }
    .ai-btn {
        background: rgba(255,255,255,0.2); border: 1px solid rgba(255,255,255,0.6);
        color: white; padding: 4px 10px; border-radius: 4px; cursor: pointer; font-size: 12px; margin-left: 5px;
    }
    .ai-btn:hover { background: rgba(255,255,255,0.3); }
    .reasoning { color: #666; font-style: italic; background: #f8f9fa; padding: 8px; margin-bottom: 10px; font-size: 12px; border-left: 3px solid #ddd; }
    .answer { color: #333; font-weight: 600; white-space: pre-wrap; }
    .current-q { border: 2px solid #2c3e50 !important; box-shadow: 0 0 10px rgba(44,62,80,0.2); }

    #ai-panel.minimized {
    width: 50px !important; height: 50px !important;
    border-radius: 50%; border: 2px solid #fff;
    box-shadow: 0 4px 15px rgba(0,0,0,0.3);
    cursor: pointer; background-color: #2c3e50;
    }
    #ai-panel.minimized #ai-content, #ai-panel.minimized #ai-header { opacity: 0; pointer-events: none; }
    #ai-panel.minimized::after {
    content: "⚙️"; font-size: 24px; position: absolute;
    top: 50%; left: 50%; transform: translate(-50%, -50%);
    color: white; pointer-events: none;
    }
    `);

    // ================= 状态管理 =================
    const isListPage = () => document.querySelector(CONFIG.selectors.listContainer) !== null;
    const isResultPage = () => location.href.includes("stuReport") || document.querySelector(CONFIG.selectors.resultPageMarker) !== null;
    const isPotentialQuizPage = () => !isListPage() && !isResultPage() && (
        document.querySelectorAll(CONFIG.selectors.questionContainer).length > 0 ||
        document.querySelector(CONFIG.selectors.startQuizBtn) !== null
    );

    let isRunning = false;
    let isPaused = false;
    let questionsList = [];
    let currentIndex = 0;

    // ================= 核心:API 答案获取 =================
    function getIdsFromUrl() {
        const url = location.href;
        const ocIdMatch = url.match(/ocId=(\d+)/);
        const homeworkIdMatch = url.match(/homeworkId=(\d+)/);
        if (ocIdMatch && homeworkIdMatch) return { ocId: ocIdMatch[1], homeworkId: homeworkIdMatch[1] };
        return null;
    }

    function getToken() {
        const match = document.cookie.match(/token=([^;]+)/);
        return match ? match[1] : localStorage.getItem("token");
    }

    function fetchStandardAnswers(callback) {
        const ids = getIdsFromUrl();
        if (!ids) { if(callback) callback(false); return; }
        const token = getToken();
        if (!token) { if(callback) callback(false); return; }

        const apiUrl = `https://homeworkapi.ulearning.cn/quiz/homework/stu/questions?homeworkId=${ids.homeworkId}&ocId=${ids.ocId}&showAnswer=true`;
        const statusEl = document.querySelector('#status-text');
        if(statusEl) statusEl.innerHTML = "📡 正在抓取标准答案...";

        GM_xmlhttpRequest({
            method: "GET",
            url: apiUrl,
            headers: { "Authorization": token, "User-Agent": navigator.userAgent },
            onload: function(response) {
                try {
                    const json = JSON.parse(response.responseText);
                    if (json.result) {
                        API_ANSWERS = {};
                        json.result.forEach((q, index) => {
                            if(q.id) API_ANSWERS[q.id] = q.correctAnswer;
                            API_ANSWERS[`INDEX_${index}`] = q.correctAnswer;
                        });
                        HAS_FETCHED_API = true;
                        if(statusEl) statusEl.innerHTML = `✅ 成功获取 ${json.result.length} 题答案`;
                        if(callback) callback(true);
                    } else {
                        if(callback) callback(false);
                    }
                } catch (e) { if(callback) callback(false); }
            },
            onerror: () => { if(callback) callback(false); }
        });
    }

    // ================= UI 创建 =================
    function createUI() {
        if (document.getElementById('ai-panel')) return;

        // 检查是否配置了 API
        const hasApiConfig = !!GM_getValue('UL_AI_URL', '');
        const apiStatus = hasApiConfig ? "✅ AI已配置" : "⚪ 仅客观题模式";

        const panel = document.createElement('div');
        panel.id = 'ai-panel';
        panel.innerHTML = `
        <div id="ai-header">
        <span>🤖 优学院 V14.9</span>
        <div>
        <button class="ai-btn" id="btn-settings" title="设置 API 地址">⚙️</button>
        <button class="ai-btn" id="btn-pause" style="display:none;">⏸</button>
        <button class="ai-btn" id="btn-stop" style="display:none; background:#e74c3c;">⏹</button>
        <button class="ai-btn" id="btn-action">▶ 启动</button>
        <button class="ai-btn" id="btn-minimize">❌</button>
        </div>
        </div>
        <div id="ai-content">
        <div style="font-size:12px; color:#7f8c8d; margin-bottom:10px;">当前状态: ${apiStatus}</div>
        <div id="status-text" style="color:#666; text-align:center; margin-top:5px;">等待操作...</div>
        <div id="ai-log" style="margin-top:15px; flex-grow:1;"></div>

        <div style="margin-top:20px; padding-top:10px; border-top:1px dashed #eee; font-size:12px; color:#999; text-align:center; line-height:1.6;">
        Author: <a href="https://thewinds.me" target="_blank" class="author-link">Winds</a>
        <br>
        Ref: <a href="https://blog.blackcyan.top/" target="_blank" class="ref-link">墨青</a>
        </div>
        </div>
        `;
        document.body.appendChild(panel);

        const actionBtn = panel.querySelector('#btn-action');
        const pauseBtn = panel.querySelector('#btn-pause');
        const stopBtn = panel.querySelector('#btn-stop');
        const minimizeBtn = panel.querySelector('#btn-minimize');
        const settingsBtn = panel.querySelector('#btn-settings');

        // 最小化
        minimizeBtn.onclick = (e) => { e.stopPropagation(); panel.classList.add('minimized'); };
        let isDragAction = false;
        panel.addEventListener('mousedown', () => { isDragAction = false; });
        panel.addEventListener('mousemove', () => { isDragAction = true; });
        panel.addEventListener('click', () => { if (panel.classList.contains('minimized') && !isDragAction) panel.classList.remove('minimized'); });

        // 设置按钮
        settingsBtn.onclick = () => {
            const current = GM_getValue('UL_AI_URL', '');
            const newUrl = prompt("配置 AI API 地址:\n留空则只做客观题。", current);
            if (newUrl !== null) {
                GM_setValue('UL_AI_URL', newUrl.trim());
                location.reload();
            }
        };

        pauseBtn.onclick = togglePause;
        stopBtn.onclick = stopQueue;

        if (isListPage()) {
            GM_setValue('UL_LIST_URL', window.location.href);
            actionBtn.innerText = "▶ 队列";
            actionBtn.onclick = startListQueue;
            if (GM_getValue('UL_QUEUE_MODE', false)) {
                actionBtn.style.display = 'none';
                stopBtn.style.display = 'inline-block';
                setTimeout(processListPage, 2000);
            }
        } else if (isResultPage()) {
            document.querySelector('#status-text').innerText = "✅ 作业已完成";
            actionBtn.innerText = "↩️ 返回";
            actionBtn.style.background = "#27ae60";
            actionBtn.onclick = goBackToList;
            if (GM_getValue('UL_QUEUE_MODE', false)) {
                document.querySelector('#status-text').innerText = "3秒后自动返回列表...";
                setTimeout(goBackToList, 3000);
            }
        } else if (isPotentialQuizPage()) {
            actionBtn.innerText = "▶ 答题";
            actionBtn.onclick = () => startQuiz(false);
            setTimeout(() => startQuiz(true), 1500);
        }

        const header = panel.querySelector('#ai-header');
        let isDragging = false, startX, startY, initLeft, initTop;
        header.onmousedown = (e) => { isDragging = true; startX = e.clientX; startY = e.clientY; const rect = panel.getBoundingClientRect(); initLeft = rect.left; initTop = rect.top; };
        document.onmousemove = (e) => { if(isDragging) { panel.style.left = (initLeft + e.clientX - startX) + 'px'; panel.style.top = (initTop + e.clientY - startY) + 'px'; } };
        document.onmouseup = () => isDragging = false;
    }

    // ================= 返航逻辑 =================
    function goBackToList() {
        const savedUrl = GM_getValue('UL_LIST_URL');
        if (savedUrl && savedUrl.includes('ulearning')) {
            window.location.href = savedUrl;
            return;
        }
        const backBtn = document.querySelector(CONFIG.selectors.headerBackBtn);
        if (backBtn) { backBtn.click(); return; }
        if (document.referrer && document.referrer.includes('ulearning')) {
            window.location.href = document.referrer;
            return;
        }
        window.location.href = "https://www.ulearning.cn/amooc/user/student/homework";
    }

    // ================= 暂停/停止控制 =================
    function togglePause() {
        isPaused = !isPaused;
        const btn = document.querySelector('#btn-pause');
        const status = document.querySelector('#status-text');
        if (isPaused) {
            btn.innerText = "▶"; btn.style.background = "#27ae60";
            status.innerHTML = `<span style="color:#f39c12">⏸ 已暂停</span>`;
        } else {
            btn.innerText = "⏸"; btn.style.background = "";
            status.innerHTML = "⚡ 运行中...";
            if (isListPage()) processListPage();
            else processNextQuizItem();
        }
    }

    function stopQueue() {
        isRunning = false; isPaused = false;
        GM_setValue('UL_QUEUE_MODE', false);
        document.querySelector('#status-text').innerText = "❌ 已完全停止";
        document.querySelector('#btn-action').style.display = 'inline-block';
        document.querySelector('#btn-stop').style.display = 'none';
        document.querySelector('#btn-pause').style.display = 'none';
        if (isListPage()) {
            const actionBtn = document.querySelector('#btn-action');
            actionBtn.innerText = "▶ 队列"; actionBtn.style.display = 'inline-block'; actionBtn.onclick = startListQueue;
        }
    }

    // ================= 队列管理 (列表页) =================
    function startListQueue() {
        GM_setValue('UL_LIST_URL', window.location.href);
        GM_setValue('UL_QUEUE_MODE', true);
        location.reload();
    }

    function processListPage() {
        if (!isListPage() || !GM_getValue('UL_QUEUE_MODE', false)) return;
        if (isPaused) return;

        document.querySelector('#btn-action').style.display = 'none';
        document.querySelector('#btn-stop').style.display = 'inline-block';
        document.querySelector('#btn-pause').style.display = 'inline-block';
        GM_setValue('UL_LIST_URL', window.location.href);

        const logDiv = document.querySelector('#ai-log');
        if(logDiv) logDiv.innerHTML = '<div class="reasoning">正在扫描作业列表...</div>';

        const btns = Array.from(document.querySelectorAll(CONFIG.selectors.writeBtn));

        const todoBtns = btns.filter(btn => {
            const txt = btn.innerText;
            if (!txt.includes("写作业") && !txt.includes("继续")) return false;

            // 智能避让逻辑:无AI时跳过主观题
            if (!CONFIG.aiBaseUrl) {
                const row = btn.closest('.tr') || btn.closest('li');
                const titleEl = row ? row.querySelector(CONFIG.selectors.itemName) : null;
                if (titleEl && titleEl.innerText.includes("主观题")) {
                    if(logDiv) logDiv.innerHTML += `<div class="reasoning" style="color:#999">🚫 无AI配置,跳过: ${titleEl.innerText}</div>`;
                    return false;
                }
            }
            return true;
        });

        if (todoBtns.length > 0) {
            document.querySelector('#status-text').innerText = `发现 ${todoBtns.length} 个可做作业...`;
            setTimeout(() => { if (!isPaused) todoBtns[0].click(); }, 3000);
        } else {
            const nextBtn = document.querySelector(CONFIG.selectors.nextPageBtn);
            if (nextBtn && !nextBtn.classList.contains('disabled') && nextBtn.style.display !== 'none') {
                document.querySelector('#status-text').innerText = "翻页中...";
                setTimeout(() => {
                    if(!isPaused) { nextBtn.click(); setTimeout(processListPage, 3000); }
                }, 2000);
            } else {
                document.querySelector('#status-text').innerText = "🎉 全部完成!";
                stopQueue();
            }
        }
    }

    // ================= 自动提交检测 =================
    function checkAndSubmit() {
        const progressEl = document.querySelector(CONFIG.selectors.bottomBarNum);
        let isFinished = false;
        if (progressEl) {
            const match = progressEl.innerText.match(/(\d+)\s*\/\s*(\d+)/);
            if (match) {
                const done = parseInt(match[1]);
                const total = parseInt(match[2]);
                if (done >= total && total > 0) isFinished = true;
            }
        }

        if (isFinished) {
            const logDiv = document.querySelector('#ai-log');
            logDiv.innerHTML += `<div class="answer" style="color:red; margin-top:10px;">所有题目已完成,提交中...</div>`;
            const allBtns = Array.from(document.querySelectorAll(CONFIG.selectors.submitBtn));
            const submitBtn = allBtns.find(b => b.innerText.includes("提交"));

            if (submitBtn) {
                submitBtn.click();
                setTimeout(() => {
                    const confirmBtns = Array.from(document.querySelectorAll(CONFIG.selectors.modalConfirmBtn));
                    const realConfirm = confirmBtns.find(b => b.innerText.includes("确定") || b.innerText.includes("提交"));
                    if (realConfirm) {
                        realConfirm.click();
                        logDiv.innerHTML += `<div class="reasoning">已确认。等待跳转...</div>`;
                        setTimeout(goBackToList, 5000);
                    }
                }, 1000);
                return true;
            }
        }
        return false;
    }

    // ================= 答题页流程 =================
    function startQuiz(isAuto = false) {
        const startBtns = Array.from(document.querySelectorAll(CONFIG.selectors.startQuizBtn));
        const realStartBtn = startBtns.find(b => b.innerText.includes("开始答题"));
        if (realStartBtn) {
            document.querySelector('#status-text').innerText = "👇 自动点击开始...";
            realStartBtn.click();
            setTimeout(() => startQuiz(isAuto), 2000);
            return;
        }

        questionsList = Array.from(document.querySelectorAll(CONFIG.selectors.questionContainer));
        if (questionsList.length === 0) {
            if(!isAuto) alert("未找到题目");
            return;
        }

        isRunning = true; isPaused = false; currentIndex = 0;

        document.querySelector('#btn-action').style.display = 'none';
        document.querySelector('#btn-pause').style.display = 'inline-block';
        document.querySelector('#btn-stop').style.display = 'inline-block';

        fetchStandardAnswers((success) => {
            if (!success) document.querySelector('#ai-log').innerHTML += `<div class="reasoning" style="color:orange">⚠️ API 获取失败,尝试 AI 模式</div>`;
            processNextQuizItem();
        });
    }

    function processNextQuizItem() {
        if (!isRunning) return;
        if (isPaused) return;

        if (currentIndex >= questionsList.length) {
            const submitted = checkAndSubmit();
            if (!submitted) {
                document.querySelector('#status-text').innerText = "本页遍历结束";
                setTimeout(goBackToList, 2000);
            }
            return;
        }

        const currentContainer = questionsList[currentIndex];
        currentContainer.scrollIntoView({ behavior: "smooth", block: "center" });
        questionsList.forEach(q => q.classList.remove('current-q'));
        currentContainer.classList.add('current-q');

        document.querySelector('#status-text').innerHTML = `正在处理第 <b>${currentIndex + 1}</b> / ${questionsList.length} 题`;

        if (isQuestionAnswered(currentContainer)) {
            currentIndex++; setTimeout(processNextQuizItem, 500); return;
        }

        const apiResult = getApiAnswerForQuestion(currentIndex, currentContainer);
        if (apiResult) {
            const logDiv = document.querySelector('#ai-log');
            logDiv.innerHTML = `<span class="mode-badge badge-api">API</span> ${apiResult.join(', ')}`;
            const answerStr = apiResult.join(" ");
            if (apiResult[0] === 'true' || apiResult[0] === 'false') autoSelectJudge(apiResult[0], currentContainer);
            else autoSelectChoices(answerStr, currentContainer);
            currentIndex++; setTimeout(() => processNextQuizItemWithDelay(1000), 50);
        } else {
            const logDiv = document.querySelector('#ai-log');

            // 答题页智能避让:无AI配置且无API答案
            if (!CONFIG.aiBaseUrl) {
                logDiv.innerHTML = `<span class="reasoning" style="color:gray">🚫 无AI配置且无API,跳过...</span>`;
                currentIndex++;
                setTimeout(processNextQuizItem, 1000);
                return;
            }

            logDiv.innerHTML = `<span class="mode-badge badge-ai">AI</span> 思考中...`;
            callAI(currentContainer, (aiAnswer) => {
                const clean = cleanMarkdown(aiAnswer);
                logDiv.innerHTML = `<div class="answer">${clean}</div>`;
                autoFillText(clean, currentContainer);
                currentIndex++; processNextQuizItemWithDelay(3000);
            });
        }
    }

    function processNextQuizItemWithDelay(delayMs) {
        let timeLeft = delayMs / 1000;
        const timer = setInterval(() => {
            if (!isRunning) { clearInterval(timer); return; }
            if (isPaused) { clearInterval(timer); document.querySelector('#status-text').innerHTML = `<span style="color:#f39c12">⏸ 已暂停</span>`; return; }
            if (timeLeft <= 0) { clearInterval(timer); processNextQuizItem(); }
            else { timeLeft -= 0.5; }
        }, 500);
    }

    // ================= 辅助函数 =================
    function getApiAnswerForQuestion(index, container) {
        if (!HAS_FETCHED_API) return null;
        const answers = API_ANSWERS[`INDEX_${index}`];
        if (answers && answers.length > 0) return answers;
        return null;
    }

    function callAI(container, callback) {
        const info = extractPageInfo(container);
        if (info.error) { callback(""); return; }
        GM_xmlhttpRequest({
            method: "POST",
            url: CONFIG.aiBaseUrl,
            headers: { "Content-Type": "application/json" },
            data: JSON.stringify({ prompt: info.prompt }),
                          responseType: 'text',
                          onload: function(response) {
                              const chunks = response.responseText.split('data: ');
                              let fullText = "";
                              chunks.forEach(c => {
                                  if(!c.trim() || c.includes('[DONE]')) return;
                                  try { fullText += JSON.parse(c).choices[0].delta.content || ""; } catch(e){}
                              });
                              callback(fullText);
                          },
                          onerror: () => callback("")
        });
    }

    function isQuestionAnswered(container) {
        const textInput = container.querySelector(CONFIG.selectors.textInput);
        if (textInput && textInput.value.trim() !== "") return true;
        const checkedItems = container.querySelectorAll(CONFIG.selectors.checkedSelector);
        return checkedItems.length > 0;
    }

    function cleanMarkdown(text) {
        if (!text) return "";
        return text.replace(/\*\*/g, "").replace(/#/g, "").trim();
    }

    function extractPageInfo(container) {
        const qElem = container.querySelector(CONFIG.selectors.question);
        if (!qElem) return { error: "no question" };
        let prompt = `题目:${qElem.innerText.trim()}\n这是一个填空题,请直接给出答案,不要解释。`;
        return { prompt, type: 'text' };
    }

    function autoFillText(text, container) {
        const input = container.querySelector(CONFIG.selectors.textInput);
        if (input) { input.value = text; input.dispatchEvent(new Event('input', { bubbles: true })); input.dispatchEvent(new Event('blur', { bubbles: true })); }
    }

    function autoSelectJudge(ans, container) {
        const isTrue = (ans === 'true' || ans.includes('正确'));
        const isFalse = (ans === 'false' || ans.includes('错误'));
        if (isTrue) container.querySelector(CONFIG.selectors.judgeTrueIcon)?.closest('.ul-radio')?.click();
        else if (isFalse) container.querySelector(CONFIG.selectors.judgeFalseIcon)?.closest('.ul-radio')?.click();
    }

    async function autoSelectChoices(ansStr, container) {
        const choices = container.querySelectorAll(CONFIG.selectors.choiceItem);
        const target = ansStr.toUpperCase();
        for (let i = 0; i < choices.length; i++) {
            const choice = choices[i];
            const idx = choice.querySelector(CONFIG.selectors.choiceIndex)?.innerText.trim().replace('.','');
            if (idx && target.includes(idx)) {
                const area = choice.querySelector(CONFIG.selectors.choiceClickArea);
                if (area && !area.classList.contains('is-checked') && !choice.querySelector('input:checked')) {
                    area.click();
                    await new Promise(r => setTimeout(r, 300));
                }
            }
        }
    }

    window.addEventListener('load', () => {
        setTimeout(() => {
            createUI();
            checkFirstRun();
        }, 2000);
    });
})();