// ==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();
});
})();