ChatGPT 聊天内容提取器

一键提取 ChatGPT 对话内容,支持复制和导出

Size

11.9 KB

Version

1.0.2

Created

Dec 19, 2025

Updated

about 1 month ago

1// ==UserScript==
2// @name		ChatGPT 聊天内容提取器
3// @description		一键提取 ChatGPT 对话内容,支持复制和导出
4// @version		1.0.2
5// @match		https://*.chatgpt.com/*
6// @icon		https://chatgpt.com/cdn/assets/favicon-l4nq08hd.svg
7// ==/UserScript==
8(function() {
9    'use strict';
10
11    console.log('ChatGPT 聊天内容提取器已加载');
12
13    // 提取聊天内容的核心函数
14    function extractChatContent() {
15        console.log('开始提取聊天内容...');
16        
17        const messages = [];
18        
19        // 查找所有对话轮次
20        const conversationTurns = document.querySelectorAll('article[data-testid^="conversation-turn-"]');
21        console.log(`找到 ${conversationTurns.length} 条消息`);
22        
23        conversationTurns.forEach((turn, index) => {
24            try {
25                // 判断是用户消息还是助手消息
26                const isUser = turn.getAttribute('data-turn') === 'user';
27                const isAssistant = turn.getAttribute('data-turn') === 'assistant';
28                
29                let role = 'unknown';
30                let content = '';
31                
32                if (isUser) {
33                    role = '用户';
34                    // 提取用户消息
35                    const userMessage = turn.querySelector('[data-message-author-role="user"] .whitespace-pre-wrap');
36                    if (userMessage) {
37                        content = userMessage.textContent.trim();
38                    }
39                } else if (isAssistant) {
40                    role = 'ChatGPT';
41                    // 提取助手消息
42                    const assistantMessage = turn.querySelector('[data-message-author-role="assistant"] .markdown');
43                    if (assistantMessage) {
44                        content = assistantMessage.textContent.trim();
45                    }
46                }
47                
48                if (content) {
49                    messages.push({
50                        index: index + 1,
51                        role: role,
52                        content: content
53                    });
54                    console.log(`提取消息 ${index + 1}: ${role}`);
55                }
56            } catch (error) {
57                console.error(`提取消息 ${index + 1} 时出错:`, error);
58            }
59        });
60        
61        return messages;
62    }
63
64    // 格式化聊天内容为文本
65    function formatChatAsText(messages) {
66        let text = '=== ChatGPT 对话记录 ===\n\n';
67        
68        messages.forEach(msg => {
69            text += `${msg.role}】\n${msg.content}\n\n`;
70            text += '---\n\n';
71        });
72        
73        text += `总计: ${messages.length} 条消息\n`;
74        text += `提取时间: ${new Date().toLocaleString('zh-CN')}\n`;
75        
76        return text;
77    }
78
79    // 复制到剪贴板
80    async function copyToClipboard(text) {
81        try {
82            await GM.setClipboard(text);
83            console.log('内容已复制到剪贴板');
84            return true;
85        } catch (error) {
86            console.error('复制失败:', error);
87            return false;
88        }
89    }
90
91    // 下载为文本文件
92    function downloadAsFile(text, filename) {
93        try {
94            const blob = new Blob([text], { type: 'text/plain;charset=utf-8' });
95            const url = URL.createObjectURL(blob);
96            const a = document.createElement('a');
97            a.href = url;
98            a.download = filename;
99            document.body.appendChild(a);
100            a.click();
101            document.body.removeChild(a);
102            URL.revokeObjectURL(url);
103            console.log('文件下载成功');
104            return true;
105        } catch (error) {
106            console.error('下载失败:', error);
107            return false;
108        }
109    }
110
111    // 显示提示消息
112    function showNotification(message, type = 'success') {
113        const notification = document.createElement('div');
114        notification.textContent = message;
115        notification.style.cssText = `
116            position: fixed;
117            top: 20px;
118            right: 20px;
119            padding: 15px 20px;
120            background: ${type === 'success' ? '#10a37f' : '#ef4444'};
121            color: white;
122            border-radius: 8px;
123            font-size: 14px;
124            font-weight: 500;
125            z-index: 10000;
126            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
127            animation: slideIn 0.3s ease-out;
128        `;
129        
130        document.body.appendChild(notification);
131        
132        setTimeout(() => {
133            notification.style.animation = 'slideOut 0.3s ease-out';
134            setTimeout(() => {
135                document.body.removeChild(notification);
136            }, 300);
137        }, 3000);
138    }
139
140    // 添加动画样式
141    function addStyles() {
142        const style = document.createElement('style');
143        style.textContent = `
144            @keyframes slideIn {
145                from {
146                    transform: translateX(400px);
147                    opacity: 0;
148                }
149                to {
150                    transform: translateX(0);
151                    opacity: 1;
152                }
153            }
154            
155            @keyframes slideOut {
156                from {
157                    transform: translateX(0);
158                    opacity: 1;
159                }
160                to {
161                    transform: translateX(400px);
162                    opacity: 0;
163                }
164            }
165            
166            .extract-chat-btn {
167                transition: all 0.2s ease;
168            }
169            
170            .extract-chat-btn:hover {
171                transform: scale(1.05);
172                box-shadow: 0 4px 12px rgba(0,0,0,0.2);
173            }
174            
175            .extract-chat-btn:active {
176                transform: scale(0.98);
177            }
178        `;
179        document.head.appendChild(style);
180    }
181
182    // 创建提取按钮
183    function createExtractButton() {
184        console.log('创建提取按钮...');
185        
186        // 检查按钮是否已存在
187        if (document.getElementById('chatgpt-extract-btn')) {
188            console.log('按钮已存在');
189            return;
190        }
191        
192        const button = document.createElement('button');
193        button.id = 'chatgpt-extract-btn';
194        button.innerHTML = `
195            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
196                <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
197                <polyline points="7 10 12 15 17 10"></polyline>
198                <line x1="12" y1="15" x2="12" y2="3"></line>
199            </svg>
200            <span style="margin-left: 8px;">提取对话</span>
201        `;
202        
203        button.style.cssText = `
204            position: fixed;
205            bottom: 30px;
206            right: 30px;
207            padding: 12px 20px;
208            background: #10a37f;
209            color: white;
210            border: none;
211            border-radius: 8px;
212            font-size: 14px;
213            font-weight: 600;
214            cursor: pointer;
215            z-index: 9999;
216            display: flex;
217            align-items: center;
218            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
219        `;
220        
221        button.className = 'extract-chat-btn';
222        
223        // 点击事件
224        button.addEventListener('click', async () => {
225            console.log('按钮被点击');
226            
227            // 提取内容
228            const messages = extractChatContent();
229            
230            if (messages.length === 0) {
231                showNotification('未找到聊天内容', 'error');
232                return;
233            }
234            
235            const formattedText = formatChatAsText(messages);
236            
237            // 创建操作菜单
238            showActionMenu(formattedText, messages.length);
239        });
240        
241        document.body.appendChild(button);
242        console.log('按钮已添加到页面');
243    }
244
245    // 显示操作菜单
246    function showActionMenu(text, messageCount) {
247        // 移除已存在的菜单
248        const existingMenu = document.getElementById('chatgpt-action-menu');
249        if (existingMenu) {
250            existingMenu.remove();
251        }
252        
253        const menu = document.createElement('div');
254        menu.id = 'chatgpt-action-menu';
255        menu.innerHTML = `
256            <div style="margin-bottom: 15px; font-size: 16px; font-weight: 600; color: #1f2937;">
257                已提取 ${messageCount} 条消息
258            </div>
259            <div style="display: flex; gap: 10px;">
260                <button id="copy-btn" style="
261                    flex: 1;
262                    padding: 10px;
263                    background: #10a37f;
264                    color: white;
265                    border: none;
266                    border-radius: 6px;
267                    font-size: 14px;
268                    font-weight: 500;
269                    cursor: pointer;
270                ">📋 复制</button>
271                <button id="download-btn" style="
272                    flex: 1;
273                    padding: 10px;
274                    background: #2563eb;
275                    color: white;
276                    border: none;
277                    border-radius: 6px;
278                    font-size: 14px;
279                    font-weight: 500;
280                    cursor: pointer;
281                ">💾 下载</button>
282                <button id="close-menu-btn" style="
283                    padding: 10px 15px;
284                    background: #6b7280;
285                    color: white;
286                    border: none;
287                    border-radius: 6px;
288                    font-size: 14px;
289                    font-weight: 500;
290                    cursor: pointer;
291                "></button>
292            </div>
293        `;
294        
295        menu.style.cssText = `
296            position: fixed;
297            bottom: 90px;
298            right: 30px;
299            padding: 20px;
300            background: white;
301            border-radius: 12px;
302            box-shadow: 0 8px 24px rgba(0,0,0,0.2);
303            z-index: 10000;
304            min-width: 300px;
305        `;
306        
307        document.body.appendChild(menu);
308        
309        // 复制按钮
310        document.getElementById('copy-btn').addEventListener('click', async () => {
311            const success = await copyToClipboard(text);
312            if (success) {
313                showNotification('✓ 已复制到剪贴板');
314            } else {
315                showNotification('复制失败', 'error');
316            }
317            menu.remove();
318        });
319        
320        // 下载按钮
321        document.getElementById('download-btn').addEventListener('click', () => {
322            const filename = `ChatGPT对话_${new Date().toISOString().slice(0,10)}.txt`;
323            const success = downloadAsFile(text, filename);
324            if (success) {
325                showNotification('✓ 文件已下载');
326            } else {
327                showNotification('下载失败', 'error');
328            }
329            menu.remove();
330        });
331        
332        // 关闭按钮
333        document.getElementById('close-menu-btn').addEventListener('click', () => {
334            menu.remove();
335        });
336    }
337
338    // 初始化函数
339    function init() {
340        console.log('初始化 ChatGPT 聊天内容提取器...');
341        
342        // 添加样式
343        addStyles();
344        
345        // 等待页面加载完成
346        if (document.readyState === 'loading') {
347            document.addEventListener('DOMContentLoaded', createExtractButton);
348        } else {
349            createExtractButton();
350        }
351        
352        // 监听页面变化,确保按钮始终存在
353        const observer = new MutationObserver(() => {
354            if (!document.getElementById('chatgpt-extract-btn')) {
355                createExtractButton();
356            }
357        });
358        
359        observer.observe(document.body, {
360            childList: true,
361            subtree: true
362        });
363    }
364
365    // 启动扩展
366    init();
367
368})();
ChatGPT 聊天内容提取器 | Robomonkey