<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF xmlns="http://purl.org/rss/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel rdf:about="https://let.hatelabo.jp/OkadaHiroshi/rss">
    <link>https://let.hatelabo.jp/OkadaHiroshi/rss</link>
    <description></description>
    <title>Bookmarklets from OkadaHiroshi</title>
    <items>
      <rdf:Seq>
        <rdf:li rdf:resource="https://let.hatelabo.jp/OkadaHiroshi/let/j4v1uOfsgaAA"/>
      </rdf:Seq>
    </items>
  </channel>
  <item rdf:about="https://let.hatelabo.jp/OkadaHiroshi/let/j4v1uOfsgaAA">
    <link>https://let.hatelabo.jp/OkadaHiroshi/let/j4v1uOfsgaAA</link>
    <dc:date>2023-03-23T07:43:36Z</dc:date>
    <description>ChatGPTのチャットをテキストファイルに</description>
    <dc:creator>OkadaHiroshi</dc:creator>
    <title>[Let] SaveChatGPT</title>
    <content:encoded>&lt;a href="javascript:%22https%3A%2F%2Flet.st-hatelabo.com%2FOkadaHiroshi%2Flet%2Fj4v1uOfsgaAA.bookmarklet.js%20%28arg%29%22.replace%28%2F%28%5CS%2B%29%5Cs%2B%28%5CS%2A%29%2F%2Cfunction%28s%2Curl%2Carg%29%7Bs%3Ddocument.createElement%28%22script%22%29%3Bs.charset%3D%22utf-8%22%3Bs.src%3Durl%2B%22%3Fs%3D%22%2BencodeURIComponent%28arg%29%3Bdocument.body.appendChild%28s%29%7D%29%3Bvoid%280%29%3B"&gt;SaveChatGPT&lt;/a&gt;&lt;pre&gt;/*
 * @title SaveChatGPT
 * @description ChatGPTのチャットをテキストファイルに
 * @include http://*
 * @license MIT License
 * @require 
 */

(() =&amp;gt; {
  function getUserName(element) {
    const imgElements = element.querySelectorAll(&amp;quot;img[alt]&amp;quot;);
    for (const imgElement of imgElements) {
      const altText = imgElement.alt;
      if (altText) {
        return altText;
      }
    }
    return &amp;quot;?&amp;quot;;
  }

  function getAssistantName(assistantElement) {
    let assistantName = assistantElement.textContent;
    if (assistantName.length &amp;gt; 32) {
      assistantName = assistantName.substring(0, 32) + '...';
    }
    return assistantName;
  }

  function processCodeBlocks(element) {
    const codeBlocks = element.querySelectorAll(&amp;quot;pre &amp;gt; div&amp;quot;);
    for (const codeBlock of codeBlocks) {
      const span = codeBlock.querySelector(&amp;quot;:first-child &amp;gt; span&amp;quot;);
      const language = span ? span.textContent : &amp;quot;&amp;quot;;
      const code = codeBlock.querySelector(&amp;quot;code&amp;quot;).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 === &amp;quot;UL&amp;quot;) {
      isOL = false;
      indentLelvel += 1;
    } else if (element.tagName === &amp;quot;OL&amp;quot;) {
      isOL = true;
      startNumber = element.getAttribute(&amp;quot;start&amp;quot;) || 1;
      indentLelvel += 1;
    }
    for(const childElement of element.children) {
      processListItem(childElement, indentLelvel, isOL, startNumber);
    }
    if (element.tagName === &amp;quot;LI&amp;quot;) {
      const indent = &amp;quot;  &amp;quot;.repeat(indentLelvel &amp;gt; 0 ? indentLelvel : 0);
      const maker = isOL ? `${startNumber}. ` : &amp;quot;- &amp;quot;;
      element.textContent = `\n\n${indent}${maker}${element.textContent}\n`;
    }
    return element;
  }

  function generateMarkdown(elements) {
    let markdownText = &amp;quot;&amp;quot;;
    const lastIndex = elements.length - 2;
    const assistantName = getAssistantName(elements[0].cloneNode(true));

    for (let i = 1; i &amp;lt;= lastIndex; i++) {
      const clonedElement = elements[i].cloneNode(true);
      processCodeBlocks(clonedElement);
      processListItem(clonedElement);

      const userName = getUserName(clonedElement);
      const speaker = userName !== &amp;quot;?&amp;quot; ? userName : assistantName;
      markdownText += `### ${speaker}\n\n`;

      markdownText += clonedElement.textContent;

      if (i !== lastIndex) {
        markdownText += &amp;quot;\n\n---\n\n&amp;quot;;
      }
    }

    markdownText += `\n\n---\n\n${document.URL}\n`

    return markdownText;
  }

  const elements = document.querySelector(&amp;quot;main &amp;gt; div.flex-1 &amp;gt; div.h-full &amp;gt; div &amp;gt; div&amp;quot;).children;
  const markdownText = generateMarkdown(elements);
  const now = new Date();
  const formattedDate = now.toISOString().replace(/[-T:.]/g, '_').slice(0, -5);
  const pageTitle = (document.title || &amp;quot;chatgpt-conversation&amp;quot;) + &amp;quot;_&amp;quot; + formattedDate;
  const filename = `${pageTitle}.md`;

  const file = new Blob([markdownText], {
    type: &amp;quot;text/plain&amp;quot;
  });

  const a = document.createElement(&amp;quot;a&amp;quot;);
  a.href = URL.createObjectURL(file);
  a.download = filename;
  a.click();
  a.remove();
})();
&lt;/pre&gt;</content:encoded>
  </item>
</rdf:RDF>
