您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
bilibili会员硬核答题
当前为
// ==UserScript== // @name B站硬核AI答题 // @namespace http://tampermonkey.net/ // @version 2025-04-12 // @description bilibili会员硬核答题 // @author BigShark667 // @match https://www.bilibili.com/h5/senior-newbie/qa // @run-at document-end // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @grant GM_xmlhttpRequest // @grant GM_notification // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.slim.min.js // @license Apache-2.0 // @connect api.deepseek.com // @connect api.gptapi.us // ==/UserScript== "use strict"; /* eslint-disable no-underscore-dangle, @typescript-eslint/no-empty-function */ (async () => { // Prevent tab visibility detection function disableVisibilityEvents() { window.onblur = () => {}; window.onfocus = () => {}; document.onfocusin = () => {}; document.onfocusout = () => {}; } // Override visibility event listeners disableVisibilityEvents(); document._addEventListener = document.addEventListener; document.addEventListener = (...argv) => { if (['visibilitychange', 'mozvisibilitychange', 'webkitvisibilitychange', 'msvisibilitychange'].includes(argv[0])) { return; } document._addEventListener(...argv); }; document._removeEventListener = document.removeEventListener; document.removeEventListener = (...argv) => { if (['visibilitychange', 'mozvisibilitychange', 'webkitvisibilitychange', 'msvisibilitychange'].includes(argv[0])) { return; } document._removeEventListener(...argv); }; window.onload = disableVisibilityEvents; // Configuration and state const API_CONFIG = { 'ChatAnywhere': { url: 'https://api.gptapi.us/v1/chat/completions', model: 'gpt-4o-mini' }, 'DeepSeek': { url: 'https://api.deepseek.com/v1/chat/completions', model: 'deepseek-chat' } }; let config = GM_getValue('API_CONFIG') || { key: '', provider: '' }; let prevQuestion = ''; let isRunning = false; let processedQuestions = []; // 新增:历史记录数组 // Register menu commands GM_registerMenuCommand('设置 ChatAnywhere API 密钥', () => { setupApiKey('ChatAnywhere'); }); GM_registerMenuCommand('设置 DeepSeek API 密钥', () => { setupApiKey('DeepSeek'); }); GM_registerMenuCommand('启动', () => { if (!config.key) { notify('错误', '请先设置 API 密钥!'); return; } if (isRunning) { notify('提示', '脚本已经在运行中'); return; } console.clear(); notify('提示', '启动后不要点击页面!'); isRunning = true; document.dispatchEvent(new Event('click')); }); GM_registerMenuCommand('停止', () => { isRunning = false; notify('提示', '脚本已停止'); }); // Setup functions function setupApiKey(provider) { const key = prompt(`请输入 ${provider} API 密钥:`, config.provider === provider ? config.key : ''); if (key) { config = { key, provider }; GM_setValue('API_CONFIG', config); notify('成功', `${provider} API 密钥已设置`); } } function notify(title, text) { GM_notification({ title: '哔哩哔哩硬核会员搜题GPT', text, timeout: 5000 }); } // Main process functions document.addEventListener('click', startProcess); async function startProcess() { if (!isRunning) return; try { const delayTime = getRandomDelay(5000, 10000); console.log(`等待 ${delayTime / 1000} 秒后开始搜索下一题`); await sleep(delayTime); const [question, elem] = extractQuestion(); if (!question) { console.log('未找到问题,稍后重试'); return setTimeout(startProcess, 5000); } if (question === prevQuestion) { console.log('题目未变化,稍后重试'); return setTimeout(startProcess, 5000); } prevQuestion = question; console.log('识别到问题:', question); const answer = await askGPT(question); const option = parseAnswer(answer); console.log(option); if (!option) { console.log('无法解析答案,随机选择'); selectRandomOption(); } else { console.log(`选择答案:${option}`); selectOption(option, elem); } } catch (error) { console.error('处理出错:', error); notify('错误', '处理题目时出错,稍后重试'); setTimeout(startProcess, 10000); } } function extractQuestion() { const questionDivs = $('div.senior-question').toArray(); for (const div of questionDivs) { const elements = $(div).find('.senior-question__qs, .senior-question__answer'); if (elements.length === 0) continue; const questionText = elements.toArray() .map(v => $(v).text().trim()) .join('\n') .replace(/\s+/g, ' '); // 清理多余空格 const hash = generateHash(questionText); if (!processedQuestions.includes(hash)) { processedQuestions.push(hash); // 保持历史记录最多50条 if (processedQuestions.length > 50) { processedQuestions.shift(); } return [questionText, div]; } } return null; } function generateHash(str) { // 改进的djb2哈希算法 let hash = 5381; for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = (hash * 33) ^ char; } return hash >>> 0; // 转换为无符号32位整数 } async function askGPT(question, retry = 0) { try { return await callGPTAPI(`请仅给出答案\n${question}`); } catch (error) { if (retry >= 3) { throw new Error('API 请求失败次数过多'); } console.log(`API 请求失败,${retry + 1}/3 次重试`); await sleep(5000); return askGPT(question, retry + 1); } } function parseAnswer(answer) { // First try to match a direct letter at the beginning or end let match = answer.match(/^\s*([A-D])[.\s:]|[.\s:]([A-D])\s*$/i); if (match) { return (match[1] || match[2]).toUpperCase(); } // Then try to find any letter within the answer match = answer.match(/\b([A-D])[.\s:]/i); if (match) { return match[1].toUpperCase(); } // Look for answer options by keyword const optionKeywords = { A: ['选A', '答案A', '答案是A', 'A选项', '选择A'], B: ['选B', '答案B', '答案是B', 'B选项', '选择B'], C: ['选C', '答案C', '答案是C', 'C选项', '选择C'], D: ['选D', '答案D', '答案是D', 'D选项', '选择D'] }; for (const [option, keywords] of Object.entries(optionKeywords)) { if (keywords.some(keyword => answer.includes(keyword))) { return option; } } return null; } function selectOption(option, questionDiv) { // const optionElement = $(questionDiv).find('span.senior-question__answer--icon:contains(${option})').parent()[0]; const optionElement = $(questionDiv) .find(`span.senior-question__answer--icon:contains(${option})`) .closest('.senior-question__answer') // 更精确的父级选择 .first()[0]; if (optionElement) { simulateClick(optionElement); } else { console.log(`未找到选项 ${option},随机选择`); selectRandomOption(); } } function selectRandomOption() { const options = $('span.senior-question__answer--icon').parent().toArray(); if (options.length > 0) { const randomIndex = Math.floor(Math.random() * options.length); simulateClick(options[randomIndex]); } } function simulateClick(element) { if (!element) return; const rect = element.getBoundingClientRect(); const randomX = rect.left + Math.random() * rect.width; const randomY = rect.top + Math.random() * rect.height; const clickEvent = new MouseEvent('click', { bubbles: true, cancelable: true, clientX: randomX, clientY: randomY, }); element.dispatchEvent(clickEvent); } // API functions function callGPTAPI(question) { return new Promise((resolve, reject) => { if (!config.key || !config.provider) { return reject(new Error('API 配置不完整')); } const apiConfig = API_CONFIG[config.provider]; if (!apiConfig) { return reject(new Error('未知的 API 提供商')); } const requestData = { "messages": [ { "content": question, "role": "system" } ], "temperature": 0.7, "model": apiConfig.model, "stream": false, "response_format": { "type": "text" } }; GM_xmlhttpRequest({ method: 'POST', url: apiConfig.url, headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${config.key}`, }, data: JSON.stringify(requestData), onload: function(response) { try { const data = JSON.parse(response.responseText); if (!data.choices || !data.choices[0] || !data.choices[0].message) { console.error('API 返回数据格式错误:', data); return reject(new Error('API 返回数据格式错误')); } const content = data.choices[0].message.content; console.log('API 返回结果:', content); resolve(content); } catch (error) { console.error('解析 API 响应失败:', error, response.responseText); reject(new Error('解析 API 响应失败')); } }, onerror: function(error) { console.error('API 请求失败:', error); reject(error); }, ontimeout: function() { console.error('API 请求超时'); reject(new Error('API 请求超时')); } }); }); } // Utility functions function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } function getRandomDelay(min, max) { return Math.floor(Math.random() * (max - min + 1) + min); } })();