您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Automatically sends Educake questions to ChatGPT API and displays responses inline (make sure to add in apikey)
// ==UserScript== // @name Educake ChatGPT Auto-Integration // @version 2.1 // @description Automatically sends Educake questions to ChatGPT API and displays responses inline (make sure to add in apikey) // @author frozled @ guns.lol/frozled // @match *://*.educake.co.uk/* // @grant GM_xmlhttpRequest // @grant GM_getValue // @grant GM_setValue // @connect api.openai.com // @license MIT // @namespace https://greasyfork.org/users/1390797 // ==/UserScript== (function() { 'use strict'; // Configuration object const config = { apiKey: '', // Store your API key here or use GM_getValue to get it from storage model: 'gpt-3.5-turbo', apiEndpoint: 'https://api.openai.com/v1/chat/completions' }; // Styles for the UI elements const styles = ` .gpt-response { margin-top: 10px; padding: 10px; border: 1px solid #e0e0e0; border-radius: 4px; background-color: #f8f9fa; } .gpt-loading { color: #666; font-style: italic; } .api-key-modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); z-index: 1000; width: 400px; } .modal-backdrop { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 999; } .error-message { color: #dc3545; margin-top: 10px; padding: 10px; border: 1px solid #dc3545; border-radius: 4px; background-color: #fff; } .debug-info { margin-top: 5px; font-size: 12px; color: #666; font-family: monospace; white-space: pre-wrap; } `; // Add styles to document function addStyles() { const styleSheet = document.createElement('style'); styleSheet.textContent = styles; document.head.appendChild(styleSheet); } // Create and show API key modal function showApiKeyModal() { const modalHtml = ` <div class="modal-backdrop"> <div class="api-key-modal"> <h3>Enter your OpenAI API Key</h3> <p>You need to enter your OpenAI API key to use this feature. You can get one from <a href="https://platform.openai.com/api-keys" target="_blank">OpenAI's website</a>.</p> <input type="password" id="api-key-input" placeholder="sk-..." style="width: 100%; margin: 10px 0; padding: 5px;"> <button id="save-api-key" style="padding: 5px 10px; background: #28a745; color: white; border: none; border-radius: 4px; cursor: pointer;">Save Key</button> <div id="api-key-error" style="color: red; margin-top: 10px;"></div> </div> </div> `; const modalContainer = document.createElement('div'); modalContainer.innerHTML = modalHtml; document.body.appendChild(modalContainer); document.getElementById('save-api-key').addEventListener('click', () => { const apiKey = document.getElementById('api-key-input').value.trim(); if (apiKey.startsWith('sk-') && apiKey.length > 20) { GM_setValue('openai_api_key', apiKey); config.apiKey = apiKey; modalContainer.remove(); } else { document.getElementById('api-key-error').textContent = 'Please enter a valid OpenAI API key (should start with sk-)'; } }); } // Function to get response from ChatGPT async function getGPTResponse(question) { if (!config.apiKey) { config.apiKey = GM_getValue('openai_api_key', ''); if (!config.apiKey) { showApiKeyModal(); return null; } } return new Promise((resolve, reject) => { const requestData = { model: config.model, messages: [{ role: 'user', content: question }], temperature: 0.7 }; GM_xmlhttpRequest({ method: 'POST', url: config.apiEndpoint, headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${config.apiKey}` }, data: JSON.stringify(requestData), onload: function(response) { try { const responseData = JSON.parse(response.responseText); console.log('API Response:', responseData); // Debug log if (response.status === 200 && responseData.choices && responseData.choices[0]) { resolve(responseData.choices[0].message.content); } else { const errorMessage = responseData.error ? responseData.error.message : 'Unknown error'; reject(new Error(`API Error (${response.status}): ${errorMessage}`)); } } catch (error) { console.error('Response parsing error:', error); reject(new Error(`Failed to parse API response: ${error.message}`)); } }, onerror: function(error) { console.error('Request error:', error); reject(new Error(`Network error: ${error.statusText || 'Failed to connect to API'}`)); } }); }); } // Function to create and inject the GPT button function injectGPTButton() { if (!document.getElementById('gpt-button')) { const button = document.createElement('div'); button.id = 'gpt-button'; button.className = 'btn bg-green-80 bg-green-hover r-bg-light r-bg-light-hover r-text-dark ml-2 lh-close mb-2 mb-sm-0 align-self-start'; button.textContent = 'Ask ChatGPT'; button.addEventListener('click', async function() { const questionElements = document.querySelectorAll('.question-text'); const question = Array.from(questionElements).map(el => el.textContent).join('\n'); // Create response container let responseContainer = document.querySelector('.gpt-response'); if (!responseContainer) { responseContainer = document.createElement('div'); responseContainer.className = 'gpt-response'; button.parentElement.appendChild(responseContainer); } responseContainer.innerHTML = '<div class="gpt-loading">Getting response from ChatGPT...</div>'; try { const response = await getGPTResponse(question); if (response) { responseContainer.innerHTML = ` <strong>ChatGPT Response:</strong> <div style="margin-top: 8px;">${response.replace(/\n/g, '<br>')}</div> `; } } catch (error) { responseContainer.innerHTML = ` <div class="error-message"> Error: ${error.message} <div class="debug-info"> Status: ${error.status || 'N/A'} Time: ${new Date().toISOString()} </div> </div> `; // If the error is related to authentication, show the API key modal if (error.message.includes('401') || error.message.includes('authentication')) { config.apiKey = ''; // Clear the invalid API key GM_setValue('openai_api_key', ''); // Clear stored key showApiKeyModal(); } } }); const existingDiv = document.querySelector('.column'); existingDiv.insertBefore(button, existingDiv.lastElementChild); } } // Remove paste restrictions function removePasteRestrictions() { const elements = document.querySelectorAll('.answer-text'); elements.forEach(element => { element.removeAttribute('onpaste'); }); } // Initialize function init() { addStyles(); setInterval(injectGPTButton, 500); setInterval(removePasteRestrictions, 500); } // Start the script init(); })();