ChatGPT Chat Exporter - Markdown

Export your ChatGPT conversations as clean Markdown files

  1. // ==UserScript==
  2. // @name ChatGPT Chat Exporter - Markdown
  3. // @namespace https://github.com/rashidazarang/chatgpt-chat-exporter
  4. // @version 1.0.0
  5. // @description Export your ChatGPT conversations as clean Markdown files
  6. // @author Rashid Azarang
  7. // @match https://chat.openai.com/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=openai.com
  9. // @grant none
  10. // @license MIT
  11. // @homepageURL https://github.com/rashidazarang/chatgpt-chat-exporter
  12. // @supportURL https://github.com/rashidazarang/chatgpt-chat-exporter/issues
  13. // ==/UserScript==
  14.  
  15. (function() {
  16. 'use strict';
  17.  
  18. // Add a button to the ChatGPT interface
  19. function addExportButton() {
  20. // Check if our button already exists
  21. if (document.getElementById('export-markdown-button')) return;
  22.  
  23. // Find a suitable location to add the button
  24. const targetElement = document.querySelector('nav');
  25. if (!targetElement) return;
  26.  
  27. // Create the button
  28. const button = document.createElement('button');
  29. button.id = 'export-markdown-button';
  30. button.innerHTML = 'Export as Markdown';
  31. button.style.cssText = `
  32. margin: 10px;
  33. padding: 10px;
  34. border-radius: 5px;
  35. background-color: #10a37f;
  36. color: white;
  37. border: none;
  38. cursor: pointer;
  39. font-size: 14px;
  40. width: calc(100% - 20px);
  41. `;
  42.  
  43. // Add click event listener
  44. button.addEventListener('click', exportMarkdown);
  45.  
  46. // Add button to the page
  47. targetElement.appendChild(button);
  48. }
  49.  
  50. function formatDate(date = new Date()) {
  51. return date.toISOString().split('T')[0];
  52. }
  53.  
  54. function escapeMarkdown(text) {
  55. return text
  56. .replace(/\\/g, '\\\\')
  57. .replace(/\*/g, '\\*')
  58. .replace(/_/g, '\\_')
  59. .replace(/`/g, '\\`')
  60. .replace(/\n{3,}/g, '\n\n');
  61. }
  62.  
  63. function processMessageContent(element) {
  64. const clone = element.cloneNode(true);
  65.  
  66. // Replace <pre><code> blocks
  67. clone.querySelectorAll('pre').forEach(pre => {
  68. const code = pre.innerText.trim();
  69. const langMatch = pre.querySelector('code')?.className?.match(/language-([a-zA-Z0-9]+)/);
  70. const lang = langMatch ? langMatch[1] : '';
  71. pre.replaceWith(`\n\n\`\`\`${lang}\n${code}\n\`\`\`\n`);
  72. });
  73.  
  74. // Replace images and canvas with placeholders
  75. clone.querySelectorAll('img, canvas').forEach(el => {
  76. el.replaceWith('[Image or Canvas]');
  77. });
  78.  
  79. // Convert remaining HTML to plain markdown-style text
  80. return escapeMarkdown(clone.innerText.trim());
  81. }
  82.  
  83. function exportMarkdown() {
  84. const messages = document.querySelectorAll('div[class*="group"]');
  85. const lines = [];
  86.  
  87. const title = 'Conversation with ChatGPT';
  88. const date = formatDate();
  89. const url = window.location.href;
  90.  
  91. lines.push(`# ${title}\n`);
  92. lines.push(`**Date:** ${date}`);
  93. lines.push(`**Source:** [chat.openai.com](${url})\n`);
  94. lines.push(`---\n`);
  95.  
  96. messages.forEach(group => {
  97. const isUser = !!group.querySelector('img');
  98. const sender = isUser ? 'You' : 'ChatGPT';
  99. const block = group.querySelector('.markdown, .prose, .whitespace-pre-wrap');
  100.  
  101. if (block) {
  102. const content = processMessageContent(block);
  103. if (content) {
  104. lines.push(`### **${sender}**\n`);
  105. lines.push(content);
  106. lines.push('\n---\n');
  107. }
  108. }
  109. });
  110.  
  111. const markdown = lines.join('\n').trim();
  112. const blob = new Blob([markdown], { type: 'text/markdown' });
  113. const a = document.createElement('a');
  114. a.download = `ChatGPT_Conversation_${date}.md`;
  115. a.href = URL.createObjectURL(blob);
  116. document.body.appendChild(a);
  117. a.click();
  118. document.body.removeChild(a);
  119. }
  120.  
  121. // Add the export button when the DOM is fully loaded
  122. if (document.readyState === 'loading') {
  123. document.addEventListener('DOMContentLoaded', addExportButton);
  124. } else {
  125. addExportButton();
  126. }
  127.  
  128. // Check periodically if the button needs to be added (for SPAs like ChatGPT)
  129. setInterval(addExportButton, 3000);
  130. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址