DeepSeek Chat Exporter (Markdown & PDF)

导出 DeepSeek 聊天记录为 Markdown 和 PDF 格式

当前为 2025-02-01 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         DeepSeek Chat Exporter (Markdown & PDF)
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  导出 DeepSeek 聊天记录为 Markdown 和 PDF 格式
// @author       HSyuf/Blueberrycongee
// @match        https://chat.deepseek.com/*
// @grant        GM_addStyle
// @grant        GM_download
// @license      MIT
// ==/UserScript==

(function () {
  "use strict";

  class AiAnswer {
    constructor(aiChain, aiAnswer) {
      this.aiChain = {
        type: "ai_chain",
        content: aiChain ? aiChain.textContent.trim() : "[无内容]",
      };
      this.aiAnswer = {
        type: "ai_formal_reply",
        content: aiAnswer ? aiAnswer.textContent.trim() : "[无内容]",
      };
    }
  }

  // =====================
  // 获取用户消息
  // =====================
  function getUserMessages() {
    const chatContainer = Array.from(document.querySelectorAll(".fa81"));
    return chatContainer.map((el) => ({
      type: "user",
      content: el.textContent.trim() || "[无内容]",
    }));
  }

  // =====================
  // 获取 AI 消息
  // =====================
  function getAiMessages() {
    const aiAnswers = Array.from(
      document.querySelectorAll(".f9bf7997.c05b5566")
    );

    return aiAnswers.map((el) => {
      const aiStatus = el.querySelector(".a6d716f5.db5991dd");
      const aiChain = el.querySelector(".e1675d8b");
      const aiAnswers = el.querySelector(".ds-markdown.ds-markdown--block");

      return aiStatus.textContent === "思考已停止"
        ? new AiAnswer()
        : new AiAnswer(aiChain, aiAnswers);
    });
  }

  // =====================
  // 生成 Markdown 内容
  // =====================
  function generateMdContent() {
    const userMessages = getUserMessages();
    const aiMessages = getAiMessages();

    if (userMessages.length === 0 && aiMessages.length === 0) {
      alert("未找到聊天记录!");
      return null;
    }

    const allMessages = [];
    for (let i = 0; i < userMessages.length; i++) {
      allMessages.push(userMessages[i], aiMessages[i].aiChain, aiMessages[i].aiAnswer);
    }

    return allMessages
      .map((msg) => {
        if (msg.type === "user") return `**用户:**\n${msg.content}`;
        if (msg.type === "ai_formal_reply") return `**AI 回答:**\n${msg.content}`;
        if (msg.type === "ai_chain") return `**AI 思维链:**\n${msg.content}`;
        return "";
      })
      .join("\n\n---\n\n");
  }

  // =====================
  // 导出为 Markdown
  // =====================
  function exportMarkdown() {
    const mdContent = generateMdContent();
    if (!mdContent) return;

    const blob = new Blob([mdContent], { type: "text/markdown" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = `DeepSeek_Chat_${Date.now()}.md`;
    a.click();
    setTimeout(() => URL.revokeObjectURL(url), 5000);
  }

  // =====================
  // 导出为 PDF
  // =====================
  function exportPDF() {
    const mdContent = generateMdContent();
    if (!mdContent) return;

    // 创建打印内容
    const printContent = `
      <html>
        <head>
          <title>DeepSeek Chat Export</title>
          <style>
            body {
              font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
              line-height: 1.6;
              padding: 20px;
              max-width: 800px;
              margin: 0 auto;
            }
            h2 { color: #2c3e50; border-bottom: 1px solid #eee; padding-bottom: 0.3em; }
            .ai-answer { color: #1a7f37; margin: 15px 0; }
            .ai-chain { color: #666; font-style: italic; margin: 10px 0; }
            hr { border: 0; border-top: 1px solid #eee; margin: 25px 0; }
          </style>
        </head>
        <body>
          ${mdContent
            .replace(/\*\*用户:\*\*\n/g, '<h2>用户提问</h2><div class="user-question">')
            .replace(/\*\*AI 回答:\*\*\n/g, '</div><h2>AI 回答</h2><div class="ai-answer">')
            .replace(/\*\*AI 思维链:\*\*\n/g, '</div><h2>思维链</h2><div class="ai-chain">')
            .replace(/\n/g, '<br>')
            .replace(/---/g, '</div><hr>')}
        </body>
      </html>
    `;

    // 创建打印窗口
    const printWindow = window.open("", "_blank");
    printWindow.document.write(printContent);
    printWindow.document.close();

    // 添加延迟确保内容加载完成
    setTimeout(() => {
      printWindow.print();
      printWindow.close();
    }, 500);
  }

  // =====================
  // 添加导出菜单
  // =====================
  function createExportMenu() {
    const menu = document.createElement("div");
    menu.className = "ds-exporter-menu";
    menu.innerHTML = `
      <button class="export-btn" id="md-btn">导出为 Markdown</button>
      <button class="export-btn" id="pdf-btn">导出为 PDF</button>
    `;

    menu.querySelector("#md-btn").addEventListener("click", exportMarkdown);
    menu.querySelector("#pdf-btn").addEventListener("click", exportPDF);
    document.body.appendChild(menu);
  }

  // =====================
  // 样式注入
  // =====================
  GM_addStyle(`
    .ds-exporter-menu {
      position: fixed;
      top: 20px;
      right: 20px;
      z-index: 9999;
      background: rgba(255, 255, 255, 0.95);
      padding: 12px;
      border-radius: 8px;
      box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
      display: flex;
      flex-direction: column;
      gap: 8px;
      backdrop-filter: blur(4px);
    }
    .export-btn {
      background: #2196F3;
      color: white;
      border: none;
      padding: 8px 16px;
      border-radius: 6px;
      cursor: pointer;
      font-size: 14px;
      transition: all 0.2s;
    }
    .export-btn:hover {
      background: #1976D2;
      transform: translateY(-1px);
      box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
    }
  `);

  // =====================
  // 初始化脚本
  // =====================
  function init() {
    const checkInterval = setInterval(() => {
      if (document.querySelector(".fa81")) {
        clearInterval(checkInterval);
        createExportMenu();
      }
    }, 500);
  }

  init();
})();