// ==UserScript== // @name DeepSeek Chat Exporter (Markdown & PDF & PNG) // @namespace http://tampermonkey.net/ // @version 1.7 // @description 导出 DeepSeek 聊天记录为 Markdown、PDF 和 PNG 格式 // @author HSyuf/Blueberrycongee // @match https://chat.deepseek.com/* // @grant GM_addStyle // @grant GM_download // @license MIT // @require https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js // @downloadURL none // ==/UserScript== (function () { 'use strict'; // ===================== // 配置 // ===================== const config = { chatContainerSelector: '.dad65929', // 聊天框容器 userClassPrefix: 'fa81', // 用户消息 class 前缀 aiClassPrefix: 'f9bf7997', // AI消息相关 class 前缀 aiReplyContainer: 'edb250b1', // AI回复的主要容器 searchHintSelector: '.a6d716f5.db5991dd', // 搜索/思考时间 thinkingChainSelector: '.e1675d8b', // 思考链 finalAnswerSelector: 'div.ds-markdown.ds-markdown--block', // 正式回答 exportFileName: 'DeepSeek_Chat_Export', }; let __exportPNGLock = false; // 全局锁,防止重复点击 // ===================== // 工具函数 // ===================== function isUserMessage(node) { return node.classList.contains(config.userClassPrefix); } function isAIMessage(node) { return node.classList.contains(config.aiClassPrefix); } function extractSearchOrThinking(node) { const hintNode = node.querySelector(config.searchHintSelector); return hintNode ? `**${hintNode.textContent.trim()}**` : null; } function extractThinkingChain(node) { const thinkingNode = node.querySelector(config.thinkingChainSelector); return thinkingNode ? `**思考链**\n${thinkingNode.textContent.trim()}` : null; } function extractFinalAnswer(node) { const answerNode = node.querySelector(config.finalAnswerSelector); if (!answerNode) return null; let answerContent = ''; const elements = answerNode.querySelectorAll('.ds-markdown--block p, .ds-markdown--block h3, .katex-display.ds-markdown-math, hr'); elements.forEach((element) => { if (element.tagName.toLowerCase() === 'p') { element.childNodes.forEach((childNode) => { if (childNode.nodeType === Node.TEXT_NODE) { answerContent += childNode.textContent.trim(); } else if (childNode.classList && childNode.classList.contains('katex')) { const tex = childNode.querySelector('annotation[encoding="application/x-tex"]'); if (tex) { answerContent += `$$$${tex.textContent.trim()}$$$`; } } else if (childNode.tagName === 'STRONG') { answerContent += `**${childNode.textContent.trim()}**`; } else if (childNode.tagName === 'EM') { answerContent += `*${childNode.textContent.trim()}*`; } else if (childNode.tagName === 'A') { const href = childNode.getAttribute('href'); answerContent += `[${childNode.textContent.trim()}](${href})`; } else if (childNode.nodeType === Node.ELEMENT_NODE) { answerContent += childNode.textContent.trim(); } }); answerContent += '\n\n'; } else if (element.tagName.toLowerCase() === 'h3') { answerContent += `### ${element.textContent.trim()}\n\n`; } else if (element.classList.contains('katex-display')) { const tex = element.querySelector('annotation[encoding="application/x-tex"]'); if (tex) { answerContent += `$$${tex.textContent.trim()}$$\n\n`; } } else if (element.tagName.toLowerCase() === 'hr') { answerContent += '\n---\n'; } }); return `**正式回答**\n${answerContent.trim()}`; } function getOrderedMessages() { const messages = []; const chatContainer = document.querySelector(config.chatContainerSelector); if (!chatContainer) { console.error('未找到聊天容器'); return messages; } for (const node of chatContainer.children) { if (isUserMessage(node)) { messages.push(`**用户:**\n${node.textContent.trim()}`); } else if (isAIMessage(node)) { let output = ''; const aiReplyContainer = node.querySelector(`.${config.aiReplyContainer}`); if (aiReplyContainer) { const searchHint = extractSearchOrThinking(aiReplyContainer); if (searchHint) output += `${searchHint}\n\n`; const thinkingChain = extractThinkingChain(aiReplyContainer); if (thinkingChain) output += `${thinkingChain}\n\n`; } else { const searchHint = extractSearchOrThinking(node); if (searchHint) output += `${searchHint}\n\n`; } const finalAnswer = extractFinalAnswer(node); if (finalAnswer) output += `${finalAnswer}\n\n`; if (output.trim()) { messages.push(output.trim()); } } } return messages; } function generateMdContent() { const messages = getOrderedMessages(); return messages.length ? messages.join('\n\n---\n\n') : ''; } // ===================== // 导出功能 // ===================== function exportMarkdown() { const mdContent = generateMdContent(); if (!mdContent) { alert("未找到聊天记录!"); return; } const fixedMdContent = mdContent.replace(/(\*\*.*?\*\*)/g, '$1') .replace(/\(\s*([^)]*)\s*\)/g, '\\($1\\)') .replace(/\$\$\s*([^$]*)\s*\$\$/g, '$$$1$$'); const blob = new Blob([fixedMdContent], { type: 'text/markdown' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `${config.exportFileName}_${Date.now()}.md`; a.click(); setTimeout(() => URL.revokeObjectURL(url), 5000); } function exportPDF() { const mdContent = generateMdContent(); if (!mdContent) return; const fixedMdContent = mdContent.replace(/(\*\*.*?\*\*)/g, '$1') .replace(/\(\s*([^)]*)\s*\)/g, '\\($1\\)') .replace(/\$\$\s*([^$]*)\s*\$\$/g, '$$$1$$'); const printContent = `