Fetch and log conversation data from ChatGPT using an access token
// ==UserScript==
// @name ChatGPT Conversation Data Fetcher
// @namespace http://tampermonkey.net/
// @version 0.2
// @description Fetch and log conversation data from ChatGPT using an access token
// @author glwhappen
// @match https://chat.openai.com/*
// @grant GM_xmlhttpRequest
// @homepage https://www.glwsq.cn/post/ChatGPT-Conversation-Exporter
// @supportURL https://github.com/glwhappen/chatgpt-conversation-exporter/issues
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// 创建复制按钮并添加到页面
window.addEventListener('load', function() {
const copyButton = document.createElement('button');
copyButton.textContent = '复制对话';
copyButton.style.position = 'fixed';
copyButton.style.top = '10px';
copyButton.style.right = '110px'; // 向左偏移100px
copyButton.style.zIndex = 1000;
copyButton.style.padding = '5px 10px';
copyButton.style.border = 'none';
copyButton.style.borderRadius = '5px';
copyButton.style.backgroundColor = '#4CAF50';
copyButton.style.color = 'white';
copyButton.style.cursor = 'pointer';
document.body.appendChild(copyButton);
// 点击按钮时执行操作
copyButton.addEventListener('click', function() {
this.textContent = '复制中...';
fetchAccessTokenAndConversation(() => {
this.textContent = '复制对话';
showCopySuccessMessage();
});
});
});
// 显示复制成功消息
function showCopySuccessMessage() {
const message = document.createElement('div');
message.textContent = '对话内容已复制到剪贴板';
message.style.position = 'fixed';
message.style.top = '50px';
message.style.right = '110px';
message.style.backgroundColor = '#4CAF50';
message.style.color = 'white';
message.style.padding = '5px 10px';
message.style.borderRadius = '5px';
message.style.zIndex = 1001;
document.body.appendChild(message);
// 3秒后移除消息
setTimeout(() => {
document.body.removeChild(message);
}, 1000);
}
// 获取访问令牌并获取对话内容
function fetchAccessTokenAndConversation(callback) {
GM_xmlhttpRequest({
method: "GET",
url: "https://chat.openai.com/api/auth/session",
onload: function(response) {
try {
const responseData = JSON.parse(response.responseText);
const accessToken = responseData.accessToken;
if (accessToken) {
fetchConversationData(accessToken, callback);
}
} catch (e) {
console.error("解析响应时出错: ", e);
}
},
onerror: function(error) {
console.error("请求错误: ", error);
}
});
}
// 获取对话内容
function fetchConversationData(accessToken, callback) {
const conversationId = window.location.pathname.split('/')[2];
if (conversationId) {
GM_xmlhttpRequest({
method: "GET",
url: `https://chat.openai.com/backend-api/conversation/${conversationId}`,
headers: {
"Authorization": `Bearer ${accessToken}`
},
onload: function(response) {
try {
const jsonData = JSON.parse(response.responseText);
const markdown = convertToMarkdown(jsonData);
navigator.clipboard.writeText(markdown).then(function() {
console.log('对话内容已复制到剪贴板');
if (callback) callback();
}, function(err) {
console.error('无法复制文本: ', err);
});
} catch (e) {
console.error("解析响应数据出错: ", e);
}
},
onerror: function(error) {
console.error("获取对话内容时发生错误: ", error);
}
});
}
}
function convertToMarkdown(conversationData) {
let markdown = "### 对话内容\n\n";
// 循环遍历每个消息
for (const key in conversationData.mapping) {
if (conversationData.mapping.hasOwnProperty(key)) {
const node = conversationData.mapping[key];
const message = node.message;
if (message) {
const sender = message.author.role === 'user' ? '用户' : '机器人';
const contentParts = message.content.parts.join("\n");
const content = contentParts.trim() === '' ? '[无内容]' : contentParts;
markdown += `#### ${sender}:\n${content}\n\n`;
}
}
}
return markdown;
}
})();