// ==UserScript== // @name 青书学堂考试助手 // @namespace http://tampermonkey.net/ // @version 0.5 // @description 青书学堂考试助手 使用作业答案+ai方式进行答题 // @license MIT // @author ruby // @match https://degree.qingshuxuetang.com/zzqd/Student/ExamPaper* // @grant GM_xmlhttpRequest // @connect degree.qingshuxuetang.com // @downloadURL https://update.greasyfork.icu/scripts/534019/%E9%9D%92%E4%B9%A6%E5%AD%A6%E5%A0%82%E8%80%83%E8%AF%95%E5%8A%A9%E6%89%8B.user.js // @updateURL https://update.greasyfork.icu/scripts/534019/%E9%9D%92%E4%B9%A6%E5%AD%A6%E5%A0%82%E8%80%83%E8%AF%95%E5%8A%A9%E6%89%8B.meta.js // ==/UserScript== (function() { 'use strict'; // 添加显示答案按钮和输入框 function addAnswerButton() { const container = document.createElement('div'); container.style.position = 'fixed'; container.style.top = '10px'; container.style.right = '10px'; container.style.zIndex = '9999'; container.style.display = 'flex'; container.style.flexDirection = 'column'; container.style.gap = '10px'; container.style.backgroundColor = 'white'; container.style.padding = '15px'; container.style.borderRadius = '8px'; container.style.boxShadow = '0 2px 10px rgba(0,0,0,0.1)'; container.style.width = '300px'; // 创建标题 const title = document.createElement('div'); title.textContent = '青书学堂答案助手'; title.style.fontSize = '16px'; title.style.fontWeight = 'bold'; title.style.marginBottom = '10px'; title.style.textAlign = 'center'; title.style.color = '#333'; // 创建输入区域容器 const inputContainer = document.createElement('div'); inputContainer.style.display = 'flex'; inputContainer.style.flexDirection = 'column'; inputContainer.style.gap = '10px'; // 创建API密钥输入框 const apiKeyInput = document.createElement('input'); apiKeyInput.type = 'text'; apiKeyInput.placeholder = '请从豆包AI官网获取API密钥'; apiKeyInput.style.padding = '8px'; apiKeyInput.style.borderRadius = '5px'; apiKeyInput.style.border = '1px solid #ddd'; apiKeyInput.style.width = '100%'; apiKeyInput.style.boxSizing = 'border-box'; apiKeyInput.id = 'ai-api-key'; apiKeyInput.value = localStorage.getItem('ai_api_key') || ''; // 创建保存API密钥按钮 const saveApiKeyButton = document.createElement('button'); saveApiKeyButton.textContent = '保存密钥'; saveApiKeyButton.style.padding = '8px'; saveApiKeyButton.style.backgroundColor = '#2196F3'; saveApiKeyButton.style.color = 'white'; saveApiKeyButton.style.border = 'none'; saveApiKeyButton.style.borderRadius = '5px'; saveApiKeyButton.style.cursor = 'pointer'; saveApiKeyButton.style.width = '100%'; saveApiKeyButton.addEventListener('click', () => { const apiKey = apiKeyInput.value.trim(); if (apiKey) { localStorage.setItem('ai_api_key', apiKey); alert('API密钥已保存!'); } else { alert('请输入有效的API密钥!'); } }); // 创建quizId输入框 const input = document.createElement('input'); input.type = 'text'; input.placeholder = '输入 作业quizId(可选)'; input.style.padding = '8px'; input.style.borderRadius = '5px'; input.style.border = '1px solid #ddd'; input.style.width = '100%'; input.style.boxSizing = 'border-box'; input.id = 'manual-quiz-id'; // 创建显示答案按钮 const button = document.createElement('button'); button.textContent = '显示答案'; 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.style.width = '100%'; button.style.marginBottom = '15px'; button.addEventListener('click', showAnswers); // 创建分割线 const divider = document.createElement('div'); divider.style.height = '1px'; divider.style.backgroundColor = '#eee'; divider.style.margin = '10px 0'; // 创建二维码容器 const qrContainer = document.createElement('div'); qrContainer.style.display = 'flex'; qrContainer.style.justifyContent = 'space-between'; qrContainer.style.marginTop = '10px'; // 创建打赏二维码 const donateQR = document.createElement('div'); donateQR.style.textAlign = 'center'; donateQR.style.flex = '1'; donateQR.innerHTML = `
打赏作者
`; // 创建微信二维码 const wechatQR = document.createElement('div'); wechatQR.style.textAlign = 'center'; wechatQR.style.flex = '1'; wechatQR.innerHTML = `
联系作者
`; // 添加版权信息 const copyright = document.createElement('div'); copyright.style.fontSize = '12px'; copyright.style.color = '#999'; copyright.style.textAlign = 'center'; copyright.style.marginTop = '10px'; copyright.textContent = '© 2024 Ruby. All rights reserved.'; // 组装界面 inputContainer.appendChild(apiKeyInput); inputContainer.appendChild(saveApiKeyButton); inputContainer.appendChild(input); inputContainer.appendChild(button); qrContainer.appendChild(donateQR); qrContainer.appendChild(wechatQR); container.appendChild(title); container.appendChild(inputContainer); container.appendChild(divider); container.appendChild(qrContainer); container.appendChild(copyright); document.body.appendChild(container); // 添加悬浮效果 container.addEventListener('mouseenter', () => { container.style.boxShadow = '0 4px 15px rgba(0,0,0,0.15)'; }); container.addEventListener('mouseleave', () => { container.style.boxShadow = '0 2px 10px rgba(0,0,0,0.1)'; }); } // 获取URL参数 function getUrlParams() { const url = new URL(window.location.href); // 检查是否有手动输入的 quizId,仅用于答案接口 const manualQuizId = document.getElementById('manual-quiz-id')?.value; return { courseId: url.searchParams.get('courseId'), originalQuizId: url.searchParams.get('quizId'), // 原始URL中的quizId quizId: manualQuizId || url.searchParams.get('quizId'), teachPlanId: url.searchParams.get('teachPlanId'), periodId: url.searchParams.get('periodId') }; } // 获取题目答案 async function getAnswers() { const params = getUrlParams(); const timestamp = Date.now(); const url = `https://degree.qingshuxuetang.com/zzqd/Student/DetailData?quizId=${params.quizId}&_=${timestamp}&_t=${timestamp + 2000}`; try { const response = await fetch(url, { method: 'GET', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json, text/javascript, */*; q=0.01', 'X-Requested-With': 'XMLHttpRequest' }, credentials: 'include' }); const data = await response.json(); return data; } catch (error) { console.error('获取答案失败:', error); return null; } } // 获取题目内容、选项和图片 function getQuestionContent(question) { const contentElement = question.querySelector('.subject-content'); if (!contentElement) return { text: '', options: [], imageUrl: null }; // 获取文本内容 let text = contentElement.textContent || ''; // 获取选项 const optionsElements = question.querySelectorAll('.option-item'); const options = Array.from(optionsElements).map(option => { const label = option.querySelector('.option-label')?.textContent || ''; const content = option.querySelector('.option-content')?.textContent || ''; return `${label}. ${content}`; }); // 查找图片 const image = contentElement.querySelector('img'); const imageUrl = image ? image.src : null; return { text, options, imageUrl }; } // 解析HTML内容,提取文本和图片 function parseDescription(description) { // 创建一个临时的div来解析HTML const tempDiv = document.createElement('div'); tempDiv.innerHTML = description; // 获取所有图片 const images = tempDiv.querySelectorAll('img'); const imageUrls = Array.from(images).map(img => img.src); // 获取纯文本内容(移除HTML标签) const text = tempDiv.textContent.trim(); return { text, imageUrls }; } // 获取题目类型描述 function getQuestionTypeDesc(typeId) { switch (typeId) { case 1: return '这是一道单选题,请从选项中选择一个正确答案。'; case 2: return '这是一道多选题,请从选项中选择所有正确答案。'; case 3: return '这是一道简答题,请简要回答,回答需要200 - 300字。'; default: return ''; } } // 使用豆包AI获取答案 async function getAIAnswer(questionData) { console.log('开始获取AI答案:', questionData); const API_URL = 'https://ark.cn-beijing.volces.com/api/v3/chat/completions'; const API_KEY = localStorage.getItem('ai_api_key'); // 从本地存储获取API密钥 if (!API_KEY) { throw new Error('未设置AI API密钥,请先设置密钥!'); } // 解析题目描述 const { text, imageUrls } = parseDescription(questionData.description); // 获取选项 const options = questionData.options?.map(opt => `${opt.label}. ${opt.description}` ) || []; // 构建完整的问题文本 let fullQuestion = text + '\n'; if (options.length > 0) { fullQuestion += '\n选项:\n' + options.join('\n'); } // 根据题目类型构建提示语 const typeDesc = getQuestionTypeDesc(questionData.typeId); const prompt = `${typeDesc}\n请回答以下题目:\n\n${fullQuestion}\n\n要求: 1. ${questionData.typeId === 1 ? '必须且只能选择一个选项' : questionData.typeId === 2 ? '可以选择多个正确选项' : '简要回答,不超过50字'} 2. 如果有图片请分析图片内容 3. 如果是选择题,请说明选择理由`; try { const messages = [{ role: 'user', content: [ { type: 'text', text: prompt } ] }]; // 添加图片(如果有) if (imageUrls.length > 0) { imageUrls.forEach(url => { messages[0].content.push({ type: 'image_url', image_url: { url: url } }); }); } const response = await fetch(API_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${API_KEY}` }, body: JSON.stringify({ model: 'doubao-1.5-vision-pro-250328', messages: messages }) }); const data = await response.json(); if (data.choices && data.choices[0] && data.choices[0].message) { return data.choices[0].message.content; } return null; } catch (error) { console.error('AI答案获取失败:', error); return null; } } // 获取题目详情 - 使用URL中的quizId async function getQuestionDetails() { const params = getUrlParams(); const timestamp = Date.now(); const url = `https://degree.qingshuxuetang.com/zzqd/Student/DetailData?quizId=${params.originalQuizId}&_=${timestamp}&_t=${timestamp + 2000}`; try { const response = await fetch(url, { method: 'GET', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json, text/javascript, */*; q=0.01', 'X-Requested-With': 'XMLHttpRequest' }, credentials: 'include' }); const data = await response.json(); return data; } catch (error) { console.error('获取题目详情失败:', error); return null; } } // 修改显示答案的函数 async function showAnswers() { try { const params = getUrlParams(); if (!params.quizId) { alert('请提供 quizId!'); return; } // 获取题目详情和答案 const [questionDetails, answers] = await Promise.all([ getQuestionDetails(), getAnswers() ]); console.log('题目详情:', questionDetails); console.log('答案数据:', answers); if (!questionDetails?.data?.paperDetail?.questions || !answers?.data?.paperDetail?.questions) { alert('获取数据失败,请检查网络或重试!'); return; } // 移除已有的答案显示 document.querySelectorAll('.answer-display').forEach(el => el.remove()); // 创建答案映射 const answerMap = new Map( answers.data.paperDetail.questions.map(q => [q.questionId, q.solution]) ); // 为每个题目显示答案 for (const questionData of questionDetails.data.paperDetail.questions) { const question = document.querySelector(`[id="${questionData.questionId}"]`); if (!question) continue; const answerDisplay = document.createElement('div'); answerDisplay.className = 'answer-display'; answerDisplay.style.color = '#ff0000'; answerDisplay.style.fontWeight = 'bold'; answerDisplay.style.marginTop = '10px'; answerDisplay.style.marginBottom = '10px'; answerDisplay.style.fontSize = '16px'; answerDisplay.style.backgroundColor = '#f0f0f0'; answerDisplay.style.padding = '10px'; answerDisplay.style.borderLeft = '4px solid #ff0000'; // 检查是否有官方答案 const officialAnswer = answerMap.get(questionData.questionId); if (officialAnswer) { // 如果有官方答案,直接显示 answerDisplay.innerHTML = `【官方答案】${officialAnswer}`; }else if(questionData.userAnswer){ // 已答题不在使用ai获取答案 消费算力 answerDisplay.innerHTML = `【已答题】 不再使用AI获取答案` } else { // 如果没有官方答案,使用豆包AI获取答案 const questionType = questionData.typeId === 1 ? '单选题' : questionData.typeId === 2 ? '多选题' : questionData.typeId === 3 ? '简答题' : '未知类型'; answerDisplay.innerHTML = `【正在获取AI答案...】(${questionType})`; const aiAnswer = await getAIAnswer(questionData); if (aiAnswer) { const { imageUrls } = parseDescription(questionData.description); answerDisplay.innerHTML = `
未找到官方答案,以下是AI参考答案:
题目类型:${questionType}
${imageUrls.length > 0 ? '
(包含图片分析)
' : ''} ${questionData.options?.length > 0 ? '
(包含选项分析)
' : ''}
${aiAnswer}
`; if(questionData.typeId === 3){ console.log(question); question.querySelector('.cke_wysiwyg_div').innerHTML = aiAnswer; } } else { answerDisplay.innerHTML = '【无法获取答案】'; } } question.appendChild(answerDisplay); } console.log('答案显示完成!'); } catch (error) { console.error('显示答案出错:', error); alert('显示答案过程出错,请重试!'); } } // 等待页面加载完成后初始化 window.addEventListener('load', () => { addAnswerButton(); }); })();