SaveChatGPT
by
OkadaHiroshi
2023-03-23 [2023/03/23 16:43:36]
ChatGPTのチャットをテキストファイルに
-
/*
* @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 です。