ChatGPT快捷深度搜索(o4-mini联网并自动发送)- 可拖动版

一键切换 o4-mini 模型、开启网络搜索并自动发送消息,按钮可拖动并记忆位置

// ==UserScript==
// @name         ChatGPT快捷深度搜索(o4-mini联网并自动发送)- 可拖动版
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  一键切换 o4-mini 模型、开启网络搜索并自动发送消息,按钮可拖动并记忆位置
// @author       schweigen
// @match        https://chatgpt.com/*
// @license      MIT
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    // 默认位置设置
    const DEFAULT_POSITION = {
        top: '30%',
        right: '0px'
    };

    // 从存储中获取位置,如果没有则使用默认值
    let buttonPosition = GM_getValue('o4MiniButtonPosition', DEFAULT_POSITION);

    // Flag to ensure we only modify one request after button click
    let pendingModelSwitch = false;

    // Flag to track if we need to click the send button
    let needToClickSendButton = false;

    // Timeout setting for waiting for the send button to appear
    const SEND_BUTTON_WAIT_TIMEOUT = 5000;

    // 注册(不可用)油猴菜单命令
    GM_registerMenuCommand('重置按钮位置', resetButtonPosition);

    // 重置按钮位置函数
    function resetButtonPosition() {
        buttonPosition = DEFAULT_POSITION;
        GM_setValue('o4MiniButtonPosition', buttonPosition);

        // 如果按钮已存在,则更新其位置
        const button = document.getElementById('o4-mini-button');
        if (button) {
            button.style.top = buttonPosition.top;
            button.style.right = buttonPosition.right;
            button.style.left = 'auto';
            showNotification('按钮位置已重置');
        }
    }

    // Intercept fetch requests to modify the model
    const originalFetch = window.fetch;
    window.fetch = async function(url, options) {
        // Only modify the next conversation API request after button click
        if (pendingModelSwitch && typeof url === 'string' && url.endsWith("/backend-api/conversation") && options?.method === "POST") {
            // Parse the request body
            let body = JSON.parse(options.body);
            console.log('Switching model for this conversation to o4-mini');

            // Change the model to o4-mini
            body.model = "o4-mini";

            // Update the request with modified body
            options.body = JSON.stringify(body);

            // Reset the flag - we only want to modify one request
            pendingModelSwitch = false;
        }

        // Send the request (modified or original)
        return originalFetch(url, options);
    };

    // Function to wait for and click the send button
    function waitForAndClickSendButton() {
        if (!needToClickSendButton) return;

        let hasClickedSendButton = false;

        // Use MutationObserver to watch for the send button to appear and become enabled
        const observer = new MutationObserver(function(mutations) {
            if (hasClickedSendButton) return; // If already clicked, do nothing

            // Try to find the send button with precise selector
            const sendButton = document.querySelector('button#composer-submit-button[data-testid="send-button"]');

            if (sendButton && !sendButton.disabled) {
                console.log("Found send button, preparing to click");
                hasClickedSendButton = true; // Mark as clicked

                // Create and dispatch a click event
                const clickEvent = new MouseEvent('click', {
                    bubbles: true,
                    cancelable: true,
                    view: window
                });

                // Dispatch the event to the send button
                sendButton.dispatchEvent(clickEvent);
                console.log("Clicked send button, stopping observer");

                // Reset the flag
                needToClickSendButton = false;

                // Stop observing
                observer.disconnect();
            }
        });

        // Start observing the document
        observer.observe(document, { childList: true, subtree: true });

        // Set a timeout to stop watching after the specified time
        setTimeout(function() {
            if (!hasClickedSendButton) {
                console.log("Reached maximum wait time, stopping observer");
                observer.disconnect();
                needToClickSendButton = false;
            }
        }, SEND_BUTTON_WAIT_TIMEOUT);
    }

    // 拖动功能的实现
    function makeDraggable(element) {
        let isDragging = false;
        let startY = 0;
        let startTop = 0;

        // 鼠标按下时的处理函数
        element.addEventListener('mousedown', function(e) {
            // 只有当不是点击事件时才进行拖动(例如按住超过200ms)
            const downTime = Date.now();

            const onMouseMove = function(moveEvent) {
                // 检查是否已经按住足够长的时间
                if (!isDragging && Date.now() - downTime > 200) {
                    isDragging = true;
                    element.style.cursor = 'move';
                    startY = moveEvent.clientY;
                    startTop = parseInt(element.style.top) || 0;
                }

                if (isDragging) {
                    // 计算新的位置
                    const newTop = startTop + (moveEvent.clientY - startY);

                    // 确保按钮不会超出屏幕
                    const maxTop = window.innerHeight - element.offsetHeight;
                    const topPosition = Math.max(0, Math.min(newTop, maxTop));

                    // 应用新位置
                    element.style.top = `${topPosition}px`;

                    // 阻止默认行为和事件冒泡
                    moveEvent.preventDefault();
                    moveEvent.stopPropagation();
                }
            };

            const onMouseUp = function() {
                if (isDragging) {
                    // 拖动结束,保存位置
                    buttonPosition = {
                        top: element.style.top,
                        right: element.style.right
                    };
                    GM_setValue('o4MiniButtonPosition', buttonPosition);

                    element.style.cursor = 'pointer';
                    isDragging = false;

                    // 显示提示
                    showNotification('按钮位置已保存');
                }

                // 移除事件监听器
                document.removeEventListener('mousemove', onMouseMove);
                document.removeEventListener('mouseup', onMouseUp);
            };

            // 添加事件监听器
            document.addEventListener('mousemove', onMouseMove);
            document.addEventListener('mouseup', onMouseUp);
        });
    }

    // Function to create and add our button
    function addO4MiniButton() {
        // Check if button already exists
        if (document.getElementById('o4-mini-button')) return;

        // Create button container
        const buttonContainer = document.createElement('div');
        buttonContainer.id = 'o4-mini-button';
        buttonContainer.style.cssText = `
            position: fixed;
            top: ${buttonPosition.top};
            right: ${buttonPosition.right};
            z-index: 10000;
            display: flex;
            align-items: center;
            justify-content: center;
            width: 36px;
            height: 36px;
            background: linear-gradient(140.91deg, #7367F0 12.61%, #574AB8 76.89%);
            color: white;
            border-top-left-radius: 6px;
            border-bottom-left-radius: 6px;
            font-weight: bold;
            cursor: pointer;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
            transition: background 0.3s ease;
            font-size: 18px;
            user-select: none;
        `;

        buttonContainer.textContent = '搜';

        // 添加拖动功能
        makeDraggable(buttonContainer);

        // Add click handler
        buttonContainer.addEventListener('click', function(e) {
            // 阻止因为拖动导致的点击触发
            if (e.detail === 0) return;

            // Set flag to modify the next conversation request
            pendingModelSwitch = true;

            // Set flag to click send button after activating search
            needToClickSendButton = true;

            // 立即开始监听发送按钮 - 不等待搜索按钮的激活
            waitForAndClickSendButton();

            // 同时处理搜索按钮
            const searchButtons = Array.from(document.querySelectorAll('button[aria-label="Search"]'));
            if (searchButtons.length > 0) {
                // Find the search button that's not already activated
                const searchButton = searchButtons.find(btn => btn.getAttribute('aria-pressed') === 'false');
                if (searchButton) {
                    searchButton.click();
                    showNotification('已激活联网搜索和自动发送');
                } else {
                    showNotification('联网搜索已激活,准备发送');
                }
            } else {
                showNotification('未找到搜索按钮,但会尝试发送');
            }

            // Visual feedback
            this.style.background = 'linear-gradient(140.91deg, #2ecc71 12.61%, #3498db 76.89%)';

            // Reset button after 3 seconds
            setTimeout(() => {
                this.style.background = 'linear-gradient(140.91deg, #7367F0 12.61%, #574AB8 76.89%)';
            }, 3000);
        });

        // Add button to the page
        document.body.appendChild(buttonContainer);
    }

    // Simple notification function
    function showNotification(message) {
        const notification = document.createElement('div');
        notification.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            background: rgba(0, 0, 0, 0.8);
            color: white;
            padding: 10px 20px;
            border-radius: 4px;
            z-index: 10001;
            transition: opacity 0.3s ease;
        `;
        notification.textContent = message;
        document.body.appendChild(notification);

        // Remove notification after 3 seconds
        setTimeout(() => {
            notification.style.opacity = '0';
            setTimeout(() => notification.remove(), 300);
        }, 3000);
    }

    // Periodically check and add button if it doesn't exist
    function checkAndAddButton() {
        if (document.body) {
            addO4MiniButton();
        }
    }

    // Initial attempt to add button
    if (document.readyState === 'complete' || document.readyState === 'interactive') {
        checkAndAddButton();
    } else {
        document.addEventListener('DOMContentLoaded', checkAndAddButton);
    }

    // Keep checking periodically in case the DOM changes
    setInterval(checkAndAddButton, 2000);
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址