ChatGPT对话导出工具-轻松提取聊天记录导出至本地📄(MaynorAI)

🐎一键将ChatGPT网站和国内相关镜像站的聊天记录导出为HTML或Markdown,轻松提取GPT聊天记录,让你在本地上就能使用看!

// ==UserScript==
// @name         ChatGPT对话导出工具-轻松提取聊天记录导出至本地📄(MaynorAI)
// @namespace    http://chatgpt-plus.top/
// @version      1.15
// @description  🐎一键将ChatGPT网站和国内相关镜像站的聊天记录导出为HTML或Markdown,轻松提取GPT聊天记录,让你在本地上就能使用看!
// @author       @caicats
// @match        https://chat.openai.com/*
// @match        https://chatgpt.com/**
// @match               *://chatgpt-plus.top/*
// @match               *://*.chatgpt-plus.top/*
// @match               *://*.maynor1024.live/*
// @license      MIT
// @icon         https://t1.gstatic.cn/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&size=32&url=https://chatgpt.com
// ==/UserScript==
 
 
 
(function() {
    'use strict';
 
    window.addEventListener('load', () => {
        createFloatingMenu();
    });
 
    function createFloatingMenu() {
        const menuContainer = document.createElement('div');
        menuContainer.id = 'floating-menu';
        menuContainer.style.position = 'fixed';
        menuContainer.style.bottom = '80px';
        menuContainer.style.right = '15px';
        menuContainer.style.zIndex = '10000';
        menuContainer.style.display = 'flex';
        menuContainer.style.flexDirection = 'column';
        menuContainer.style.alignItems = 'center';
        menuContainer.style.gap = '10px';
 
        // 创建提示框
        const tooltip = document.createElement('div');
        tooltip.id = 'tooltip';
        tooltip.style.position = 'fixed';
        tooltip.style.backgroundColor = 'rgba(0, 0, 0, 0.9)';
        tooltip.style.color = '#fff';
        tooltip.style.padding = '8px 12px';
        tooltip.style.borderRadius = '6px';
        tooltip.style.fontSize = '12px';
        tooltip.style.visibility = 'hidden';
        tooltip.style.zIndex = '10001';
        tooltip.style.pointerEvents = 'none';
        tooltip.style.maxWidth = '200px';
        tooltip.style.wordWrap = 'break-word';
        document.body.appendChild(tooltip);
 
        // Main button as expand icon
        const mainButton = document.createElement('button');
        mainButton.id = 'main-button';
        mainButton.style.width = '40px';
        mainButton.style.height = '40px';
        mainButton.style.borderRadius = '50%';
        mainButton.style.border = 'none';
        mainButton.style.backgroundColor = '#4cafa3';
        mainButton.style.color = 'white';
        mainButton.style.cursor = 'pointer';
        mainButton.style.fontSize = '26px';
        mainButton.textContent = '+';
 
        let isExpanded = false;
        mainButton.addEventListener('click', () => {
            isExpanded = !isExpanded;
            toggleMenu(isExpanded, menuContainer);
        });
        addTooltip(mainButton, '展开功能列表');
 
        // Markdown button with orange background
        const markdownButton = createMenuButton('📄', '导出为 Markdown', exportChatAsMarkdown, '#FFA500');
        addTooltip(markdownButton.querySelector('button'), '📄 导出为 Markdown');
 
        // HTML button with orange background
        const htmlButton = createMenuButton('🌐', '导出为 HTML', exportChatAsHTML, '#FFA500');
        addTooltip(htmlButton.querySelector('button'), '🌐 导出为 HTML');
 
        // New Shop button to redirect to the specified link
        const shopButton = createMenuButton('🛒', '付费GPT库系统', () => {
            window.open('http://chatgpt-plus.top/', '_blank');
        }, '#32CD32'); // LimeGreen color for shop button
        addTooltip(shopButton.querySelector('button'), '🛒 付费GPT库系统');
 
        // Append buttons to the menu
        menuContainer.appendChild(mainButton);
        menuContainer.appendChild(markdownButton);
        menuContainer.appendChild(htmlButton);
        menuContainer.appendChild(shopButton);
        document.body.appendChild(menuContainer);
 
        // Initially hide the menu buttons
        toggleMenu(false, menuContainer);
 
        function addTooltip(element, text) {
            element.addEventListener('mouseenter', (event) => {
                tooltip.textContent = text;
                tooltip.style.visibility = 'visible';
 
                // 设置提示框在按钮左侧显示
                const rect = element.getBoundingClientRect();
                tooltip.style.left = `${rect.left - tooltip.offsetWidth - 10}px`;
                tooltip.style.top = `${rect.top + (rect.height / 2) - (tooltip.offsetHeight / 2)}px`;
            });
 
            element.addEventListener('mouseleave', () => {
                tooltip.style.visibility = 'hidden';
            });
        }
    }
 
    function createMenuButton(icon, text, onClick, bgColor) {
        const buttonContainer = document.createElement('div');
        buttonContainer.style.position = 'relative';
 
        const button = document.createElement('button');
        button.className = 'menu-button';
        button.style.width = '36px';
        button.style.height = '36px';
        button.style.borderRadius = '50%';
        button.style.border = 'none';
        button.style.backgroundColor = bgColor;
        button.style.color = 'white';
        button.style.cursor = 'pointer';
        button.textContent = icon;
        button.addEventListener('click', onClick);
 
        buttonContainer.appendChild(button);
        return buttonContainer;
    }
 
    function toggleMenu(expand, menuContainer) {
        const buttons = menuContainer.querySelectorAll('.menu-button');
        buttons.forEach(button => {
            button.style.display = expand ? 'block' : 'none';
        });
    }
 
    async function exportChatAsMarkdown() {
        try {
            let markdownContent = "# ChatGPT 对话记录\n\n";
            let allElements = document.querySelectorAll('div.flex.flex-grow.flex-col.max-w-full');
 
            allElements.forEach((element, index) => {
                let text = element.textContent.trim();
                if (index % 2 === 0) {
                    markdownContent += `## User\n${text}\n\n`;
                } else {
                    markdownContent += `## Assistant\n${text}\n\n`;
                }
            });
 
            download(markdownContent, 'chat-export.md', 'text/markdown');
        } catch (error) {
            console.error("导出为 Markdown 时出错:", error);
        }
    }
 
    async function exportChatAsHTML() {
        try {
            let htmlContent = "<!DOCTYPE html><html><head><meta charset='UTF-8'><title>Chat Export</title></head><body>";
            let allElements = document.querySelectorAll('div[class*="text-base"]');
 
            allElements.forEach((element, index) => {
                let text = element.innerHTML.trim();
                let role = element.closest('[data-message-author-role]')?.getAttribute('data-message-author-role');
                if (role === 'user') {
                    htmlContent += `<h2>User</h2><div>${text}</div>`;
                } else if (role === 'assistant') {
                    htmlContent += `<h2>Assistant</h2><div>${text}</div>`;
                }
            });
 
            htmlContent += "</body></html>";
            download(htmlContent, 'chat-export.html', 'text/html');
        } catch (error) {
            console.error("导出为 HTML 时出错:", error);
        }
    }
 
    function download(data, filename, type) {
        const blob = new Blob([data], {type: type});
        const a = document.createElement('a');
        const url = URL.createObjectURL(blob);
        a.href = url;
        a.download = filename;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        URL.revokeObjectURL(url);
    }
})();