// ==UserScript== // @name 一个优化你使用AI时候的prompt智能体。 // @namespace prompt-agent // @version 0.0.5 // @description prompt-agent // @author LLinkedList771 // @run-at document-end // @match https://chat.deepseek.com/* // @match https://chatgpt.com/* // @match https://claude.ai/* // @homepageURL https://github.com/linkedlist771/prompt-agent // @supportURL https://github.com/linkedlist771/prompt-agent/issues // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/540649/%E4%B8%80%E4%B8%AA%E4%BC%98%E5%8C%96%E4%BD%A0%E4%BD%BF%E7%94%A8AI%E6%97%B6%E5%80%99%E7%9A%84prompt%E6%99%BA%E8%83%BD%E4%BD%93%E3%80%82.user.js // @updateURL https://update.greasyfork.icu/scripts/540649/%E4%B8%80%E4%B8%AA%E4%BC%98%E5%8C%96%E4%BD%A0%E4%BD%BF%E7%94%A8AI%E6%97%B6%E5%80%99%E7%9A%84prompt%E6%99%BA%E8%83%BD%E4%BD%93%E3%80%82.meta.js // ==/UserScript== (function() { 'use strict'; // 全局配置变量 let isEnabled = false; let apiKey = localStorage.getItem('ai-script-apikey') || ''; let isProcessing = false; let currentSiteConfig = null; // 网站配置 const SITE_CONFIGS = { 'chat.deepseek.com': { name: 'DeepSeek', textareaSelectors: ['textarea'], sendButtonSelectors: [ 'button[type="submit"]', '[data-testid="send-button"]', '.send-button' ], textInputMethod: 'simulate' // 'simulate' or 'direct' }, 'chatgpt.com': { name: 'ChatGPT', textareaSelectors: ['textarea', '[contenteditable="true"]'], sendButtonSelectors: [ 'button[data-testid="send-button"]', 'button[aria-label*="Send"]', 'button:has(svg)' ], textInputMethod: 'simulate' }, 'claude.ai': { name: 'Claude', textareaSelectors: ['div[contenteditable="true"]', 'textarea', '[contenteditable="true"]'], sendButtonSelectors: [ 'button[aria-label*="Send"]', 'button:has(svg)', 'button[type="submit"]' ], textInputMethod: 'simulate' } }; // API配置 const API_CONFIG = { endpoint: "https://promptagent.qqyunsd.com/api/v1/chat/completions", model: "mock-gpt-model", maxTokens: 512, temperature: 0.1 }; // 初始化网站配置 function initSiteConfig() { const hostname = window.location.hostname; currentSiteConfig = SITE_CONFIGS[hostname]; if (!currentSiteConfig) { console.warn('Prompt Agent: 不支持的网站:', hostname); return false; } console.log('Prompt Agent: 已加载配置for', currentSiteConfig.name); return true; } // UI管理类 class UIManager { constructor() { this.container = null; } // 创建浮动UI createFloatingUI() { const container = document.createElement('div'); container.id = 'ai-floating-ui'; container.style.cssText = ` position: fixed; top: 20px; right: 20px; width: 280px; background: #ffffff; border: 1px solid #ddd; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 10000; font-family: Arial, sans-serif; font-size: 14px; `; const siteName = currentSiteConfig ? currentSiteConfig.name : 'Unknown'; container.innerHTML = `
Prompt Agent (${siteName})
`; document.body.appendChild(container); this.container = container; // 绑定事件 this.bindEvents(); } bindEvents() { document.getElementById('ai-minimize').addEventListener('click', () => this.toggleMinimize()); document.getElementById('ai-enable').addEventListener('change', (e) => this.toggleEnable(e)); document.getElementById('ai-apikey').addEventListener('input', (e) => this.saveApiKey(e)); document.getElementById('ai-test').addEventListener('click', () => aiAgent.testConnection()); document.getElementById('ai-manual-send').addEventListener('click', () => aiAgent.manualSend()); // 监听Ctrl+Enter组合键 document.addEventListener('keydown', (e) => this.handleKeyDown(e)); } toggleMinimize() { const content = document.getElementById('ai-content'); const button = document.getElementById('ai-minimize'); if (content.style.display === 'none') { content.style.display = 'block'; button.textContent = '−'; } else { content.style.display = 'none'; button.textContent = '+'; } } toggleEnable(e) { isEnabled = e.target.checked; this.updateStatus(isEnabled ? '已启用 - 按Ctrl+Enter发送' : '已禁用'); } saveApiKey(e) { apiKey = e.target.value; localStorage.setItem('ai-script-apikey', apiKey); } updateStatus(message, isError = false) { const status = document.getElementById('ai-status'); if (status) { status.textContent = message; status.style.color = isError ? '#d32f2f' : '#666'; } } handleKeyDown(e) { if (e.ctrlKey && e.key === 'Enter' && isEnabled && !isProcessing) { const textarea = inputManager.findActiveTextarea(); if (textarea && inputManager.getTextContent(textarea).trim()) { e.preventDefault(); aiAgent.sendRequest(textarea); } } } } // 输入管理类 class InputManager { constructor(siteConfig) { this.siteConfig = siteConfig; } // 查找当前活跃的输入框 findActiveTextarea() { // 优先查找聚焦的输入框 for (const selector of this.siteConfig.textareaSelectors) { const focused = document.querySelector(selector + ':focus'); if (focused) return focused; } // 查找页面上的输入框 for (const selector of this.siteConfig.textareaSelectors) { const elements = document.querySelectorAll(selector); if (elements.length > 0) { return elements[elements.length - 1]; // 选择最后一个 } } return null; } // 获取文本内容(兼容textarea和contenteditable) getTextContent(element) { if (element.tagName.toLowerCase() === 'textarea') { return element.value; } else if (element.contentEditable === 'true' || element.getAttribute('contenteditable') === 'true') { return element.textContent || element.innerText || ''; } return ''; } // 设置文本内容 async setTextContent(element, text) { if (this.siteConfig.textInputMethod === 'simulate') { await this.simulateTextInput(element, text); } else { this.directSetText(element, text); } } // 模拟文字输入 async simulateTextInput(element, text) { element.focus(); await new Promise(resolve => setTimeout(resolve, 50)); // 获取当前内容并清空 const currentContent = this.getTextContent(element); if (currentContent.length > 0) { await this.clearContent(element); } // 逐字符输入 for (let i = 0; i < text.length; i++) { const char = text[i]; // 模拟按键事件 this.simulateKeyboardEvent(element, 'keydown', char); this.simulateKeyboardEvent(element, 'keypress', char); // 更新内容 if (element.tagName.toLowerCase() === 'textarea') { element.value = text.substring(0, i + 1); } else { element.textContent = text.substring(0, i + 1); } // 触发input事件 const inputEvent = new Event('input', { bubbles: true, cancelable: true, }); element.dispatchEvent(inputEvent); this.simulateKeyboardEvent(element, 'keyup', char); // 短暂延迟 if (i % 5 === 0) { await new Promise(resolve => setTimeout(resolve, 10)); } } // 确保光标在最后 if (element.tagName.toLowerCase() === 'textarea') { element.selectionStart = element.selectionEnd = element.value.length; } } // 清空内容 async clearContent(element) { element.focus(); await new Promise(resolve => setTimeout(resolve, 30)); // 对于contenteditable元素,使用不同的选择方法 if (element.tagName.toLowerCase() === 'textarea') { element.select(); } else { // 对于contenteditable div,选择所有内容 const range = document.createRange(); range.selectNodeContents(element); const selection = window.getSelection(); selection.removeAllRanges(); selection.addRange(range); } await new Promise(resolve => setTimeout(resolve, 30)); this.simulateKeyboardEvent(element, 'keydown', 'a', { ctrlKey: true }); this.simulateKeyboardEvent(element, 'keyup', 'a', { ctrlKey: true }); this.simulateKeyboardEvent(element, 'keydown', 'Delete'); if (element.tagName.toLowerCase() === 'textarea') { element.value = ''; } else { element.textContent = ''; element.innerHTML = ''; } const inputEvent = new Event('input', { bubbles: true, cancelable: true, inputType: 'deleteContentBackward' }); element.dispatchEvent(inputEvent); this.simulateKeyboardEvent(element, 'keyup', 'Delete'); await new Promise(resolve => setTimeout(resolve, 30)); } // 直接设置文本 directSetText(element, text) { if (element.tagName.toLowerCase() === 'textarea') { element.value = text; } else { element.textContent = text; } const inputEvent = new Event('input', { bubbles: true, cancelable: true, }); element.dispatchEvent(inputEvent); } // 模拟键盘事件 simulateKeyboardEvent(element, eventType, key, options = {}) { const event = new KeyboardEvent(eventType, { key: key, code: key, charCode: key.charCodeAt ? key.charCodeAt(0) : 0, keyCode: key.charCodeAt ? key.charCodeAt(0) : 0, which: key.charCodeAt ? key.charCodeAt(0) : 0, bubbles: true, cancelable: true, ...options }); element.dispatchEvent(event); } // 查找发送按钮 findSendButton() { for (const selector of this.siteConfig.sendButtonSelectors) { try { const button = document.querySelector(selector); if (button && button.offsetParent !== null) { return button; } } catch (e) { // 忽略无效的选择器 } } // 查找包含发送文本的按钮 const buttons = document.querySelectorAll('button'); for (const button of buttons) { const text = button.textContent || button.innerText || ''; if ((text.includes('发送') || text.includes('Send') || text.includes('提交')) && button.offsetParent !== null) { return button; } } return null; } } // AI代理类 class AIAgent { constructor(uiManager, inputManager) { this.uiManager = uiManager; this.inputManager = inputManager; } async testConnection() { if (!apiKey.trim()) { this.uiManager.updateStatus('请先输入API Key', true); return; } this.uiManager.updateStatus('测试连接中...'); try { const response = await fetch(API_CONFIG.endpoint, { method: 'POST', headers: { "authorization": `Bearer ${apiKey}`, "User-Agent": "Apifox/1.0.0 (https://apifox.com)", "Content-Type": "application/json" }, body: JSON.stringify({ "model": API_CONFIG.model, "messages": [ { "role": "user", "content": "test" } ], "max_tokens": 10, "temperature": API_CONFIG.temperature, "stream": false }) }); if (response.ok) { this.uiManager.updateStatus('连接成功!'); } else { this.uiManager.updateStatus(`连接失败: ${response.status}`, true); } } catch (error) { this.uiManager.updateStatus(`连接错误: ${error.message}`, true); } } manualSend() { if (isProcessing) { this.uiManager.updateStatus('正在处理中,请稍后...', true); return; } const textarea = this.inputManager.findActiveTextarea(); if (!textarea) { this.uiManager.updateStatus('未找到输入框', true); return; } const content = this.inputManager.getTextContent(textarea); if (!content.trim()) { this.uiManager.updateStatus('请先输入内容', true); return; } this.sendRequest(textarea); } async sendRequest(textarea) { if (!apiKey.trim()) { this.uiManager.updateStatus('请先输入API Key', true); return; } isProcessing = true; this.uiManager.updateStatus('处理中...'); const userInput = this.inputManager.getTextContent(textarea).trim(); const myHeaders = new Headers(); myHeaders.append("authorization", `Bearer ${apiKey}`); myHeaders.append("User-Agent", "Apifox/1.0.0 (https://apifox.com)"); myHeaders.append("Content-Type", "application/json"); const raw = JSON.stringify({ "model": API_CONFIG.model, "messages": [ { "role": "user", "content": userInput } ], "max_tokens": API_CONFIG.maxTokens, "temperature": API_CONFIG.temperature, "stream": true }); const requestOptions = { method: 'POST', headers: myHeaders, body: raw, redirect: 'follow' }; try { const response = await fetch(API_CONFIG.endpoint, requestOptions); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const reader = response.body.getReader(); const decoder = new TextDecoder(); this.uiManager.updateStatus('接收响应中...'); let aiResponse = ''; let isFirstContent = true; // 标记是否是第一次接收到内容 while (true) { const { done, value } = await reader.read(); if (done) { break; } const chunk = decoder.decode(value, { stream: true }); const lines = chunk.split('\n'); for (const line of lines) { if (line.startsWith('data: ')) { const jsonStr = line.substring(6).trim(); if (jsonStr === '[DONE]') { continue; } try { const data = JSON.parse(jsonStr); if (data.choices && data.choices[0] && data.choices[0].delta && data.choices[0].delta.content) { const content = data.choices[0].delta.content; aiResponse += content; // 如果是第一次接收到内容,先清空输入框 if (isFirstContent) { this.uiManager.updateStatus('开始输出响应...'); await this.inputManager.setTextContent(textarea, ''); isFirstContent = false; } // 逐字符添加到输入框 for (const char of content) { if (textarea.tagName.toLowerCase() === 'textarea') { textarea.value += char; } else { // 对于contenteditable元素,追加文本内容 textarea.textContent += char; } // 触发input事件 const inputEvent = new Event('input', { bubbles: true, cancelable: true, }); textarea.dispatchEvent(inputEvent); // 模拟打字效果 await new Promise(resolve => setTimeout(resolve, 20)); // 滚动到底部 if (textarea.scrollTop !== undefined) { textarea.scrollTop = textarea.scrollHeight; } } } } catch (parseError) { console.log('JSON解析错误:', parseError, jsonStr); } } } } this.uiManager.updateStatus('完成!'); } catch (error) { this.uiManager.updateStatus(`错误: ${error.message}`, true); console.error('请求错误:', error); } finally { isProcessing = false; } } } // 全局实例 let uiManager, inputManager, aiAgent; // 初始化 function init() { // 检查是否支持当前网站 if (!initSiteConfig()) { console.warn('Prompt Agent: 当前网站不受支持'); return; } // 创建管理器实例 uiManager = new UIManager(); inputManager = new InputManager(currentSiteConfig); aiAgent = new AIAgent(uiManager, inputManager); // 等待页面加载完成后创建UI if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { uiManager.createFloatingUI(); uiManager.updateStatus(`就绪 - 支持${currentSiteConfig.name} - 按Ctrl+Enter发送`); }); } else { uiManager.createFloatingUI(); uiManager.updateStatus(`就绪 - 支持${currentSiteConfig.name} - 按Ctrl+Enter发送`); } } init(); })();