Greasy Fork

Greasy Fork is available in English.

青书学堂考试显示助手

青书学堂答案显示助手 只用于考试

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         青书学堂考试显示助手
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  青书学堂答案显示助手 只用于考试
// @license MIT
// @author       ruby
// @match        https://degree.qingshuxuetang.com/zzqd/Student/ExercisePaper*
// @grant        GM_xmlhttpRequest
// @connect      degree.qingshuxuetang.com
// @connect      www.baidu.com
// @connect      cn.bing.com
// @connect      *
// ==/UserScript==

(function() {
    'use strict';

    // 添加显示答案按钮
    function addAnswerButton() {
        const button = document.createElement('button');
        button.textContent = '显示答案';
        button.style.position = 'fixed';
        button.style.top = '10px';
        button.style.right = '10px';
        button.style.zIndex = '9999';
        button.style.padding = '10px';
        button.style.backgroundColor = '#4CAF50';
        button.style.color = 'white';
        button.style.border = 'none';
        button.style.borderRadius = '5px';
        button.style.cursor = 'pointer';

        button.addEventListener('click', showAnswers);
        document.body.appendChild(button);
    }

    // 获取URL参数
    function getUrlParams() {
        const url = new URL(window.location.href);
        return {
            courseId: url.searchParams.get('courseId'),
            quizId: url.searchParams.get('quizId'),
            teachPlanId: url.searchParams.get('teachPlanId'),
            periodId: url.searchParams.get('periodId')
        };
    }

    // 搜索配置
    const SEARCH_CONFIG = {
        // 一些常见的ChatGPT镜像网站API,建议自行替换为可用的地址
        FREE_AI_APIS: [
            'https://free-api.cveoy.top/v3/chat/completions',
            'https://api.chatanywhere.cn/v1/chat/completions'
        ],
        // 搜索引擎
        SEARCH_URLS: {
            BAIDU: 'https://www.baidu.com/s?wd=',
            BING: 'https://cn.bing.com/search?q='
        },
        // 请求头
        HEADERS: {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
        }
    };

    // 获取题目答案
    async function getAnswers() {
        const params = getUrlParams();
        const timestamp = Date.now();
        const questionBankUrl = `https://degree.qingshuxuetang.com/zzqd/Student/QuestionBank`;

        try {
            // 获取题库数据
            const bankResponse = await fetch(questionBankUrl, {
                credentials: 'include',
                headers: {
                    'Content-Type': 'application/json',
                    'Accept': 'application/json'
                }
            });
            const bankData = await bankResponse.json();
            const matchedAnswers = [];

            // 获取当前考试题目
            const questions = document.querySelectorAll('.question-content');

            for (const question of questions) {
                const questionId = question.id;
                let answer = null;

                // 获取题目中的图片链接
                const imgElement = question.querySelector('img');
                const questionText = question.textContent.trim();

                // 先尝试通过图片链接匹配
                if (imgElement) {
                    const imgUrl = imgElement.src;
                    const matchedQuestion = bankData.find(q => q.imageUrl === imgUrl);
                    if (matchedQuestion) {
                        answer = matchedQuestion.solution;
                    }
                }

                // 如果没有找到匹配的图片,尝试文本匹配
                if (!answer) {
                    const matchedQuestion = bankData.find(q =>
                        similarity(questionText, q.questionText) > 0.9
                    );
                    if (matchedQuestion) {
                        answer = matchedQuestion.solution;
                    }
                }

                // 如果仍然没有答案,使用AI搜索
                if (!answer) {
                    answer = await searchWithAI(questionText);
                }

                matchedAnswers.push({
                    questionId: questionId,
                    solution: answer || '未找到答案'
                });
            }

            return { data: { paperDetail: { questions: matchedAnswers } } };
        } catch (error) {
            console.error('获取答案失败:', error);
            return null;
        }
    }

    // 搜索答案主函数
    async function searchWithAI(questionText) {
        const results = [];

        // 并行执行多个搜索
        const searchPromises = [
            searchBaidu(questionText),
            searchBing(questionText),
            searchFreeAI(questionText)
        ];

        try {
            const searchResults = await Promise.all(searchPromises);
            results.push(...searchResults.filter(Boolean));

            // 验证和处理答案
            return processSearchResults(results, questionText);
        } catch (error) {
            console.error('搜索过程出错:', error);
            return null;
        }
    }

    // 百度搜索
    async function searchBaidu(question) {
        try {
            const encodedQuestion = encodeURIComponent(question);
            const url = SEARCH_CONFIG.SEARCH_URLS.BAIDU + encodedQuestion;

            // 使用GM_xmlhttpRequest进行跨域请求
            return new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: url,
                    headers: SEARCH_CONFIG.HEADERS,
                    onload: function(response) {
                        const parser = new DOMParser();
                        const doc = parser.parseFromString(response.responseText, 'text/html');

                        // 提取搜索结果
                        const searchResults = doc.querySelectorAll('.c-container');
                        let combinedText = '';

                        searchResults.forEach((result, index) => {
                            if (index < 3) { // 只取前3个结果
                                combinedText += result.textContent + ' ';
                            }
                        });

                        resolve(combinedText.trim());
                    },
                    onerror: reject
                });
            });
        } catch (error) {
            console.error('百度搜索失败:', error);
            return null;
        }
    }

    // 必应搜索
    async function searchBing(question) {
        try {
            const encodedQuestion = encodeURIComponent(question);
            const url = SEARCH_CONFIG.SEARCH_URLS.BING + encodedQuestion;

            return new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: url,
                    headers: SEARCH_CONFIG.HEADERS,
                    onload: function(response) {
                        const parser = new DOMParser();
                        const doc = parser.parseFromString(response.responseText, 'text/html');

                        // 提取搜索结果
                        const searchResults = doc.querySelectorAll('.b_algo');
                        let combinedText = '';

                        searchResults.forEach((result, index) => {
                            if (index < 3) {
                                combinedText += result.textContent + ' ';
                            }
                        });

                        resolve(combinedText.trim());
                    },
                    onerror: reject
                });
            });
        } catch (error) {
            console.error('必应搜索失败:', error);
            return null;
        }
    }

    // 使用免费AI API
    async function searchFreeAI(question) {
        for (const apiUrl of SEARCH_CONFIG.FREE_AI_APIS) {
            try {
                const response = await fetch(apiUrl, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({
                        messages: [{
                            role: 'user',
                            content: `请简洁回答这个问题:${question}`
                        }],
                        model: 'gpt-3.5-turbo',
                        temperature: 0.3
                    })
                });

                if (!response.ok) continue;

                const data = await response.json();
                if (data.choices && data.choices[0]) {
                    return data.choices[0].message.content.trim();
                }
            } catch (error) {
                console.error(`AI API ${apiUrl} 调用失败:`, error);
                continue;
            }
        }
        return null;
    }

    // 处理搜索结果
    function processSearchResults(results, questionText) {
        if (!results.length) return null;

        // 清理和标准化结果
        const cleanResults = results.map(result => {
            if (!result) return null;
            // 清理HTML标签和特殊字符
            return result.replace(/<[^>]*>/g, '')
                        .replace(/\s+/g, ' ')
                        .trim();
        }).filter(Boolean);

        if (!cleanResults.length) return null;

        // 提取最相关的答案
        const relevantAnswers = cleanResults.map(result => {
            // 分析结果相关性
            const relevanceScore = calculateRelevance(result, questionText);
            return { text: result, score: relevanceScore };
        });

        // 按相关性排序
        relevantAnswers.sort((a, b) => b.score - a.score);

        // 如果有多个高相关性答案,进行交叉验证
        if (relevantAnswers.length >= 2 &&
            relevantAnswers[0].score > 0.6 &&
            relevantAnswers[1].score > 0.6) {
            // 比较前两个答案的相似度
            const similarity = calculateSimilarity(relevantAnswers[0].text, relevantAnswers[1].text);
            if (similarity > 0.7) {
                return relevantAnswers[0].text;
            }
            return `[待确认] ${relevantAnswers[0].text}`;
        }

        // 返回最相关的答案
        return relevantAnswers[0].score > 0.6 ?
               relevantAnswers[0].text :
               `[待确认] ${relevantAnswers[0].text}`;
    }

    // 计算文本相关性
    function calculateRelevance(text, question) {
        const questionWords = question.toLowerCase().split(/\s+/);
        const textWords = text.toLowerCase().split(/\s+/);

        let matchCount = 0;
        questionWords.forEach(word => {
            if (textWords.includes(word)) matchCount++;
        });

        return matchCount / questionWords.length;
    }

    // 计算文本相似度
    function calculateSimilarity(text1, text2) {
        const words1 = text1.toLowerCase().split(/\s+/);
        const words2 = text2.toLowerCase().split(/\s+/);

        const intersection = words1.filter(word => words2.includes(word));
        const union = new Set([...words1, ...words2]);

        return intersection.length / union.size;
    }

    // 显示答案
    async function showAnswers() {
        try {
            const answers = await getAnswers();
            console.log('获取到的答案数据:', answers);

            if (!answers || !answers.data || !answers.data.paperDetail || !answers.data.paperDetail.questions) {
                alert('获取答案失败,请检查网络或重试!');
                return;
            }

            // 移除已有的答案显示
            document.querySelectorAll('.answer-display').forEach(el => el.remove());

            // 为每个题目显示答案
            answers.data.paperDetail.questions.forEach((questionData) => {
                const question = document.querySelector(`[id="${questionData.questionId}"]`);
                if (question) {
                    const answerDisplay = document.createElement('div');
                    answerDisplay.className = 'answer-display';

                    // 根据答案来源设置不同的样式
                    if (questionData.solution.startsWith('[待确认]')) {
                        answerDisplay.style.backgroundColor = '#fff3cd';
                        answerDisplay.style.borderLeft = '4px solid #ffc107';
                    } else if (questionData.solution === '未找到答案') {
                        answerDisplay.style.backgroundColor = '#f8d7da';
                        answerDisplay.style.borderLeft = '4px solid #dc3545';
                    } else {
                        answerDisplay.style.backgroundColor = '#d4edda';
                        answerDisplay.style.borderLeft = '4px solid #28a745';
                    }

                    answerDisplay.style.color = '#000';
                    answerDisplay.style.fontWeight = 'bold';
                    answerDisplay.style.marginTop = '10px';
                    answerDisplay.style.padding = '10px';

                    answerDisplay.textContent = `【答案】${questionData.solution}`;
                    question.appendChild(answerDisplay);
                }
            });

            console.log('答案显示完成!');
        } catch (error) {
            console.error('显示答案出错:', error);
            alert('显示答案过程出错,请重试!');
        }
    }

    // 等待页面加载完成后初始化
    window.addEventListener('load', () => {
        addAnswerButton();
    });
})();