您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
多密钥支持(仅自定义)、拖动、主题切换、卡通背景、真实聊天的增强脚本
当前为
// ==UserScript== // @name GlobalChatgpt Pro // @namespace http://tampermonkey.net/ // @version 1.5 // @description 多密钥支持(仅自定义)、拖动、主题切换、卡通背景、真实聊天的增强脚本 // @author maken // @match *://*/* // @grant GM_getValue // @grant GM_setValue // @grant GM_notification // @grant GM_xmlhttpRequest // @connect api.openai.com // @license MIT (https://opensource.org/license/mit/) // ==/UserScript== (function () { 'use strict'; // 只支持自定义密钥,无预设密钥 const CONFIG = { customKey: GM_getValue('gcui_apiKey', ''), theme: GM_getValue('gcui_theme', 'pink'), expanded: true, posX: GM_getValue('gcui_posX', 100), posY: GM_getValue('gcui_posY', 100), }; const state = { container: null, header: null, body: null, inputArea: null, input: null, sendBtn: null, themeBtn: null, keyBtn: null, }; function getCurrentApiKey() { return CONFIG.customKey || ''; } function savePosition(x, y) { GM_setValue('gcui_posX', x); GM_setValue('gcui_posY', y); } function showMessage(sender, text) { const msg = document.createElement('div'); Object.assign(msg.style, { marginBottom: '8px', wordBreak: 'break-word', backgroundColor: sender === '我' ? 'rgba(255,255,255,0.7)' : 'rgba(255,255,255,0.3)', padding: '6px 10px', borderRadius: '6px', alignSelf: sender === '我' ? 'flex-end' : 'flex-start', maxWidth: '80%' }); msg.innerHTML = `<strong style="color:${sender === '我' ? '#d6006e' : '#333'}">${sender}:</strong> ${text}`; state.body.appendChild(msg); state.body.scrollTop = state.body.scrollHeight; } async function sendMessage() { const text = state.input.value.trim(); if (!text) return; const apiKey = getCurrentApiKey(); if (!apiKey || !apiKey.startsWith('sk-')) { GM_notification({ text: '请设置有效的 API 密钥', timeout: 2000 }); return; } showMessage('我', text); state.input.value = ''; state.input.focus(); const payload = { model: 'gpt-3.5-turbo', messages: [{ role: 'user', content: text }], temperature: 0.7 }; GM_xmlhttpRequest({ method: 'POST', url: 'https://api.openai.com/v1/chat/completions', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` }, data: JSON.stringify(payload), responseType: 'json', onload: function (res) { const data = res.response; console.log('[返回数据]', data); if (!data || data.error) { const msg = data?.error?.message || '请求失败'; showMessage('系统', `错误:${msg}`); return; } const reply = data.choices?.[0]?.message?.content?.trim(); if (reply) { showMessage('AI', reply); } else { showMessage('系统', '无有效回复,检查模型或密钥'); } }, onerror: function (err) { console.error('请求失败:', err); showMessage('系统', '请求失败,请检查网络或密钥'); } }); } function updateTheme() { const t = CONFIG.theme; const { container, body, inputArea, sendBtn, themeBtn } = state; container.style.backgroundColor = t === 'pink' ? '#ffc0d9' : '#2c2c2c'; state.header.style.backgroundColor = t === 'pink' ? '#ff66b2' : '#444'; state.header.style.color = t === 'pink' ? '#fff' : '#ddd'; body.style.color = t === 'pink' ? '#333' : '#ddd'; if (t === 'pink') { body.style.backgroundImage = 'url("https://img.redocn.com/sheji/20240607/keaikatongmaosucaituAItu_13339783.jpg")'; body.style.backgroundColor = '#fff0f7'; themeBtn.textContent = '🌙'; } else { body.style.backgroundImage = 'none'; body.style.backgroundColor = '#222'; themeBtn.textContent = '☀️'; } inputArea.style.backgroundColor = t === 'pink' ? '#ffd6e8' : '#333'; sendBtn.style.backgroundColor = t === 'pink' ? '#ff66b2' : '#666'; } async function buildUI() { const container = document.createElement('div'); Object.assign(container.style, { position: 'fixed', top: CONFIG.posY + 'px', left: CONFIG.posX + 'px', width: '360px', height: '500px', borderRadius: '10px', boxShadow: '0 0 10px rgba(0,0,0,0.3)', zIndex: '999999', display: 'flex', flexDirection: 'column', userSelect: 'none' }); // Header const header = document.createElement('div'); Object.assign(header.style, { padding: '10px', fontSize: '18px', fontWeight: 'bold', display: 'flex', justifyContent: 'space-between', alignItems: 'center', cursor: 'grab' }); const title = document.createElement('span'); title.textContent = 'GlobalChatUI Pro'; header.appendChild(title); const controls = document.createElement('div'); controls.style.display = 'flex'; controls.style.gap = '6px'; controls.style.alignItems = 'center'; // Theme button const themeBtn = document.createElement('button'); themeBtn.style.cssText = 'font-size:16px;background:#fff;border:none;border-radius:4px;padding:3px 6px;cursor:pointer;color:#333;'; controls.appendChild(themeBtn); state.themeBtn = themeBtn; // 自定义密钥按钮(钥匙图标) const keyBtn = document.createElement('button'); keyBtn.title = '点击设置自定义密钥'; keyBtn.textContent = '🔑'; keyBtn.style.cssText = 'font-size:18px;background:#fff;border:none;border-radius:4px;padding:3px 6px;cursor:pointer;color:#333;'; controls.appendChild(keyBtn); state.keyBtn = keyBtn; keyBtn.addEventListener('click', async () => { const newKey = prompt('请输入自定义密钥(以 sk- 开头)', CONFIG.customKey); if (newKey !== null) { CONFIG.customKey = newKey.trim(); await GM_setValue('gcui_apiKey', CONFIG.customKey); GM_notification({ text: CONFIG.customKey ? '自定义密钥已保存' : '已清除密钥', timeout: 2000 }); } }); // Toggle Expand button const toggleBtn = document.createElement('button'); toggleBtn.textContent = CONFIG.expanded ? '−' : '+'; toggleBtn.style.cssText = 'font-size:20px;background:none;border:none;cursor:pointer;'; controls.appendChild(toggleBtn); header.appendChild(controls); container.appendChild(header); state.header = header; // Body const body = document.createElement('div'); Object.assign(body.style, { flex: '1', overflowY: 'auto', padding: '10px', fontSize: '14px', display: CONFIG.expanded ? 'flex' : 'none', flexDirection: 'column' }); container.appendChild(body); state.body = body; // Input Area const inputArea = document.createElement('div'); Object.assign(inputArea.style, { display: CONFIG.expanded ? 'flex' : 'none', padding: '10px', borderTop: '1px solid #ccc', alignItems: 'center', gap: '5px' }); const input = document.createElement('textarea'); input.rows = 2; input.placeholder = '请输入消息...'; Object.assign(input.style, { flex: '1', resize: 'none', borderRadius: '5px', border: '1px solid #ccc', padding: '5px', fontSize: '14px', fontFamily: 'inherit' }); const sendBtn = document.createElement('button'); sendBtn.textContent = '发送'; sendBtn.style.cssText = 'padding:6px 15px;border:none;border-radius:5px;color:#fff;cursor:pointer;'; inputArea.appendChild(input); inputArea.appendChild(sendBtn); container.appendChild(inputArea); state.container = container; state.input = input; state.sendBtn = sendBtn; state.inputArea = inputArea; // Toggle expand event toggleBtn.addEventListener('click', () => { CONFIG.expanded = !CONFIG.expanded; container.style.height = CONFIG.expanded ? '500px' : '40px'; body.style.display = CONFIG.expanded ? 'flex' : 'none'; inputArea.style.display = CONFIG.expanded ? 'flex' : 'none'; toggleBtn.textContent = CONFIG.expanded ? '−' : '+'; }); // Drag to move let dragging = false, offsetX = 0, offsetY = 0; header.addEventListener('mousedown', e => { dragging = true; offsetX = e.clientX - container.offsetLeft; offsetY = e.clientY - container.offsetTop; header.style.cursor = 'grabbing'; }); document.addEventListener('mouseup', () => { if (dragging) { dragging = false; header.style.cursor = 'grab'; savePosition(container.offsetLeft, container.offsetTop); } }); document.addEventListener('mousemove', e => { if (!dragging) return; let x = e.clientX - offsetX, y = e.clientY - offsetY; x = Math.max(0, Math.min(x, window.innerWidth - container.offsetWidth)); y = Math.max(0, Math.min(y, window.innerHeight - container.offsetHeight)); container.style.left = x + 'px'; container.style.top = y + 'px'; }); // Send message events sendBtn.addEventListener('click', sendMessage); input.addEventListener('keydown', e => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendBtn.click(); } }); // Theme switch event themeBtn.addEventListener('click', async () => { CONFIG.theme = CONFIG.theme === 'pink' ? 'dark' : 'pink'; await GM_setValue('gcui_theme', CONFIG.theme); updateTheme(); }); document.body.appendChild(container); updateTheme(); } buildUI(); })(); // ==UserScript== // @name GlobalChatUI Pro 第二部分 修正版 // @match *://*/* // @grant GM_getValue // @grant GM_setValue // @grant GM_notification // ==/UserScript== (async function() { 'use strict'; // 等待DOM准备 await new Promise(resolve => { if (document.readyState === 'complete' || document.readyState === 'interactive') resolve(); else window.addEventListener('DOMContentLoaded', resolve); }); // 获取DOM元素,确认ID是第1部分中对应的 const container = document.getElementById('gcui-container'); const header = document.getElementById('gcui-header'); const body = document.getElementById('gcui-body'); const inputArea = document.getElementById('gcui-input-area'); const sendBtn = document.getElementById('gcui-send-btn'); if (!container || !header || !body || !inputArea || !sendBtn) { console.error('缺少必要DOM元素,请确认前置代码生成'); return; } // 异步读取配置 let theme = 'pink'; let posX = 100; let posY = 100; try { theme = await GM_getValue('gcui_theme', 'pink'); posX = await GM_getValue('gcui_posX', 100); posY = await GM_getValue('gcui_posY', 100); } catch(e) { console.warn('读取配置失败,使用默认值', e); } // 初始化位置样式 container.style.position = 'fixed'; container.style.left = posX + 'px'; container.style.top = posY + 'px'; container.style.zIndex = 99999; container.style.userSelect = 'none'; // 拖拽相关变量 let dragging = false; let offsetX = 0; let offsetY = 0; header.style.cursor = 'grab'; header.style.userSelect = 'none'; header.addEventListener('mousedown', e => { dragging = true; offsetX = e.clientX - container.offsetLeft; offsetY = e.clientY - container.offsetTop; header.style.cursor = 'grabbing'; e.preventDefault(); }); document.addEventListener('mouseup', async () => { if (dragging) { dragging = false; header.style.cursor = 'grab'; try { await GM_setValue('gcui_posX', container.offsetLeft); await GM_setValue('gcui_posY', container.offsetTop); } catch(e) { console.warn('保存位置失败', e); } } }); document.addEventListener('mousemove', e => { if (!dragging) return; let x = e.clientX - offsetX; let y = e.clientY - offsetY; x = Math.min(Math.max(0, x), window.innerWidth - container.offsetWidth); y = Math.min(Math.max(0, y), window.innerHeight - container.offsetHeight); container.style.left = x + 'px'; container.style.top = y + 'px'; }); // 主题应用函数 function applyTheme(currentTheme) { if (currentTheme === 'pink') { container.style.backgroundColor = '#ffc0d9'; header.style.backgroundColor = '#ff66b2'; header.style.color = '#fff'; body.style.backgroundColor = '#fff0f7'; body.style.color = '#333'; inputArea.style.backgroundColor = '#ffd6e8'; sendBtn.style.backgroundColor = '#ff66b2'; sendBtn.style.color = '#fff'; } else { container.style.backgroundColor = '#2c2c2c'; header.style.backgroundColor = '#444'; header.style.color = '#ddd'; body.style.backgroundColor = '#222'; body.style.color = '#ddd'; inputArea.style.backgroundColor = '#333'; sendBtn.style.backgroundColor = '#666'; sendBtn.style.color = '#fff'; } } // 创建并插入主题切换按钮 const themeToggleBtn = document.createElement('button'); themeToggleBtn.textContent = theme === 'pink' ? '切换暗黑' : '切换粉色'; themeToggleBtn.style.marginLeft = '10px'; themeToggleBtn.style.padding = '4px 10px'; themeToggleBtn.style.border = 'none'; themeToggleBtn.style.borderRadius = '4px'; themeToggleBtn.style.cursor = 'pointer'; themeToggleBtn.style.backgroundColor = '#fff'; themeToggleBtn.style.color = '#333'; themeToggleBtn.style.userSelect = 'none'; header.appendChild(themeToggleBtn); themeToggleBtn.addEventListener('click', async () => { theme = theme === 'pink' ? 'dark' : 'pink'; try { await GM_setValue('gcui_theme', theme); } catch(e) { console.warn('保存主题失败', e); } applyTheme(theme); themeToggleBtn.textContent = theme === 'pink' ? '切换暗黑' : '切换粉色'; }); applyTheme(theme); // 提示函数 function showToast(msg) { if (typeof GM_notification === 'function') { GM_notification({ text: msg, timeout: 2000, silent: true }); } else { alert(msg); } } showToast('GlobalChatUI Pro 第二部分已加载'); })();