将 https://grok.com/chat/* 页面中的聊天记录导出为 .md 文件,使用对话标题命名,固定交替标识 User / Grok 3 发言者身份(奇偶标)
// ==UserScript==
// @name Grok Chat对话记录导出为.md(Markdown)
// @namespace https://grok.com/
// @version 0.5
// @description 将 https://grok.com/chat/* 页面中的聊天记录导出为 .md 文件,使用对话标题命名,固定交替标识 User / Grok 3 发言者身份(奇偶标)
// @author GPT
// @match https://grok.com/chat/*
// @grant none
// @license MIT
// ==/UserScript==
(function () {
'use strict';
/* === 配置区 === */
const DEFAULT_MODEL_NAME = 'Grok 3'; // 如果你用的是 Grok 2、GPT-4、Claude,也可改这里
const messageSelectors = [
'[data-testid*="chat-message"]',
'[class*="Message"]',
'[class*="message" i]'
].join(',');
const buttonStyle = `
position: fixed;
bottom: 12px;
right: 12px;
z-index: 9999;
padding: 4px 10px;
font-size: 12px;
background: #4caf50;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
box-shadow: 0 2px 6px rgba(0,0,0,.15);
`;
const BUTTON_ID = 'grok-md-export-btn';
function createButton() {
if (document.getElementById(BUTTON_ID)) return;
const btn = document.createElement('button');
btn.id = BUTTON_ID;
btn.textContent = '导出 Markdown';
btn.setAttribute('style', buttonStyle);
btn.addEventListener('click', exportChat);
document.body.appendChild(btn);
}
const observer = new MutationObserver(createButton);
observer.observe(document.body, { childList: true, subtree: true });
createButton();
function exportChat() {
const nodes = Array.from(document.querySelectorAll(messageSelectors));
if (!nodes.length) {
alert('未找到任何聊天记录,可能需要调整脚本中的 messageSelectors。');
return;
}
const messages = nodes.map((node, i) => processNode(node, i)).filter(Boolean);
if (!messages.length) {
alert('无法解析聊天内容,可能需要调整脚本。');
return;
}
const mdContent = buildMarkdown(messages);
downloadMarkdown(mdContent);
}
function processNode(node, index) {
const text = node.innerText.trim();
if (!text) return null;
const author = index % 2 === 0 ? 'User' : DEFAULT_MODEL_NAME;
return { author, text };
}
function buildMarkdown(messages) {
const lines = [];
messages.forEach(m => {
lines.push(`**${m.author}:**`);
lines.push('');
lines.push(m.text.replace(/\r?\n/g, ' \n'));
lines.push('');
});
return lines.join('\n');
}
function downloadMarkdown(content) {
const blob = new Blob([content], { type: 'text/markdown;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
let title = document.title || 'grok-chat';
try {
const titleNode = document.querySelector('h1, h2, [class*=title], [class*=header]');
if (titleNode && titleNode.textContent.trim()) {
title = titleNode.textContent.trim();
}
} catch (e) {
console.warn('无法提取对话标题,使用默认命名');
}
title = title.replace(/[\\/:*?"<>|]/g, '-');
const ts = new Date().toISOString().slice(0, 19).replace(/[T:]/g, '-');
a.download = `${title}-${ts}.md`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
})();