ChatGPT Export to Markdown (Enhanced with Data)

Export ChatGPT conversation to a Markdown file across multiple sites with correct content extraction.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         ChatGPT Export to Markdown (Enhanced with Data)
// @namespace    https://chatgpt.com
// @version      1.3
// @description  Export ChatGPT conversation to a Markdown file across multiple sites with correct content extraction.
// @match        https://chat.openai.com/*
// @match        https://chatgpt.com/*
// @match        https://*.chatgpt.com/*
// @require      https://cdn.jsdelivr.net/npm/@kudoai/[email protected]/dist/chatgpt.min.js
// @grant        none
// ==/UserScript==

(async () => {
    'use strict';

    // Ensure chatgpt.js is loaded
    await chatgpt.isLoaded();

    // Function to create and insert the export button
    const createExportButton = () => {
        // Check if the button already exists
        if (document.getElementById('export-to-markdown-btn')) {
            return;
        }

        // Create and style the export button
        const button = document.createElement('button');
        button.id = 'export-to-markdown-btn';
        button.innerHTML = 'Export to Markdown';
        button.style.position = 'fixed';
        button.style.bottom = '20px';
        button.style.right = '20px';
        button.style.padding = '10px';
        button.style.zIndex = '10000'; // High zIndex to ensure visibility
        button.style.backgroundColor = '#4CAF50';
        button.style.color = '#fff';
        button.style.border = 'none';
        button.style.borderRadius = '5px';
        button.style.cursor = 'pointer';
        document.body.appendChild(button);

        console.log('Export button added.');

        // Button click handler to export conversation
        button.addEventListener('click', async () => {
            try {
                // Fetch the current conversation data
                const chatData = await chatgpt.getChatData("active", ["msg"], "both");
                const chatTitleData = await chatgpt.getChatData("active", "title");

                // Use the chat's title or a fallback
                const chatTitle = chatTitleData.title || 'ChatGPT_Conversation';
                const sanitizedTitle = chatTitle.replace(/[<>:"/\\|?*]+/g, ''); // Remove invalid filename characters

                // Convert the conversation data to a markdown format
                let markdownContent = `# ${chatTitle}\n\n`;
                chatData.forEach((msg, index) => {
                    markdownContent += `### Message ${index + 1}\n\n`;
                    markdownContent += `**User:**\n\n${msg.user}\n\n`;
                    markdownContent += `**ChatGPT:**\n\n${msg.chatgpt}\n\n---\n`;
                });

                // Create a Blob object from the markdown content
                const blob = new Blob([markdownContent], { type: 'text/markdown' });

                // Create a link element to download the markdown file
                const link = document.createElement('a');
                link.href = URL.createObjectURL(blob);
                link.download = `${sanitizedTitle}.md`;
                link.click();

                // Notify the user
                chatgpt.notify('✅ Exported to Markdown successfully!', 'topRight', 2);
            } catch (error) {
                chatgpt.notify('❌ Failed to export conversation!', 'topRight', 2);
                console.error('Export to Markdown failed:', error);
            }
        });
    };

    // MutationObserver to ensure button is always present
    const observer = new MutationObserver(() => {
        createExportButton(); // Ensure the button is recreated if it's removed
    });

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

    // Initial delay before inserting the button to ensure the page is fully loaded
    setTimeout(createExportButton, 3000); // Delay by 3 seconds to ensure all dynamic content is loaded
})();