chatgpt自动重复输入消息(可自定义输入内容)

一个增强ChatGPT体验的自动化工具,主要功能如下:

// ==UserScript==
// @name         chatgpt自动重复输入消息(可自定义输入内容)
// @namespace    http://tampermonkey.net/
// @version      2.7
// @description  一个增强ChatGPT体验的自动化工具,主要功能如下:
//               1. 在ChatGPT界面添加一个"继续"按钮
//               2. 支持自定义输入内容(默认为"继续")
//               3. 可以自动重复发送消息
//               4. 带有美观的图形界面和动画效果
//               5. 支持明暗主题自适应
//
//               使用方法:
//               1. 鼠标悬停在"继续"按钮上会显示输入框
//               2. 在输入框中可以自定义要重复发送的消息
//               3. 点击"确定"保存自定义消息
//               4. 点击"继续"按钮开始自动发送(再次点击停止)
//               5. 当ChatGPT回复完成后会自动发送下一条
//
//               特色功能:
//               - 优雅的界面设计和交互动画
//               - 可拖动的参考图片
//               - 智能等待机制,确保确发送
//               - ���美融入ChatGPT原生界面
// @author       喜乐BB844785535@gmail.com  Claude 和chatgpt共同制作
// @match        https://chatgpt.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    let autoRunInterval = null;
    let customMessage = '继续'; // 默认消息
    let hideTimeout = null; // 用于延迟隐藏

    function createButton() {
        const wrapper = document.createElement('div');
        wrapper.style.position = 'relative';
        wrapper.style.display = 'inline-block';

        const button = document.createElement('button');
        button.innerHTML = '继续';
        button.className = 'flex h-8 min-w-8 items-center justify-center rounded-lg p-1 text-xs font-semibold hover:bg-black/10 focus-visible:outline-black dark:focus-visible:outline-white';
        button.style.margin = '0 4px';
        button.setAttribute('aria-label', '发送继续');
        button.setAttribute('data-auto-running', 'false');

        wrapper.appendChild(button);

        // 创建输入框容器
        const inputContainer = document.createElement('div');
        inputContainer.style.cssText = `
            position: absolute;
            bottom: 100%;
            left: 50%;
            transform: translateX(-50%);
            margin-bottom: 12px;
            display: none;
            z-index: 10000;
            white-space: nowrap;
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            opacity: 0;
            background: rgba(244, 244, 244, 0.98);
            border-radius: 16px;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08);
            backdrop-filter: blur(8px);
        `;

        // 创建flex容器
        const inputWrapper = document.createElement('div');
        inputWrapper.style.cssText = `
            display: flex;
            align-items: center;
            gap: 8px;
            padding: 6px;
            background: transparent;
        `;

        // 创建输入框
        const input = document.createElement('input');
        input.type = 'text';
        input.value = ''; // 移除默认值
        input.placeholder = '请在这里输入你要重复输入的消息'; // 添加占位符文字
        input.style.cssText = `
            width: 200px;
            padding: 10px 14px;
            border: none;
            border-radius: 12px;
            font-size: 11px;
            line-height: 1.5;
            outline: none;
            background: rgba(255, 255, 255, 0.9);
            color: #333;
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.05);
            ::placeholder {
                color: rgba(0, 0, 0, 0.4);
                transition: all 0.3s ease;
            }
            :focus::placeholder {
                opacity: 0.5;
            }
        `;

        // 创建确定按钮
        const confirmButton = document.createElement('button');
        confirmButton.innerHTML = '确定';
        confirmButton.style.cssText = `
            padding: 10px 18px;
            background: #000000;
            color: white;
            border: none;
            border-radius: 12px;
            font-size: 14px;
            font-weight: 700;
            cursor: pointer;
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            white-space: nowrap;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
        `;

        // 添加按钮悬停效果
        confirmButton.addEventListener('mouseover', () => {
            confirmButton.style.transform = 'translateY(-1px)';
            confirmButton.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.2)';
            confirmButton.style.background = 'linear-gradient(45deg, #000000, #333333)';
        });

        confirmButton.addEventListener('mouseout', () => {
            confirmButton.style.transform = 'translateY(0)';
            confirmButton.style.boxShadow = '0 2px 8px rgba(0, 0, 0, 0.12)';
            confirmButton.style.background = '#000000';
        });

        // 添加输入框焦点效果
        input.addEventListener('focus', () => {
            clearTimeout(hideTimeout);
            input.style.background = '#ffffff';
            input.style.boxShadow = 'inset 0 1px 2px rgba(0, 0, 0, 0.05), 0 0 0 3px rgba(0, 0, 0, 0.05)';
        });

        input.addEventListener('blur', () => {
            input.style.background = 'rgba(255, 255, 255, 0.9)';
            input.style.boxShadow = 'inset 0 1px 2px rgba(0, 0, 0, 0.05)';
        });

        // 修改确定按钮点击事件
        confirmButton.addEventListener('click', () => {
            customMessage = input.value || '继续';
            inputContainer.style.opacity = '0';
            setTimeout(() => {
                inputContainer.style.display = 'none';
                // 如果当前没有在自动运行,则开启自动运行模式
                const button = document.querySelector('[aria-label="发送继续"]');
                if (button && button.getAttribute('data-auto-running') !== 'true') {
                    button.setAttribute('data-auto-running', 'true');
                    button.style.backgroundColor = '#4CAF50';
                    button.style.color = 'white';

                    executeContinue();

                    if (autoRunInterval) {
                        clearInterval(autoRunInterval);
                    }

                    autoRunInterval = setInterval(async () => {
                        try {
                            const stopButton = document.querySelector('[data-testid="stop-button"]');
                            if (!stopButton) {
                                await executeContinue();
                            }
                        } catch (error) {
                            console.error('自动运行出错:', error);
                            stopAutoRun();
                        }
                    }, 1000);
                }
            }, 200);
        });

        // 组装DOM
        inputWrapper.appendChild(input);
        inputWrapper.appendChild(confirmButton);
        inputContainer.appendChild(inputWrapper);
        wrapper.appendChild(inputContainer);

        // 创建可拖动的图片容器
        const imgContainer = document.createElement('div');
        imgContainer.style.cssText = `
            position: fixed;
            right: 20px;
            bottom: 20px;
            z-index: 10000;
            cursor: move;
            display: none;
            background: transparent;
            padding: 0;
        `;

        // 创建图片元素
        const img = document.createElement('img');
        img.src = 'https://pic.imgdb.cn/item/6767ecd3d0e0a243d4e80a94.png';
        img.style.cssText = `
            max-width: 200px;
            max-height: 300px;
            object-fit: contain;
            pointer-events: none;
            user-select: none;
            -webkit-user-drag: none;
            filter: drop-shadow(0 2px 8px rgba(0,0,0,0.1));
        `;

        imgContainer.appendChild(img);
        document.body.appendChild(imgContainer);

        // 添加拖动功能
        let isDragging = false;
        let currentX;
        let currentY;
        let initialX;
        let initialY;

        imgContainer.addEventListener('mousedown', dragStart);
        document.addEventListener('mousemove', drag);
        document.addEventListener('mouseup', dragEnd);

        function dragStart(e) {
            initialX = e.clientX - imgContainer.offsetLeft;
            initialY = e.clientY - imgContainer.offsetTop;

            if (e.target === imgContainer) {
                isDragging = true;
            }
        }

        function drag(e) {
            if (isDragging) {
                e.preventDefault();
                currentX = e.clientX - initialX;
                currentY = e.clientY - initialY;

                currentX = Math.min(Math.max(0, currentX), window.innerWidth - imgContainer.offsetWidth);
                currentY = Math.min(Math.max(0, currentY), window.innerHeight - imgContainer.offsetHeight);

                imgContainer.style.left = currentX + 'px';
                imgContainer.style.top = currentY + 'px';
            }
        }

        function dragEnd() {
            isDragging = false;
        }

        // 修改主题更新函数
        function updateThemeColors() {
            const isDarkMode = document.documentElement.classList.contains('dark');
            const backgroundColor = isDarkMode ? '#2D3748' : 'white';
            const borderColor = isDarkMode ? '#4A5568' : '#e5e7eb';
            const textColor = isDarkMode ? 'white' : 'black';

            inputContainer.style.setProperty('--background-color', backgroundColor);
            inputContainer.style.setProperty('--border-color', borderColor);
            input.style.setProperty('--text-color', textColor);
        }

        // 初始更新主题
        updateThemeColors();

        // 监听主题变化
        const observer = new MutationObserver(updateThemeColors);
        observer.observe(document.documentElement, {
            attributes: true,
            attributeFilter: ['class']
        });

        // 修改显示和隐藏逻辑
        function showInput() {
            clearTimeout(hideTimeout);
            inputContainer.style.display = 'block';
            imgContainer.style.display = 'block';
            button.style.transform = 'scale(1.1)';
            requestAnimationFrame(() => {
                inputContainer.style.opacity = '1';
                input.focus();
            });
        }

        function hideInput() {
            // 检查鼠标是否在任何相关元素上
            if (isMouseOverElements()) {
                return;
            }

            // 检查是否正在输入
            if (document.activeElement === input) {
                return;
            }

            hideTimeout = setTimeout(() => {
                // 再次检查鼠标位置和输入状态
                if (!isMouseOverElements() && document.activeElement !== input) {
                    inputContainer.style.opacity = '0';
                    imgContainer.style.display = 'none';
                    button.style.transform = '';
                    setTimeout(() => {
                        if (inputContainer.style.opacity === '0' &&
                            !isMouseOverElements() &&
                            document.activeElement !== input) {
                            inputContainer.style.display = 'none';
                        }
                    }, 300);
                }
            }, 800); // 增加更长的延迟时间
        }

        // 改进鼠标位置检查函数
        function isMouseOverElements() {
            const elements = [wrapper, inputContainer, input, confirmButton, imgContainer];
            return elements.some(el => el && el.matches(':hover'));
        }

        // 添加更多的事件监听
        wrapper.addEventListener('mouseenter', showInput);
        inputContainer.addEventListener('mouseenter', showInput);
        input.addEventListener('mouseenter', () => clearTimeout(hideTimeout));
        confirmButton.addEventListener('mouseenter', () => clearTimeout(hideTimeout));
        imgContainer.addEventListener('mouseenter', () => clearTimeout(hideTimeout));

        // 修改离开事件
        wrapper.addEventListener('mouseleave', (e) => {
            const relatedTarget = e.relatedTarget;
            if (!isPartOfComponent(relatedTarget)) {
                hideInput();
            }
        });

        inputContainer.addEventListener('mouseleave', (e) => {
            const relatedTarget = e.relatedTarget;
            if (!isPartOfComponent(relatedTarget)) {
                hideInput();
            }
        });

        // 添加组件检查函数
        function isPartOfComponent(element) {
            const components = [wrapper, inputContainer, input, confirmButton, imgContainer];
            return components.some(comp => comp && (comp === element || comp.contains(element)));
        }

        // 添加输入框焦点事件
        input.addEventListener('focus', () => {
            clearTimeout(hideTimeout);
        });

        input.addEventListener('blur', (e) => {
            // 检查是否点击到了组件内的其他元素
            if (!isPartOfComponent(e.relatedTarget)) {
                hideInput();
            }
        });

        // 添加点击外部关闭功能
        document.addEventListener('click', (e) => {
            if (!isPartOfComponent(e.target) && inputContainer.style.display === 'block') {
                hideInput();
            }
        });

        // 修改按钮点击事件处
        button.addEventListener('click', function() {
            const isAutoRunning = button.getAttribute('data-auto-running') === 'true';

            // 确保清理之前的定时器
            if (autoRunInterval) {
                clearInterval(autoRunInterval);
                autoRunInterval = null;
            }

            if (!isAutoRunning) {
                // 开始自动运行
                button.setAttribute('data-auto-running', 'true');
                button.style.backgroundColor = '#4CAF50';
                button.style.color = 'white';

                executeContinue();

                autoRunInterval = setInterval(async () => {
                    try {
                        const stopButton = document.querySelector('[data-testid="stop-button"]');
                        if (!stopButton) {
                            await executeContinue();
                        }
                    } catch (error) {
                        console.error('自动运行出错:', error);
                        // 出错时自动停止
                        stopAutoRun();
                    }
                }, 1000);
            } else {
                // 停止自动运行
                stopAutoRun();
            }
        });

        // 添加停止自动运行的函数
        function stopAutoRun() {
            const button = document.querySelector('[aria-label="发送继续"]');
            if (button) {
                button.setAttribute('data-auto-running', 'false');
                button.style.backgroundColor = '';
                button.style.color = '';
            }

            if (autoRunInterval) {
                clearInterval(autoRunInterval);
                autoRunInterval = null;
            }
        }

        // 为输入框和确定按钮添加额外的鼠标事件监听
        input.addEventListener('mouseenter', () => {
            clearTimeout(hideTimeout);
        });

        confirmButton.addEventListener('mouseenter', () => {
            clearTimeout(hideTimeout);
        });

        // 添加显示输入框的事件监听
        wrapper.addEventListener('mouseenter', showInput);
        inputContainer.addEventListener('mouseenter', showInput);

        return wrapper;
    }

    function waitForResponse() {
        return new Promise((resolve) => {
            function checkResponseStatus() {
                const stopButton = document.querySelector('[data-testid="stop-button"]');
                const sendButton = document.querySelector('[data-testid="send-button"]');
                const speechButton = document.querySelector('[data-testid="composer-speech-button"]');

                if (!stopButton && (sendButton || speechButton)) {
                    resolve();
                } else {
                    setTimeout(checkResponseStatus, 1000);
                }
            }
            checkResponseStatus();
        });
    }

    async function executeContinue() {
        const inputElement = document.querySelector('p.placeholder') ||
                           document.querySelector('[contenteditable="true"]') ||
                           document.querySelector('#composer-background p');

        if (!inputElement) {
            console.error('找不到输入元素');
            stopAutoRun(); // 找不到输入元素时停止自动运行
            return;
        }

        try {
            await waitForResponse();

            inputElement.innerHTML = '';
            inputElement.textContent = '';

            while (inputElement.firstChild) {
                inputElement.removeChild(inputElement.firstChild);
            }

            const textNode = document.createTextNode(customMessage);
            inputElement.appendChild(textNode);
            inputElement.focus();

            ['input', 'change', 'keyup'].forEach(eventType => {
                inputElement.dispatchEvent(new Event(eventType, { bubbles: true }));
            });

            setTimeout(function() {
                const sendButton = document.querySelector('[data-testid="send-button"]');
                if (sendButton) {
                    sendButton.click();
                } else {
                    console.error('找不到发送按钮');
                    stopAutoRun(); // 找不到发送按钮时停止自动运行
                }
            }, 100);
        } catch (e) {
            console.error('操作失败:', e);
            stopAutoRun(); // 操作失败时停止自动运行
        }
    }

    function tryInsertButton() {
        if (document.querySelector('[aria-label="发送继续"]')) {
            return true;
        }

        const toolbarDiv = document.querySelector('.flex.gap-x-1');
        if (!toolbarDiv) {
            return false;
        }

        const buttonWrapper = createButton();

        const buttons = Array.from(toolbarDiv.children);
        const globeButton = buttons.find(child => {
            const svg = child.querySelector('svg');
            return svg && svg.querySelector('path[d^="M2 12C2 6.47715"]');
        });

        if (globeButton) {
            if (globeButton.nextSibling) {
                toolbarDiv.insertBefore(buttonWrapper, globeButton.nextSibling);
            } else {
                toolbarDiv.appendChild(buttonWrapper);
            }
        } else {
            toolbarDiv.appendChild(buttonWrapper);
        }

        return true;
    }

    function initButton() {
        if (!tryInsertButton()) {
            const checkInterval = setInterval(() => {
                if (tryInsertButton()) {
                    clearInterval(checkInterval);
                }
            }, 1000);

            setTimeout(() => {
                clearInterval(checkInterval);
            }, 60000);
        }
    }

    if (document.readyState === 'complete') {
        initButton();
    } else {
        window.addEventListener('load', initButton);
    }

    const observer = new MutationObserver((mutations) => {
        if (!document.querySelector('[aria-label="发送继续"]')) {
            tryInsertButton();
        }
    });

    observer.observe(document.body, {
        childList: true,
        subtree: true
    });

    // 添加页面卸载时的清理
    window.addEventListener('unload', () => {
        if (autoRunInterval) {
            clearInterval(autoRunInterval);
            autoRunInterval = null;
        }
    });
})();