GlobalChatgpt Pro

多密钥支持(仅自定义)、拖动、主题切换、卡通背景、真实聊天的增强脚本

当前为 2025-07-06 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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 第二部分已加载');

})();