Greasy Fork

Greasy Fork is available in English.

元宝 & DeepSeek 对话导出工具 | Yuanbao & DeepSeek Chat Exporter

支持元宝和 DeepSeek 对话导出,支持 JSON 和 Markdown 格式。Export Yuanbao and DeepSeek chat history with JSON and Markdown formats.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         元宝 & DeepSeek 对话导出工具 | Yuanbao & DeepSeek Chat Exporter
// @namespace    http://tampermonkey.net/
// @version      1.0.7
// @description  支持元宝和 DeepSeek 对话导出,支持 JSON 和 Markdown 格式。Export Yuanbao and DeepSeek chat history with JSON and Markdown formats.
// @author       dst1213
// @license      MIT
// @match        https://yuanbao.tencent.com/chat/*
// @match        https://*.deepseek.com/a/chat/s/*
// @grant        none
// ==/UserScript==
 
(function () {
    'use strict';
 
    let state = {
        targetResponse: null,
        lastUpdateTime: null,
        convertedMd: null,
        isYuanbao: window.location.href.includes('yuanbao.tencent.com'),
        isDeepSeek: window.location.href.includes('deepseek.com')
    };
 
    const log = {
        info: (msg) => console.log(`[Chat Exporter] ${msg}`),
        error: (msg, e) => console.error(`[Chat Exporter] ${msg}`, e)
    };
 
    function processTargetResponse() {
        try {
            if (state.isYuanbao) {
                const messages = extractYuanbaoMessages();
                if (messages.length > 0) {
                    state.targetResponse = JSON.stringify({ messages }, null, 2);
                    state.lastUpdateTime = new Date().toLocaleTimeString();
                    state.convertedMd = convertJsonToMd({ messages });
                    updateButtonStatus();
                    log.info('成功提取元宝对话内容');
                }
            } else if (state.isDeepSeek) {
                // DeepSeek 的逻辑保持不变
                // ...
            }
        } catch (e) {
            log.error('处理对话内容时出错:', e);
        }
    }
 
    function extractYuanbaoMessages() {
        const messages = [];
        const messageElements = document.querySelectorAll('.agent-chat__list__item');
        messageElements.forEach(el => {
            const role = el.classList.contains('agent-chat__list__item--human') ? 'human' : 'ai';
            const content = el.querySelector('.hyc-content-text, .hyc-content-md')?.innerText || '';
            messages.push({ role, content });
        });
        return messages;
    }
 
    function convertJsonToMd(data) {
        let mdContent = [];
        if (state.isYuanbao) {
            data.messages.forEach(msg => {
                const role = msg.role === 'human' ? 'Human' : 'Assistant';
                mdContent.push(`### ${role}`);
                mdContent.push(msg.content + '\n');
            });
        } else if (state.isDeepSeek) {
            // DeepSeek 的逻辑保持不变
            // ...
        }
        return mdContent.join('\n');
    }
 
    function updateButtonStatus() {
        const jsonButton = document.getElementById('downloadJsonButton');
        const mdButton = document.getElementById('downloadMdButton');
        if (jsonButton && mdButton) {
            const hasResponse = state.targetResponse !== null;
            jsonButton.style.backgroundColor = hasResponse ? '#28a745' : '#007bff';
            mdButton.style.backgroundColor = state.convertedMd ? '#28a745' : '#007bff';
            const statusText = hasResponse ? `最后更新: ${state.lastUpdateTime}\n数据已准备好` : '等待目标响应中...';
            jsonButton.title = statusText;
            mdButton.title = statusText;
        }
    }
 
    function createDownloadButtons() {
        const buttonContainer = document.createElement('div');
        const jsonButton = document.createElement('button');
        const mdButton = document.createElement('button');
 
        Object.assign(buttonContainer.style, {
            position: 'fixed',
            top: '45%',
            right: '10px',
            zIndex: '9999',
            display: 'flex',
            flexDirection: 'column',
            gap: '10px',
            opacity: '0.5',
            transition: 'opacity 0.3s ease',
            cursor: 'move'
        });
 
        const buttonStyles = {
            padding: '8px 12px',
            backgroundColor: '#007bff',
            color: '#ffffff',
            border: 'none',
            borderRadius: '5px',
            cursor: 'pointer',
            transition: 'all 0.3s ease',
            fontFamily: 'Arial, sans-serif',
            boxShadow: '0 2px 5px rgba(0,0,0,0.2)',
            whiteSpace: 'nowrap',
            fontSize: '14px'
        };
 
        jsonButton.id = 'downloadJsonButton';
        jsonButton.innerText = 'JSON';
        mdButton.id = 'downloadMdButton';
        mdButton.innerText = 'MD';
 
        Object.assign(jsonButton.style, buttonStyles);
        Object.assign(mdButton.style, buttonStyles);
 
        buttonContainer.onmouseenter = () => buttonContainer.style.opacity = '1';
        buttonContainer.onmouseleave = () => buttonContainer.style.opacity = '0.5';
 
        let isDragging = false;
        let currentX;
        let currentY;
        let initialX;
        let initialY;
        let xOffset = 0;
        let yOffset = 0;
 
        buttonContainer.onmousedown = dragStart;
        document.onmousemove = drag;
        document.onmouseup = dragEnd;
 
        function dragStart(e) {
            initialX = e.clientX - xOffset;
            initialY = e.clientY - yOffset;
            if (e.target === buttonContainer) {
                isDragging = true;
            }
        }
 
        function drag(e) {
            if (isDragging) {
                e.preventDefault();
                currentX = e.clientX - initialX;
                currentY = e.clientY - initialY;
                xOffset = currentX;
                yOffset = currentY;
                setTranslate(currentX, currentY, buttonContainer);
            }
        }
 
        function dragEnd() {
            isDragging = false;
        }
 
        function setTranslate(xPos, yPos, el) {
            el.style.transform = `translate(${xPos}px, ${yPos}px)`;
        }
 
        jsonButton.onclick = function () {
            if (!state.targetResponse) {
                alert('还没有发现有效的对话记录。\n请等待目标响应或进行一些对话。');
                return;
            }
            try {
                const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
                const chatName = state.isYuanbao ? `Yuanbao - Chat` : `DeepSeek - Chat`;
                const fileName = `${chatName}_${timestamp}.json`;
 
                const blob = new Blob([state.targetResponse], { type: 'application/json' });
                const link = document.createElement('a');
                link.href = URL.createObjectURL(blob);
                link.download = fileName;
                link.click();
 
                log.info(`成功下载文件: ${fileName}`);
            } catch (e) {
                log.error('下载过程中出错:', e);
                alert('下载过程中发生错误,请查看控制台了解详情。');
            }
        };
 
        mdButton.onclick = function () {
            if (!state.convertedMd) {
                alert('还没有发现有效的对话记录。\n请等待目标响应或进行一些对话。');
                return;
            }
            try {
                const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
                const chatName = state.isYuanbao ? `Yuanbao - Chat` : `DeepSeek - Chat`;
                const fileName = `${chatName}_${timestamp}.md`;
 
                const blob = new Blob([state.convertedMd], { type: 'text/markdown' });
                const link = document.createElement('a');
                link.href = URL.createObjectURL(blob);
                link.download = fileName;
                link.click();
 
                log.info(`成功下载文件: ${fileName}`);
            } catch (e) {
                log.error('下载过程中出错:', e);
                alert('下载过程中发生错误,请查看控制台了解详情。');
            }
        };
 
        buttonContainer.appendChild(jsonButton);
        buttonContainer.appendChild(mdButton);
        document.body.appendChild(buttonContainer);
 
        updateButtonStatus();
    }
 
    window.addEventListener('load', function () {
        createDownloadButtons();
 
        const observer = new MutationObserver(() => {
            processTargetResponse();
        });
 
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
 
        log.info('对话导出脚本已启动');
    });
})();