Greasy Fork

Greasy Fork is available in English.

优学院答题助手 V14.9

全自动刷课 | 智能避让 | 自动进入讨论 | 自动回复 | 自动净化AI答案 | 支持暂停/继续 | 自动关闭标签 | 修复UI显示

您需要先安装一个扩展,例如 篡改猴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.8
// @description  全自动刷课 | 智能避让 | 自动进入讨论 | 自动回复 | 自动净化AI答案 | 支持暂停/继续 | 自动关闭标签 | 修复UI显示
// @author       Winds (Modified)
// @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
// @grant        unsafeWindow
// ==/UserScript==

(function() {
    'use strict';

    // ================= 配置区域 =================
    const USER_API_URL = GM_getValue('UL_AI_URL', '');

    const CONFIG = {
        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",
            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",

            // 👇👇👇 讨论区相关选择器 👇👇👇
            discussionPageMarker: ".course-discussion-page", // 讨论列表页标志
            discussionRow: ".discussion-list .table-body",   // 讨论列表行
            myPostCount: ".item-post-my span",               // 我的帖子计数
            viewDiscussionBtn: ".item-operation .button-red-hollow", // 查看按钮

            // 👇👇👇 讨论详情页自动回复相关 👇👇👇
            discussionDetailTitle: ".contentTopText", // 题目容器
            discussionSubmitBtn: ".el-button.submit.el-button--primary" // 提交按钮
        }
    };

    let API_ANSWERS = {};
    let HAS_FETCHED_API = false;
    let hasAutoReplied = false; // 防止单页重复回复

    // ================= 状态管理 =================
    const isListPage = () => document.querySelector(CONFIG.selectors.listContainer) !== null;
    const isResultPage = () => location.href.includes("stuReport") || document.querySelector(CONFIG.selectors.resultPageMarker) !== null;
    const isDiscussionListPage = () => document.querySelector(CONFIG.selectors.discussionPageMarker) !== null;

    // 判断是否为讨论详情页
    const isDiscussionDetailPage = () => {
        return document.querySelector(CONFIG.selectors.discussionDetailTitle) !== null &&
               document.getElementById('cke_editorContainer') !== null;
    };

    const isPotentialQuizPage = () => !isListPage() && !isResultPage() && !isDiscussionListPage() && !isDiscussionDetailPage() && (
        document.querySelectorAll(CONFIG.selectors.questionContainer).length > 0 ||
        document.querySelector(CONFIG.selectors.startQuizBtn) !== null
    );

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

    // ================= 首次运行检查 =================
    function checkFirstRun() {
        if (!GM_getValue('UL_HAS_INIT', false)) {
            const tips = "🎉 欢迎使用优学院助手!\n\n请输入AI API地址(留空则只做客观题):\n注意:配置API后,进入讨论题将自动生成回复!";
            const input = prompt(tips, "");
            if (input !== null) {
                GM_setValue('UL_AI_URL', input.trim());
                GM_setValue('UL_HAS_INIT', true);
                location.reload();
            }
        }
    }

    // ================= 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 创建 (已修复宽度) =================
    GM_addStyle(`
    #ai-panel {
        position: fixed; top: 20px; right: 20px;
        width: 360px; /* 🟢 宽度增加到360px */
        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;
        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; }

    .ai-btn {
        background: rgba(255,255,255,0.2);
        border: 1px solid rgba(255,255,255,0.6);
        color: white;
        padding: 3px 8px; /* 🟢 减小内边距 */
        border-radius: 4px;
        cursor: pointer;
        font-size: 12px;
        margin-left: 3px; /* 🟢 减小间距 */
    }
    .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%; 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;
    }
    `);

    function createUI() {
        if (document.getElementById('ai-panel')) return;
        const panel = document.createElement('div');
        panel.id = 'ai-panel';
        panel.innerHTML = `
        <div id="ai-header">
            <span>🤖 优学院助手</span>
            <div style="display:flex; align-items:center;"> <button class="ai-btn" id="btn-settings" title="设置">⚙️</button>
                <button class="ai-btn" id="btn-pause" style="display:none;" title="暂停/继续">⏸</button>
                <button class="ai-btn" id="btn-stop" style="display:none; background:#e74c3c;" title="停止">⏹</button>
                <button class="ai-btn" id="btn-action">▶ 启动</button>
                <button class="ai-btn" id="btn-minimize" title="最小化">❌</button>
            </div>
        </div>
        <div id="ai-content">
            <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>`;
        document.body.appendChild(panel);

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

        const minimizeBtn = panel.querySelector('#btn-minimize');
        minimizeBtn.onclick = (e) => { e.stopPropagation(); panel.classList.add('minimized'); };
        panel.onclick = () => { if (panel.classList.contains('minimized')) panel.classList.remove('minimized'); };

        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;

        panel.querySelector('#btn-settings').onclick = () => {
            const current = GM_getValue('UL_AI_URL', '');
            const newUrl = prompt("配置 AI API 地址:", current);
            if (newUrl !== null) { GM_setValue('UL_AI_URL', newUrl.trim()); location.reload(); }
        };

        stopBtn.onclick = () => { isRunning = false; GM_setValue('UL_QUEUE_MODE', false); location.reload(); };
        pauseBtn.onclick = togglePause;

        // 页面类型判断与初始化
        if (isDiscussionDetailPage()) {
            // 讨论详情页
            document.querySelector('#status-text').innerText = "📝 讨论详情页";
            actionBtn.innerText = "▶ 自动回复";
            actionBtn.onclick = () => { processDiscussionReply(); showPauseBtn(); };

            // 队列模式自动开始
            if (GM_getValue('UL_QUEUE_MODE', false)) {
                showPauseBtn();
                setTimeout(processDiscussionReply, 2000);
            }
        } else if (isDiscussionListPage()) {
            // 讨论列表页
            document.querySelector('#status-text').innerText = "💬 讨论列表";
            actionBtn.innerText = "▶ 扫描未做";
            actionBtn.onclick = () => { processDiscussionListPage(); showPauseBtn(); };
            // 自动模式
            if(GM_getValue('UL_QUEUE_MODE', false)) showPauseBtn();
            setTimeout(processDiscussionListPage, 1500);
        } else if (isListPage()) {
            GM_setValue('UL_LIST_URL', window.location.href);
            actionBtn.innerText = "▶ 队列";
            actionBtn.onclick = () => { startListQueue(); showPauseBtn(); };
            if (GM_getValue('UL_QUEUE_MODE', false)) {
                actionBtn.style.display = 'none'; stopBtn.style.display = 'inline-block';
                showPauseBtn();
                setTimeout(processListPage, 2000);
            }
        } else if (isResultPage()) {
            document.querySelector('#status-text').innerText = "✅ 作业已完成";
            actionBtn.innerText = "↩️ 返回";
            actionBtn.onclick = goBackToList;
            if (GM_getValue('UL_QUEUE_MODE', false)) setTimeout(goBackToList, 3000);
        } else if (isPotentialQuizPage()) {
            actionBtn.innerText = "▶ 答题";
            actionBtn.onclick = () => { startQuiz(false); showPauseBtn(); };
            setTimeout(() => { startQuiz(true); showPauseBtn(); }, 1500);
        }
    }

    // ================= 暂停逻辑 =================
    function showPauseBtn() {
        document.querySelector('#btn-pause').style.display = 'inline-block';
    }

    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 (isDiscussionDetailPage()) processDiscussionReply();
            else if (isDiscussionListPage()) processDiscussionListPage();
            else if (isListPage()) processListPage();
            else if (isPotentialQuizPage()) processNextQuizItem();
        }
    }

    // ================= 核心功能1:讨论详情页自动回复 =================
    function processDiscussionReply() {
        if (!isDiscussionDetailPage() || hasAutoReplied) return;
        if (isPaused) return; // 暂停检查

        const logDiv = document.querySelector('#ai-log');
        const statusText = document.querySelector('#status-text');

        if (!CONFIG.aiBaseUrl) {
            if(logDiv) logDiv.innerHTML += `<div class="reasoning" style="color:red">⚠️ 未配置AI,无法自动回复。请在设置中配置API。</div>`;
            return;
        }

        hasAutoReplied = true; // 锁定防止重复
        statusText.innerText = "🧠 AI 生成观点中...";
        if(logDiv) logDiv.innerHTML = '<div class="reasoning">正在提取题目...</div>';

        // 提取题目
        const titleEl = document.querySelector(CONFIG.selectors.discussionDetailTitle);
        let promptText = "请就以下话题发表一段深刻的、正能量的、符合大学生身份的观点(150字左右):\n";
        if (titleEl) {
            promptText += titleEl.innerText.trim();
        } else {
            promptText += "未知话题";
        }

        const payload = { prompt: promptText };

        GM_xmlhttpRequest({
            method: "POST",
            url: CONFIG.aiBaseUrl,
            headers: { "Content-Type": "application/json" },
            data: JSON.stringify(payload),
            responseType: 'text',
            onload: function(response) {
                // 处理流式或非流式响应
                let answer = "";
                if (response.responseText.includes("data: ")) {
                    const chunks = response.responseText.split('data: ');
                    chunks.forEach(c => {
                        if(!c.trim() || c.includes('[DONE]')) return;
                        try { answer += JSON.parse(c).choices[0].delta.content || ""; } catch(e){}
                    });
                } else {
                    try {
                        const json = JSON.parse(response.responseText);
                        answer = json.choices[0].message.content || json.response;
                    } catch(e) { answer = response.responseText; }
                }

                if (!answer) {
                    if(logDiv) logDiv.innerHTML += `<div class="reasoning" style="color:red">AI 响应为空</div>`;
                    return;
                }

                // 👇👇👇 净化 AI 答案:删除 "XXX字" 相关的描述 👇👇👇
                answer = answer.replace(/[((\[]?\s*\d+\s*字(左右)?\s*[))\]]?[::]?/g, "").trim();

                if(logDiv) logDiv.innerHTML += `<div class="answer">${answer}</div>`;
                statusText.innerText = "✍️ 正在写入编辑器...";

                // 写入 CKEditor (需要 unsafeWindow)
                try {
                    if (unsafeWindow.CKEDITOR && unsafeWindow.CKEDITOR.instances && unsafeWindow.CKEDITOR.instances.editorContainer) {
                        unsafeWindow.CKEDITOR.instances.editorContainer.setData(answer);

                        if(logDiv) logDiv.innerHTML += `<div class="reasoning">写入成功,2秒后提交...</div>`;

                        setTimeout(() => {
                            if (isPaused) return; // 提交前检查暂停
                            const submitBtn = document.querySelector(CONFIG.selectors.discussionSubmitBtn);
                            if (submitBtn) {
                                submitBtn.click();
                                statusText.innerText = "✅ 已提交";
                                if(logDiv) logDiv.innerHTML += `<div class="reasoning" style="color:green">已点击提交。2秒后自动关闭...</div>`;

                                setTimeout(() => {
                                    if (isPaused) return; // 关闭前检查暂停
                                    // 【核心逻辑】完成任务:设置标志位 + 关闭窗口
                                    GM_setValue('UL_DISCUSSION_STATE', 'finished');
                                    window.close();

                                    // 兜底:如果无法关闭(比如同一个标签页打开的),尝试返回
                                    setTimeout(() => {
                                        statusText.innerText = "⚠️ 窗口未关闭,尝试返回...";
                                        goBackToList();
                                    }, 500);
                                }, 2000);

                            } else {
                                if(logDiv) logDiv.innerHTML += `<div class="reasoning" style="color:red">未找到提交按钮</div>`;
                            }
                        }, 2000);

                    } else {
                        if(logDiv) logDiv.innerHTML += `<div class="reasoning" style="color:red">CKEditor 对象未找到,无法自动写入。</div>`;
                    }
                } catch (e) {
                    console.error(e);
                    if(logDiv) logDiv.innerHTML += `<div class="reasoning" style="color:red">写入出错: ${e.message}</div>`;
                }
            },
            onerror: () => {
                 if(logDiv) logDiv.innerHTML += `<div class="reasoning" style="color:red">AI 请求失败</div>`;
            }
        });
    }

    // ================= 核心功能2:讨论列表自动化 =================
    function processDiscussionListPage() {
        if (!isDiscussionListPage()) return;
        if (isPaused) return; // 暂停检查

        // 【关键】保存当前列表页地址
        GM_setValue('UL_LIST_URL', window.location.href);

        const logDiv = document.querySelector('#ai-log');
        const statusText = document.querySelector('#status-text');

        statusText.innerText = "🔍 扫描未做讨论...";
        if(logDiv) logDiv.innerHTML = '<div class="reasoning">正在分析列表...</div>';

        const rows = Array.from(document.querySelectorAll(CONFIG.selectors.discussionRow));
        let targetBtn = null;
        let targetTitle = "";

        for (let row of rows) {
            const myCountEl = row.querySelector(CONFIG.selectors.myPostCount);
            if (myCountEl) {
                const count = parseInt(myCountEl.innerText.trim());
                // 只有当帖子数为 0 时才进入
                if (!isNaN(count) && count === 0) {
                    const btn = row.querySelector(CONFIG.selectors.viewDiscussionBtn);
                    const titleEl = row.querySelector('.discussion-name .text span');
                    if (btn && btn.innerText.includes("查看")) {
                        targetBtn = btn;
                        targetTitle = titleEl ? titleEl.innerText : "未知主题";
                        break;
                    }
                }
            }
        }

        if (targetBtn) {
            GM_setValue('UL_QUEUE_MODE', true); // 确保进入详情页后知道是自动模式
            GM_setValue('UL_DISCUSSION_STATE', 'processing'); // 标记为正在处理

            statusText.innerText = "⚡ 进入讨论 (完成后自动刷新)...";
            if(logDiv) logDiv.innerHTML += `<div class="answer" style="color:#e67e22">发现未做讨论:${targetTitle}</div>`;

            // 【核心逻辑】开启轮询,等待子标签页完成任务
            const checkTimer = setInterval(() => {
                if (isPaused) return; // 轮询中检查暂停,但不清除定时器,等待恢复
                const state = GM_getValue('UL_DISCUSSION_STATE');
                if (state === 'finished') {
                    clearInterval(checkTimer);
                    GM_setValue('UL_DISCUSSION_STATE', 'idle'); // 重置状态
                    statusText.innerText = "✅ 检测到任务完成,正在刷新...";
                    if(logDiv) logDiv.innerHTML += `<div class="reasoning" style="color:green">任务完成,刷新列表...</div>`;
                    setTimeout(() => location.reload(), 1000); // 刷新页面
                }
            }, 1000);

            setTimeout(() => {
                if(!isPaused) targetBtn.click();
            }, 2000);
        } else {
            statusText.innerText = "✅ 本页已完成";
            const nextBtn = document.querySelector(CONFIG.selectors.nextPageBtn);
            if (nextBtn && !nextBtn.classList.contains('disabled') && nextBtn.style.display !== 'none') {
                if(logDiv) logDiv.innerHTML += `<div class="reasoning">自动翻页中...</div>`;
                setTimeout(() => {
                    if(!isPaused) { nextBtn.click(); setTimeout(processDiscussionListPage, 3000); }
                }, 2000);
            } else {
                 if(logDiv) logDiv.innerHTML += `<div class="reasoning" style="color:green; font-weight:bold">🎉 所有讨论均已完成!</div>`;
                 GM_setValue('UL_QUEUE_MODE', false);
            }
        }
    }

    // ================= 列表页逻辑 =================
    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; // 暂停检查

        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;
            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("主观题")) 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')) {
                setTimeout(() => { if(!isPaused) nextBtn.click(); setTimeout(processListPage, 3000); }, 2000);
            } else {
                GM_setValue('UL_QUEUE_MODE', false);
                document.querySelector('#status-text').innerText = "全部完成";
            }
        }
    }

    // ================= 答题页提交逻辑 =================
    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 && parseInt(match[1]) >= parseInt(match[2])) isFinished = true;
        }

        if (isFinished) {
            const submitBtn = Array.from(document.querySelectorAll(CONFIG.selectors.submitBtn)).find(b => b.innerText.includes("提交"));
            if (submitBtn) {
                submitBtn.click();
                setTimeout(() => {
                    if (isPaused) return; // 暂停检查
                    const confirmBtn = Array.from(document.querySelectorAll(CONFIG.selectors.modalConfirmBtn)).find(b => b.innerText.includes("确定"));
                    if (confirmBtn) {
                        confirmBtn.click();
                        document.querySelector('#ai-log').innerHTML += `<div class="reasoning">提交成功,准备返回...</div>`;
                        setTimeout(goBackToList, 3000);
                    }
                }, 1000);
                return true;
            }
        }
        return 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; }
        window.history.back();
    }

    function startQuiz(isAuto) {
        const startBtns = Array.from(document.querySelectorAll(CONFIG.selectors.startQuizBtn));
        const realStartBtn = startBtns.find(b => b.innerText.includes("开始答题"));
        if (realStartBtn) { realStartBtn.click(); setTimeout(() => startQuiz(isAuto), 2000); return; }

        questionsList = Array.from(document.querySelectorAll(CONFIG.selectors.questionContainer));
        if (questionsList.length === 0) return;

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

        fetchStandardAnswers(() => processNextQuizItem());
    }

    function processNextQuizItem() {
        if (!isRunning || isPaused) return; // 暂停检查
        if (currentIndex >= questionsList.length) {
            if(!checkAndSubmit()) setTimeout(goBackToList, 2000);
            return;
        }
        const container = questionsList[currentIndex];
        container.scrollIntoView({ behavior: "smooth", block: "center" });

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

        const apiResult = getApiAnswerForQuestion(currentIndex);
        if (apiResult) {
            if (apiResult[0] === 'true' || apiResult[0] === 'false') autoSelectJudge(apiResult[0], container);
            else autoSelectChoices(apiResult.join(" "), container);
            currentIndex++; setTimeout(processNextQuizItem, 1000);
        } else if (CONFIG.aiBaseUrl) {
            callAI(container, (ans) => {
                autoFillText(cleanMarkdown(ans), container);
                currentIndex++; setTimeout(processNextQuizItem, 3000);
            });
        } else {
            currentIndex++; setTimeout(processNextQuizItem, 1000);
        }
    }

    function getApiAnswerForQuestion(index) {
        if (!HAS_FETCHED_API) return null;
        return API_ANSWERS[`INDEX_${index}`] || null;
    }
    function isQuestionAnswered(container) {
        const input = container.querySelector(CONFIG.selectors.textInput);
        if (input && input.value.trim()) return true;
        return container.querySelectorAll(CONFIG.selectors.checkedSelector).length > 0;
    }
    function autoSelectJudge(ans, container) {
        const sel = ans === 'true' || ans.includes('正确') ? CONFIG.selectors.judgeTrueIcon : CONFIG.selectors.judgeFalseIcon;
        container.querySelector(sel)?.closest('.ul-radio')?.click();
    }
    async function autoSelectChoices(ansStr, container) {
        const choices = container.querySelectorAll(CONFIG.selectors.choiceItem);
        for (let c of choices) {
            const idx = c.querySelector(CONFIG.selectors.choiceIndex)?.innerText.trim().replace('.','');
            if (idx && ansStr.toUpperCase().includes(idx)) c.querySelector(CONFIG.selectors.choiceClickArea)?.click();
        }
    }
    function callAI(container, callback) {
        const qElem = container.querySelector(CONFIG.selectors.question);
        if (!qElem) return;
        const prompt = `题目:${qElem.innerText.trim()}\n请直接给出答案,不要解释。`;

        GM_xmlhttpRequest({
            method: "POST",
            url: CONFIG.aiBaseUrl,
            headers: { "Content-Type": "application/json" },
            data: JSON.stringify({ prompt: prompt }),
            onload: function(response) {
                let fullText = "";
                 if (response.responseText.includes("data: ")) {
                    const chunks = response.responseText.split('data: ');
                    chunks.forEach(c => {
                        if(!c.trim() || c.includes('[DONE]')) return;
                        try { fullText += JSON.parse(c).choices[0].delta.content || ""; } catch(e){}
                    });
                } else {
                     try { fullText = JSON.parse(response.responseText).choices[0].message.content; } catch(e){ fullText = response.responseText; }
                }
                callback(fullText);
            }
        });
    }
    function cleanMarkdown(text) { return text.replace(/\*\*/g, "").replace(/#/g, "").trim(); }
    function autoFillText(text, container) {
        const input = container.querySelector(CONFIG.selectors.textInput);
        if (input) { input.value = text; input.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('blur')); }
    }

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