将 https://grok.com/chat/* 页面中的聊天记录导出为 .md 文件
当前为
// ==UserScript==
// @name Grok Chat对话记录导出为.md(Markdown)
// @namespace https://grok.com/
// @version 0.2
// @description 将 https://grok.com/chat/* 页面中的聊天记录导出为 .md 文件
// @author GPT
// @match https://grok.com/chat/*
// @grant none
// @license MIT
// ==/UserScript==
/*
* 使用方法:
* 1. 在 Tampermonkey 中安装此脚本并启用。
* 2. 打开任意 Grok 对话页面(https://grok.com/chat/xxx)。
* 3. 页面**右下角**会出现“导出 Markdown”按钮,点击即可下载 .md 文件。
*
* 如果未来 Grok 前端 DOM 结构发生改变,可能需要修改下方 messageSelectors
* 或者调整 authorDetect 逻辑以正确识别消息作者。
*/
(function () {
'use strict';
/* ---------------- 配置区 ---------------- */
// 根据实际 DOM 结构修改选择器,确保能选出所有消息节点,顺序必须与页面展示一致
const messageSelectors = [
'[data-testid*="chat-message"]', // 通用 data-testid
'[class*="Message"]', // 类名包含 Message
'[class*="message" i]' // 类名包含 message(不区分大小写)
].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);
}
// 监听 DOM 变化,确保按钮在 SPA 路由切换后依旧存在
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(processNode).filter(Boolean);
if (!messages.length) {
alert('无法解析聊天内容,可能需要调整脚本。');
return;
}
const mdContent = buildMarkdown(messages);
downloadMarkdown(mdContent);
}
// 解析单条消息节点,返回 {author, text}
function processNode(node) {
// 尝试通过 aria-label、类名、文本前缀等方式判断作者
let author = 'Unknown';
const aria = (node.getAttribute('aria-label') || '').toLowerCase();
if (/assistant|grok/.test(aria)) author = 'Grok';
else if (/user|you/.test(aria)) author = 'User';
// Fallback: 根据类名或文本判断
if (author === 'Unknown') {
const cls = node.className.toLowerCase();
if (cls.includes('assistant') || cls.includes('grok')) author = 'Grok';
else if (cls.includes('user')) author = 'User';
}
const text = node.innerText.trim();
if (!text) return null;
return { author, text };
}
// 构建 Markdown 字符串
function buildMarkdown(messages) {
const lines = [];
messages.forEach(m => {
lines.push(`**${m.author}:**`);
lines.push('');
// 保留换行,使用两个空格 + 换行以符合 Markdown 换行语法
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;
const ts = new Date().toISOString().slice(0, 19).replace(/[T:]/g, '-');
a.download = `grok-chat-${ts}.md`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
})();