您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a minimal export button with auto-daily export when new chats are detected
// ==UserScript== // @name Export All DuckDuckGo AI Chats // @namespace http://tampermonkey.net/ // @version 2.1 // @description Adds a minimal export button with auto-daily export when new chats are detected // @author manuc66 // @match *://duckduckgo.com/* // @grant none // ==/UserScript== (function () { 'use strict'; // Configuration const STORAGE_KEYS = { LAST_EXPORT_DATE: 'aiChatExporter_lastExportDate', LAST_MOST_RECENT_EDIT: 'aiChatExporter_lastMostRecentEdit', DEVICE_ID_KEY: 'aiChatExporter_deviceID' }; function getDeviceID() { let deviceID = localStorage.getItem(STORAGE_KEYS.DEVICE_ID_KEY); if (!deviceID) { // Generate a new device ID (UUID format) deviceID = 'device-' + Date.now() + '-' + Math.random().toString(36).slice(2, 11); localStorage.setItem(STORAGE_KEYS.DEVICE_ID_KEY, deviceID); } return deviceID; } // Utility functions function getFormattedDateTime() { const now = new Date(); const year = now.getFullYear(); const month = String(now.getMonth() + 1).padStart(2, '0'); const day = String(now.getDate()).padStart(2, '0'); const hours = String(now.getHours()).padStart(2, '0'); const minutes = String(now.getMinutes()).padStart(2, '0'); return `${year}-${month}-${day}_${hours}-${minutes}`; } function getTodayDateString() { const now = new Date(); return now.toDateString(); // Returns format like "Wed Oct 25 2023" } function getMostRecentChatEdit(savedAIChats) { try { const data = JSON.parse(savedAIChats); if (!data.chats || !Array.isArray(data.chats)) { return null; } const mostRecentEdit = data.chats.reduce((max, chat) => chat.lastEdit > max ? chat.lastEdit : max, "1970-01-01T00:00:00Z" ); return new Date(mostRecentEdit); } catch (error) { console.error('Error getting most recent chat edit:', error); return null; } } function getExportFileName() { const datetime = getFormattedDateTime(); const deviceID = getDeviceID(); return `savedAIChats_${datetime}_${deviceID}.json`; } function exportChats(isAutomatic = false) { const savedAIChats = localStorage.getItem('savedAIChats'); if (!savedAIChats) { if (!isAutomatic) { alert('No savedAIChats found in local storage.'); } return false; } try { const data = JSON.parse(savedAIChats); const filename = getExportFileName(); const blob = new Blob([JSON.stringify(data, null, 2)], {type: 'application/json'}); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = filename; // Programmatically click the link to trigger the download document.body.appendChild(link); link.click(); document.body.removeChild(link); // Update tracking data const today = getTodayDateString(); const mostRecentEdit = getMostRecentChatEdit(savedAIChats); const chatCount = data.chats ? data.chats.length : 0; localStorage.setItem(STORAGE_KEYS.LAST_EXPORT_DATE, today); if (mostRecentEdit) { localStorage.setItem(STORAGE_KEYS.LAST_MOST_RECENT_EDIT, mostRecentEdit.toISOString()); } if (isAutomatic) { showNotification(`Auto-exported ${chatCount} chats (${filename})`); } return true; } catch (error) { console.error('Export failed:', error); if (!isAutomatic) { alert('Export failed: ' + error.message); } return false; } } function showNotification(message) { const notification = document.createElement('div'); notification.textContent = message; notification.style.position = 'fixed'; notification.style.top = '20px'; notification.style.right = '20px'; notification.style.backgroundColor = '#28a745'; notification.style.color = 'white'; notification.style.padding = '10px 15px'; notification.style.borderRadius = '5px'; notification.style.zIndex = '10000'; notification.style.fontSize = '14px'; notification.style.maxWidth = '300px'; notification.style.boxShadow = '0 4px 12px rgba(0,0,0,0.3)'; notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s ease'; document.body.appendChild(notification); // Fade in setTimeout(() => notification.style.opacity = '1', 100); // Fade out and remove after 4 seconds setTimeout(() => { notification.style.opacity = '0'; setTimeout(() => document.body.removeChild(notification), 300); }, 4000); } function checkForNewChats() { const savedAIChats = localStorage.getItem('savedAIChats'); if (!savedAIChats) return false; try { const currentMostRecentEdit = getMostRecentChatEdit(savedAIChats); if (!currentMostRecentEdit) return false; const lastExportDate = localStorage.getItem(STORAGE_KEYS.LAST_EXPORT_DATE); const lastMostRecentEditStr = localStorage.getItem(STORAGE_KEYS.LAST_MOST_RECENT_EDIT); const today = getTodayDateString(); const isNewDay = lastExportDate !== today; let hasNewChats = false; if (lastMostRecentEditStr) { const lastMostRecentEdit = new Date(lastMostRecentEditStr); hasNewChats = currentMostRecentEdit > lastMostRecentEdit; } else { // First time running, consider it as having new chats hasNewChats = true; } return isNewDay && hasNewChats; } catch (error) { console.error('Error checking for new chats:', error); return false; } } function startAutoExportCheck() { // Check immediately if (checkForNewChats()) { exportChats(true); } // Then check every 30 minutes setInterval(() => { if (checkForNewChats()) { exportChats(true); } }, 30 * 60 * 1000); // 30 minutes } // Create the export button const exportButton = document.createElement('button'); exportButton.innerText = '💾'; exportButton.title = 'Export AI Chats (Auto-exports daily when new chats detected)'; exportButton.id = 'ai-chat-export-btn'; // Base styles for the button exportButton.style.position = 'fixed'; exportButton.style.top = '50%'; exportButton.style.right = '10px'; exportButton.style.transform = 'translateY(-50%)'; exportButton.style.width = '40px'; exportButton.style.height = '40px'; exportButton.style.padding = '0'; exportButton.style.backgroundColor = '#007bff'; exportButton.style.color = '#fff'; exportButton.style.border = 'none'; exportButton.style.borderRadius = '50%'; exportButton.style.cursor = 'pointer'; exportButton.style.zIndex = '9999'; exportButton.style.fontSize = '16px'; exportButton.style.display = 'flex'; exportButton.style.alignItems = 'center'; exportButton.style.justifyContent = 'center'; exportButton.style.boxShadow = '0 2px 8px rgba(0,0,0,0.2)'; exportButton.style.transition = 'all 0.2s ease'; // Add hover effects exportButton.addEventListener('mouseenter', function () { this.style.backgroundColor = '#0056b3'; this.style.transform = 'translateY(-50%) scale(1.1)'; }); exportButton.addEventListener('mouseleave', function () { this.style.backgroundColor = '#007bff'; this.style.transform = 'translateY(-50%) scale(1)'; }); // Responsive styles using CSS const style = document.createElement('style'); style.innerHTML = ` @media (max-width: 600px) { #ai-chat-export-btn { width: 35px !important; height: 35px !important; font-size: 14px !important; right: 8px !important; } } @media (max-width: 400px) { #ai-chat-export-btn { width: 30px !important; height: 30px !important; font-size: 12px !important; right: 5px !important; } } `; document.head.appendChild(style); // Append the button to the body document.body.appendChild(exportButton); // Add click event to the button exportButton.addEventListener('click', function () { // Add click animation this.style.transform = 'translateY(-50%) scale(0.95)'; setTimeout(() => { this.style.transform = 'translateY(-50%) scale(1)'; }, 100); exportChats(false); }); // Start the auto-export monitoring // Wait a bit for the page to fully load setTimeout(startAutoExportCheck, 3000); })();