您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Logs ChatGPT messages with labels, dynamically updates, and includes a copy button. UI can be positioned at the top center or above the input box.
当前为
// ==UserScript== // @name Simple ChatGPT Text Exporter // @namespace https://github.com/samomar/Simple-ChatGPT-Text-Exporter // @version 3.8 // @description Logs ChatGPT messages with labels, dynamically updates, and includes a copy button. UI can be positioned at the top center or above the input box. // @match https://chatgpt.com/* // @grant none // @homepage https://github.com/samomar/Simple-ChatGPT-Text-Exporter // @supportURL https://github.com/samomar/Simple-ChatGPT-Text-Exporter/issues // @license MIT // ==/UserScript== (function() { 'use strict'; const CONFIG = { enableLogging: false, chatContainerSelector: localStorage.getItem('chatContainerSelector') || '', position: localStorage.getItem('chatLoggerPosition') || 'bottom' }; let chatMessages = []; let observer = null; let lastUrl = location.href; function init() { CONFIG.chatContainerSelector = localStorage.getItem('chatContainerSelector') || ''; CONFIG.position = localStorage.getItem('chatLoggerPosition') || 'bottom'; // Immediately try to create controls and observe chat tryInitialize(); // Set up a MutationObserver to watch for DOM changes const bodyObserver = new MutationObserver(tryInitialize); bodyObserver.observe(document.body, { childList: true, subtree: true }); // Also set up an interval as a fallback const initInterval = setInterval(() => { if (document.getElementById('chat-logger-controls')) { clearInterval(initInterval); } else { tryInitialize(); } }, 1000); // Continue checking for URL changes setInterval(checkUrlChange, 1000); } function tryInitialize() { if (!document.getElementById('chat-logger-controls')) { const inputBox = findInputBox(); if (inputBox) { createControls(); if (CONFIG.chatContainerSelector) { observeChatContainer(CONFIG.chatContainerSelector); } else { // If no selector is saved, try to find a suitable container const containers = findPossibleChatContainers(); if (containers.length > 0) { CONFIG.chatContainerSelector = containers[0].selector; localStorage.setItem('chatContainerSelector', CONFIG.chatContainerSelector); observeChatContainer(CONFIG.chatContainerSelector); } } } } } function createControls() { const existingControls = document.getElementById('chat-logger-controls'); if (existingControls) existingControls.remove(); const container = document.createElement('div'); container.id = 'chat-logger-controls'; updateControlsStyle(container); container.innerHTML = ` <div class="dropdown"> <button id="download-chat-button" class="chat-logger-btn">⬇️</button> <div class="dropdown-content"> <a href="#" id="download-txt">Download TXT</a> <a href="#" id="download-json">Download JSON</a> </div> </div> <button id="toggle-selector-button" class="chat-logger-btn">⚙️</button> <button id="copy-chat-button" class="chat-logger-btn">Copy Chat</button> <button id="toggle-position-button" class="chat-logger-btn">↕️</button> <div id="chat-selector-container" style="display:none;"> <select id="chat-container-dropdown" class="chat-logger-select"></select> <button id="copy-selector-button" class="chat-logger-btn">📋</button> </div> `; const style = document.createElement('style'); style.textContent = ` #chat-logger-controls { display: flex; align-items: center; gap: 5px; padding: 5px; background-color: #202123; border-radius: 5px; } .chat-logger-btn { padding: 5px 10px; font-size: 12px; background-color: #343541; color: #fff; border: none; border-radius: 4px; cursor: pointer; } .chat-logger-btn:hover { background-color: #40414f; } .chat-logger-select { background-color: #343541; color: #fff; border: none; border-radius: 4px; padding: 5px; font-size: 12px; } .dropdown { position: relative; display: inline-block; } .dropdown-content { display: none; position: absolute; background-color: #202123; min-width: 160px; box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); z-index: 1; border-radius: 4px; top: 100%; left: 0; } .dropdown-content a { color: #fff; padding: 12px 16px; text-decoration: none; display: block; font-size: 12px; } .dropdown-content a:hover { background-color: #343541; } .dropdown:hover .dropdown-content { display: block; } `; document.head.appendChild(style); if (CONFIG.position === 'top') { document.body.insertBefore(container, document.body.firstChild); } else { const inputBox = findInputBox(); if (inputBox) { inputBox.parentElement.insertBefore(container, inputBox); } else { console.warn('Input box not found. Appending to body.'); document.body.appendChild(container); } } populateDropdown(); addEventListeners(); } function updateControlsStyle(container) { const commonStyles = { zIndex: '9999', backgroundColor: 'rgba(0, 0, 0, 0.3)', border: '1px solid rgba(255, 255, 255, 0.1)', fontFamily: 'Arial, sans-serif', color: '#fff', borderRadius: '4px', display: 'flex', alignItems: 'center', padding: '3px 6px', fontSize: '12px', gap: '4px', }; if (CONFIG.position === 'top') { Object.assign(container.style, { ...commonStyles, position: 'fixed', top: '10px', left: '50%', transform: 'translateX(-50%)', }); } else { Object.assign(container.style, { ...commonStyles, position: 'relative', marginBottom: '5px', width: 'fit-content', }); } } function addEventListeners() { document.getElementById('toggle-selector-button').addEventListener('click', toggleSelectorVisibility); document.getElementById('download-chat-button').addEventListener('click', toggleDownloadOptions); document.getElementById('download-txt').addEventListener('click', (e) => downloadChat(e, 'txt')); document.getElementById('download-json').addEventListener('click', (e) => downloadChat(e, 'json')); document.getElementById('copy-chat-button').addEventListener('click', copyChat); document.getElementById('toggle-position-button').addEventListener('click', togglePosition); document.getElementById('copy-selector-button').addEventListener('click', copySelectorToClipboard); document.getElementById('chat-container-dropdown').addEventListener('change', onSelectChange); document.addEventListener('click', closeDropdowns); } function toggleSelectorVisibility(event) { event.stopPropagation(); const selectorContainer = document.getElementById('chat-selector-container'); selectorContainer.style.display = selectorContainer.style.display === 'none' ? 'block' : 'none'; } function toggleDownloadOptions(event) { event.stopPropagation(); const dropdownContent = document.querySelector('.dropdown-content'); dropdownContent.style.display = dropdownContent.style.display === 'none' ? 'block' : 'none'; } function copyChat(e) { e.preventDefault(); const button = e.target; const chatContent = chatMessages.join('\n\n'); if (chatContent) { navigator.clipboard.writeText(chatContent).then(() => { showTemporaryStatus(button, 'Copied!', '#4CAF50'); }).catch(() => { showTemporaryStatus(button, 'Failed to Copy', '#f44336'); }); } else { showTemporaryStatus(button, 'No Content', '#FFA500'); } } function downloadChat(e, format) { e.preventDefault(); e.stopPropagation(); const content = format === 'json' ? JSON.stringify(chatMessages, null, 2) : chatMessages.join('\n\n'); const blob = new Blob([content], { type: format === 'json' ? 'application/json' : 'text/plain' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = `chat_export.${format}`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(a.href); } function togglePosition() { CONFIG.position = CONFIG.position === 'top' ? 'bottom' : 'top'; localStorage.setItem('chatLoggerPosition', CONFIG.position); createControls(); // Recreate controls with new position } function copySelectorToClipboard(e) { e.preventDefault(); const select = document.getElementById('chat-container-dropdown'); navigator.clipboard.writeText(select.value).then(() => { alert('Selector copied to clipboard!'); }).catch(() => { alert('Failed to copy selector'); }); } function onSelectChange(e) { CONFIG.chatContainerSelector = e.target.value; localStorage.setItem('chatContainerSelector', CONFIG.chatContainerSelector); resetChatData(); if (CONFIG.chatContainerSelector) { observeChatContainer(); } } function showTemporaryStatus(button, message, bgColor) { const originalText = button.innerText; button.innerText = message; button.style.backgroundColor = bgColor; button.style.color = '#fff'; setTimeout(() => { button.innerText = originalText; button.style.backgroundColor = 'transparent'; button.style.color = '#fff'; }, 2000); } function closeDropdowns() { document.querySelectorAll('.dropdown-content, #chat-selector-container').forEach(el => { el.style.display = 'none'; }); } function checkUrlChange() { if (location.href !== lastUrl) { lastUrl = location.href; resetChatData(); if (CONFIG.chatContainerSelector) { observeChatContainer(); } } } function resetChatData() { chatMessages = []; if (observer) observer.disconnect(); } function observeChatContainer() { if (observer) observer.disconnect(); const container = document.querySelector(CONFIG.chatContainerSelector); if (container) { scanChatContent(container); observer = new MutationObserver(() => scanChatContent(container)); observer.observe(container, { childList: true, subtree: true }); } else { const intervalId = setInterval(() => { const container = document.querySelector(CONFIG.chatContainerSelector); if (container) { clearInterval(intervalId); scanChatContent(container); observer = new MutationObserver(() => scanChatContent(container)); observer.observe(container, { childList: true, subtree: true }); } }, 500); } } function scanChatContent(container) { const messageElements = container.querySelectorAll('[data-message-author-role]'); chatMessages = Array.from(messageElements).map(el => { const role = el.getAttribute('data-message-author-role'); const textElement = el.querySelector('.text-message') || el; const text = textElement.innerText.trim(); return text ? `${role === 'user' ? 'You' : 'Assistant'} said:\n${text}` : null; }).filter(Boolean); if (CONFIG.enableLogging) { console.log(chatMessages); } } function populateDropdown() { const select = document.getElementById('chat-container-dropdown'); const options = findPossibleChatContainers(); select.innerHTML = '<option value="">-- Select --</option>' + options.map(opt => `<option value="${opt.selector}">${opt.description}</option>` ).join(''); if (CONFIG.chatContainerSelector) select.value = CONFIG.chatContainerSelector; } function findPossibleChatContainers() { const selectors = [ '[data-testid^="conversation-turn-"]', '[role*="log"]', '[role*="feed"]', '[role*="list"]', '[aria-live="polite"]', '[aria-relevant="additions"]', '[class*="chat"]', '[class*="message"]', 'main', 'section', // Add more general selectors that might contain the chat 'div[class*="conversation"]', 'div[class*="thread"]', 'div[class*="dialog"]' ]; return selectors.flatMap(selector => Array.from(document.querySelectorAll(selector)).map(el => ({ selector: getUniqueSelector(el), description: buildElementDescription(el, selector) })) ).filter((item, index, self) => index === self.findIndex((t) => t.selector === item.selector) ); } function getUniqueSelector(el) { if (el.id) return `#${el.id}`; if (el.className) { const className = `.${[...el.classList].join('.')}`; return `${el.tagName.toLowerCase()}${className}`; } return el.tagName.toLowerCase(); } function buildElementDescription(el, selector) { const description = []; if (el.id) { description.push(`#${el.id}`); } if (el.className) { description.push(`.${[...el.classList].join('.')}`); } if (el.tagName) { description.push(el.tagName.toLowerCase()); } return description.join(' '); } function findInputBox() { const selectors = [ 'textarea', 'div[contenteditable="true"]', 'input[type="text"]', 'div.group.relative.flex.w-full.items-center', 'form div.relative', 'div[role="presentation"]', 'div.flex.flex-col.w-full.py-2.flex-grow.md\\:py-3.md\\:pl-4', 'div.flex.flex-col.w-full.py-[10px].flex-grow.md\\:py-4.md\\:pl-4' ]; for (const sel of selectors) { const el = document.querySelector(sel); if (el) return el; } return null; } window.addEventListener('load', init); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址