// ==UserScript==
// @name 厦门理工高校邦考试AI助手 (DeepSeek)
// @namespace https://github.com/Wu557666/gaoxiaobangai
// @version 1.1.2
// @description 在厦门理工高校邦考试/测验页面调用 DeepSeek API 自动答题,支持自定义 API 地址
// @author Wu557666
// @icon https://favicon.im/xmut.gaoxiaobang.com?size=128
// @match https://xmut.class.gaoxiaobang.com/class/*/exam/*
// @match https://xmut.class.gaoxiaobang.com/class/*/quiz/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_xmlhttpRequest
// @grant GM_registerMenuCommand
// @connect api.deepseek.com
// @connect api.deepseek.com
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
// ========== 默认配置 ==========
const DEFAULT_API_URL = 'https://api.deepseek.com/chat/completions';
const DEFAULT_MODEL = 'deepseek-chat';
// ========== 存储键名 ==========
const STORAGE_API_KEY = 'deepseek_api_key';
const STORAGE_API_URL = 'deepseek_api_url';
const STORAGE_MODEL = 'deepseek_model';
// ========== 获取/设置配置 ==========
function getApiKey() {
return GM_getValue(STORAGE_API_KEY, '');
}
function setApiKey(key) {
GM_setValue(STORAGE_API_KEY, key);
}
function getApiUrl() {
return GM_getValue(STORAGE_API_URL, DEFAULT_API_URL);
}
function setApiUrl(url) {
GM_setValue(STORAGE_API_URL, url);
}
function getModel() {
return GM_getValue(STORAGE_MODEL, DEFAULT_MODEL);
}
function setModel(model) {
GM_setValue(STORAGE_MODEL, model);
}
// ========== 页面内弹窗配置(友好版) ==========
function showSettingsPanel() {
// 如果已经存在面板,先移除
const oldPanel = document.getElementById('ai-settings-panel');
if (oldPanel) oldPanel.remove();
const apiKey = getApiKey();
const apiUrl = getApiUrl();
const model = getModel();
// 创建遮罩层
const overlay = document.createElement('div');
overlay.id = 'ai-settings-overlay';
overlay.style.position = 'fixed';
overlay.style.top = '0';
overlay.style.left = '0';
overlay.style.width = '100%';
overlay.style.height = '100%';
overlay.style.background = 'rgba(0,0,0,0.5)';
overlay.style.zIndex = '99999';
overlay.style.display = 'flex';
overlay.style.alignItems = 'center';
overlay.style.justifyContent = 'center';
// 创建面板
const panel = document.createElement('div');
panel.id = 'ai-settings-panel';
panel.style.background = 'white';
panel.style.padding = '24px';
panel.style.borderRadius = '16px';
panel.style.boxShadow = '0 10px 40px rgba(0,0,0,0.3)';
panel.style.width = '400px';
panel.style.maxWidth = '90vw';
panel.style.fontFamily = 'Arial, sans-serif';
panel.innerHTML = `
🤖 考试 AI 助手设置
请输入你的 DeepSeek API Key(点击获取)
高级设置(可选)
`;
overlay.appendChild(panel);
document.body.appendChild(overlay);
// 绑定事件
document.getElementById('ai-settings-cancel').onclick = () => {
overlay.remove();
// 如果没有保存过Key,提示用户
if (!getApiKey()) {
const tip = document.createElement('div');
tip.style.position = 'fixed';
tip.style.bottom = '20px';
tip.style.right = '20px';
tip.style.zIndex = 9999;
tip.style.background = '#ff9800';
tip.style.color = 'white';
tip.style.padding = '10px 15px';
tip.style.borderRadius = '8px';
tip.innerText = '⚠️ 未设置 API Key,无法使用';
document.body.appendChild(tip);
setTimeout(() => tip.remove(), 3000);
}
};
document.getElementById('ai-settings-save').onclick = () => {
const newKey = document.getElementById('ai-api-key-input').value.trim();
const newUrl = document.getElementById('ai-api-url-input').value.trim();
const newModel = document.getElementById('ai-model-input').value.trim();
if (!newKey) {
alert('❌ API Key 不能为空!');
return;
}
setApiKey(newKey);
if (newUrl) setApiUrl(newUrl);
if (newModel) setModel(newModel);
overlay.remove();
alert('✅ 配置已保存!现在可以使用 AI 答题了。');
// 刷新页面使配置生效(或重新初始化面板)
location.reload();
};
}
// 注册菜单命令(备用)
GM_registerMenuCommand('⚙️ 打开设置面板', showSettingsPanel);
GM_registerMenuCommand('📋 查看当前配置', () => {
const key = getApiKey();
const url = getApiUrl();
const model = getModel();
const keyPreview = key ? key.slice(0, 8) + '...' + key.slice(-4) : '未设置';
alert(`当前配置:\n\nAPI Key: ${keyPreview}\nAPI 地址: ${url}\n模型: ${model}`);
});
// ========== 初始化:如果没有 API Key,自动弹出设置面板 ==========
const apiKey = getApiKey();
if (!apiKey) {
console.warn('⚠️ 未设置 DeepSeek API Key,正在打开设置面板...');
setTimeout(showSettingsPanel, 1000);
return;
}
console.log('✅ DeepSeek 配置已加载');
// ========== 高校邦题目提取器 ==========
function extractQuestion() {
const selectors = [
'.exam-question', '.question-title', '.quiz-question',
'.question-content', '.subject-title', '.stem',
'.question-item .title', '.exam-item .title'
];
for (let sel of selectors) {
const el = document.querySelector(sel);
if (el) return el.innerText.trim();
}
const container = document.querySelector('.question-panel, .exam-panel, .quiz-panel, .paper-content');
if (container) {
const title = container.querySelector('h3, h4, .title, .text');
if (title) return title.innerText.trim();
}
return null;
}
function extractOptions() {
const options = [];
const optionItems = document.querySelectorAll('.option-item, .exam-option, .choice-item, .question-option, li');
if (optionItems.length) {
optionItems.forEach(el => {
const text = el.innerText.trim();
if (text && /^[A-Z][\.、\s]/.test(text)) options.push(text);
else if (text) options.push(text);
});
} else {
document.querySelectorAll('label').forEach(el => {
const text = el.innerText.trim();
if (text && /^[A-Z][\.、\s]/.test(text)) options.push(text);
});
}
return [...new Set(options.filter(opt => opt.length > 0))];
}
// ========== AI 答题核心 ==========
async function answerCurrentQuestion() {
const apiKeyNow = getApiKey();
if (!apiKeyNow) {
alert('请先设置 API Key!');
showSettingsPanel();
return;
}
const question = extractQuestion();
if (!question) {
alert('❌ 未检测到题目,请确保在考试/测验页面');
return;
}
const options = extractOptions();
if (!options.length) {
alert('❌ 未检测到选项');
return;
}
const optionsText = options.map((opt, i) => `${String.fromCharCode(65 + i)}. ${opt}`).join('\n');
const prompt = `请回答以下题目,只返回正确答案的字母(如 A, B, C 或 AB):\n题目:${question}\n选项:\n${optionsText}`;
const apiUrl = getApiUrl();
const model = getModel();
console.log('🤖 正在调用 AI...');
console.log(' 题目:', question);
console.log(' 选项:', optionsText);
GM_xmlhttpRequest({
method: 'POST',
url: apiUrl,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKeyNow}`
},
data: JSON.stringify({
model: model,
messages: [
{ role: 'system', content: '你是一个考试答题助手,只返回正确答案的字母,不要任何解释。' },
{ role: 'user', content: prompt }
],
temperature: 0.1
}),
onload: function(resp) {
try {
const data = JSON.parse(resp.responseText);
const answer = data.choices[0].message.content.trim();
console.log('✅ AI 答案:', answer);
selectOption(answer);
} catch (e) {
console.error('解析 AI 响应失败:', e);
alert('AI 响应解析失败,请查看控制台');
}
},
onerror: function(err) {
console.error('API 请求失败:', err);
alert('AI 调用失败,请检查网络或 API 地址是否正确。');
}
});
}
// ========== 选项选择 ==========
function selectOption(answer) {
const letters = answer.match(/[A-D]/gi);
if (!letters) {
console.warn('⚠️ 未识别到有效答案字母:', answer);
alert('AI 返回的答案格式异常: ' + answer);
return;
}
const optionEls = document.querySelectorAll('.option-item, .exam-option, .choice-item, .question-option');
if (optionEls.length) {
letters.forEach(letter => {
const index = letter.toUpperCase().charCodeAt(0) - 65;
if (index < optionEls.length) {
const input = optionEls[index].querySelector('input[type="radio"], input[type="checkbox"]');
if (input) {
input.checked = true;
input.click();
}
}
});
} else {
const inputs = document.querySelectorAll('input[type="radio"], input[type="checkbox"]');
letters.forEach(letter => {
const index = letter.toUpperCase().charCodeAt(0) - 65;
if (inputs[index]) {
inputs[index].checked = true;
inputs[index].click();
}
});
}
console.log('📌 已选择:', letters.join(', '));
}
// ========== 浮动控制面板 ==========
function addControlPanel() {
const panel = document.createElement('div');
panel.style.position = 'fixed';
panel.style.bottom = '20px';
panel.style.right = '20px';
panel.style.zIndex = 9998;
panel.style.background = '#1fb6ff';
panel.style.color = 'white';
panel.style.padding = '12px 18px';
panel.style.borderRadius = '12px';
panel.style.boxShadow = '0 4px 15px rgba(0,0,0,0.2)';
panel.style.display = 'flex';
panel.style.alignItems = 'center';
panel.style.gap = '12px';
panel.style.fontFamily = 'Arial, sans-serif';
const status = document.createElement('span');
status.innerText = '🤖 AI 就绪';
status.style.fontWeight = 'bold';
const btn = document.createElement('button');
btn.innerText = '答题';
btn.style.background = 'white';
btn.style.color = '#1fb6ff';
btn.style.border = 'none';
btn.style.padding = '6px 16px';
btn.style.borderRadius = '6px';
btn.style.cursor = 'pointer';
btn.style.fontWeight = 'bold';
btn.style.fontSize = '14px';
btn.onclick = () => {
status.innerText = '🤖 AI 思考中...';
answerCurrentQuestion().finally(() => {
status.innerText = '🤖 AI 就绪';
});
};
// 添加小齿轮设置按钮
const settingsBtn = document.createElement('button');
settingsBtn.innerText = '⚙️';
settingsBtn.style.background = 'transparent';
settingsBtn.style.color = 'white';
settingsBtn.style.border = 'none';
settingsBtn.style.fontSize = '18px';
settingsBtn.style.cursor = 'pointer';
settingsBtn.style.padding = '0 4px';
settingsBtn.title = '打开设置';
settingsBtn.onclick = showSettingsPanel;
panel.appendChild(status);
panel.appendChild(btn);
panel.appendChild(settingsBtn);
document.body.appendChild(panel);
}
setTimeout(addControlPanel, 1500);
})();