SaveChatGPT

  • /*
     * @title SaveChatGPT
     * @description ChatGPTのチャットをテキストファイルに
     * @include http://*
     * @license MIT License
     * @require 
     */
    
    (() => {
      function getUserName(element) {
        const imgElements = element.querySelectorAll("img[alt]");
        for (const imgElement of imgElements) {
          const altText = imgElement.alt;
          if (altText) {
            return altText;
          }
        }
        return "?";
      }
    
      function getAssistantName(assistantElement) {
        let assistantName = assistantElement.textContent;
        if (assistantName.length > 32) {
          assistantName = assistantName.substring(0, 32) + '...';
        }
        return assistantName;
      }
    
      function processCodeBlocks(element) {
        const codeBlocks = element.querySelectorAll("pre > div");
        for (const codeBlock of codeBlocks) {
          const span = codeBlock.querySelector(":first-child > span");
          const language = span ? span.textContent : "";
          const code = codeBlock.querySelector("code").textContent;
          const formattedCode = `\n\n\`\`\`${language}\n${code}\n\`\`\`\n\n`;
    
          codeBlock.parentElement.innerHTML = formattedCode;
        }
      }
    
      function processListItem(element, indentLelvel = -1, isOL=false, startNumber = 1) {
        if (element.nodeType !== Node.ELEMENT_NODE) {
          return;
        }
        if (element.tagName === "UL") {
          isOL = false;
          indentLelvel += 1;
        } else if (element.tagName === "OL") {
          isOL = true;
          startNumber = element.getAttribute("start") || 1;
          indentLelvel += 1;
        }
        for(const childElement of element.children) {
          processListItem(childElement, indentLelvel, isOL, startNumber);
        }
        if (element.tagName === "LI") {
          const indent = "  ".repeat(indentLelvel > 0 ? indentLelvel : 0);
          const maker = isOL ? `${startNumber}. ` : "- ";
          element.textContent = `\n\n${indent}${maker}${element.textContent}\n`;
        }
        return element;
      }
    
      function generateMarkdown(elements) {
        let markdownText = "";
        const lastIndex = elements.length - 2;
        const assistantName = getAssistantName(elements[0].cloneNode(true));
    
        for (let i = 1; i <= lastIndex; i++) {
          const clonedElement = elements[i].cloneNode(true);
          processCodeBlocks(clonedElement);
          processListItem(clonedElement);
    
          const userName = getUserName(clonedElement);
          const speaker = userName !== "?" ? userName : assistantName;
          markdownText += `### ${speaker}\n\n`;
    
          markdownText += clonedElement.textContent;
    
          if (i !== lastIndex) {
            markdownText += "\n\n---\n\n";
          }
        }
    
        markdownText += `\n\n---\n\n${document.URL}\n`
    
        return markdownText;
      }
    
      const elements = document.querySelector("main > div.flex-1 > div.h-full > div > div").children;
      const markdownText = generateMarkdown(elements);
      const now = new Date();
      const formattedDate = now.toISOString().replace(/[-T:.]/g, '_').slice(0, -5);
      const pageTitle = (document.title || "chatgpt-conversation") + "_" + formattedDate;
      const filename = `${pageTitle}.md`;
    
      const file = new Blob([markdownText], {
        type: "text/plain"
      });
    
      const a = document.createElement("a");
      a.href = URL.createObjectURL(file);
      a.download = filename;
      a.click();
      a.remove();
    })();
    
  • Permalink
    このページへの個別リンクです。
    RAW
    書かれたコードへの直接のリンクです。
    Packed
    文字列が圧縮された書かれたコードへのリンクです。
    Userscript
    Greasemonkey 等で利用する場合の .user.js へのリンクです。
    Loader
    @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
    Metadata
    コード中にコメントで @xxx と書かれたメタデータの JSON です。

History

  1. 2023/03/23 16:43:36 - 2023-03-23
  2. 2023/03/23 16:39:44 - 2023-03-23
  3. 2023/03/23 16:39:21 - 2023-03-23
  4. 2023/03/21 20:07:51 - 2023-03-21
  5. 2023/03/21 12:06:21 - 2023-03-21