GlobalChatgpt Pro

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

目前為 2025-07-06 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 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 第二部分已加载');

})();