您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
获取Claude的UUID并跳转到API页面,支持树形模式切换,收纳隐藏,UI更友好!
当前为
// ==UserScript== // @name Lyra's Exporter Fetch // @namespace userscript://lyra-conversation-exporter // @version 2.1 // @description 获取Claude的UUID并跳转到API页面,支持树形模式切换,收纳隐藏,UI更友好! // @author Yalums // @match https://claude.ai/* // @run-at document-start // @grant none // @license GNU General Public License v3.0 // ==/UserScript== (function() { 'use strict'; // 存储请求到的用户ID let capturedUserId = ''; // 存储工具栏的折叠状态 let isCollapsed = localStorage.getItem('claudeToolCollapsed') === 'true'; // 拦截XMLHttpRequest const originalXHROpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function(method, url) { const organizationsMatch = url.match(/api\/organizations\/([a-zA-Z0-9-]+)/); if (organizationsMatch && organizationsMatch[1]) { capturedUserId = organizationsMatch[1]; console.log("✨ 已请求用户ID:", capturedUserId); } return originalXHROpen.apply(this, arguments); }; // 拦截fetch请求 const originalFetch = window.fetch; window.fetch = function(resource, options) { if (typeof resource === 'string') { const organizationsMatch = resource.match(/api\/organizations\/([a-zA-Z0-9-]+)/); if (organizationsMatch && organizationsMatch[1]) { capturedUserId = organizationsMatch[1]; console.log("✨ 已请求用户ID:", capturedUserId); } } return originalFetch.apply(this, arguments); }; const CONTROL_ID = "lyra-tool-container"; const SWITCH_ID = "lyra-tree-mode"; const TOGGLE_ID = "lyra-toggle-button"; function injectCustomStyle() { const style = document.createElement('style'); style.textContent = ` #${CONTROL_ID} { position: fixed; right: 10px; bottom: 80px; display: flex; flex-direction: column; gap: 8px; z-index: 999999; transition: all 0.3s ease; background: rgba(255, 255, 255, 0.9); border-radius: 12px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); padding: 10px; border: 1px solid rgba(77, 171, 154, 0.3); max-width: 200px; } #${CONTROL_ID}.collapsed { transform: translateX(calc(100% - 40px)); } #${TOGGLE_ID} { position: absolute; left: 0; top: 10px; width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: rgba(77, 171, 154, 0.2); color: #4DAB9A; cursor: pointer; border: 1px solid rgba(77, 171, 154, 0.3); transition: all 0.3s; transform: translateX(-50%); } #${TOGGLE_ID}:hover { background: rgba(77, 171, 154, 0.3); } .lyra-main-controls { display: flex; flex-direction: column; gap: 8px; padding-left: 15px; } .lyra-button { display: inline-flex; align-items: center; justify-content: center; padding: 8px 10px; border-radius: 8px; cursor: pointer; font-size: 14px; background-color: rgba(77, 171, 154, 0.1); color: #4DAB9A; border: 1px solid rgba(77, 171, 154, 0.2); transition: all 0.3s; text-align: left; } .lyra-button:hover { background-color: rgba(77, 171, 154, 0.2); } .lyra-toggle { display: flex; align-items: center; font-size: 13px; margin-bottom: 5px; } .lyra-switch { position: relative; display: inline-block; width: 32px; height: 16px; margin: 0 5px; } .lyra-switch input { opacity: 0; width: 0; height: 0; } .lyra-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: .4s; border-radius: 34px; } .lyra-slider:before { position: absolute; content: ""; height: 12px; width: 12px; left: 2px; bottom: 2px; background-color: white; transition: .4s; border-radius: 50%; } input:checked + .lyra-slider { background-color: #4DAB9A; } input:checked + .lyra-slider:before { transform: translateX(16px); } .lyra-toast { position: fixed; bottom: 60px; right: 20px; background-color: #323232; color: white; padding: 8px 12px; border-radius: 6px; z-index: 1000000; opacity: 0; transition: opacity 0.3s ease-in-out; font-size: 13px; } .lyra-title { font-size: 12px; font-weight: 500; color: #4DAB9A; margin-bottom: 5px; text-align: center; } `; document.head.appendChild(style); } function showToast(message) { let toast = document.querySelector(".lyra-toast"); if (!toast) { toast = document.createElement("div"); toast.className = "lyra-toast"; document.body.appendChild(toast); } toast.textContent = message; toast.style.opacity = "1"; setTimeout(() => { toast.style.opacity = "0"; }, 2000); } function getCurrentChatUUID() { const url = window.location.href; const match = url.match(/\/chat\/([a-zA-Z0-9-]+)/); return match ? match[1] : null; } function checkUrlForTreeMode() { return window.location.href.includes('?tree=True&rendering_mode=messages&render_all_tools=true') || window.location.href.includes('&tree=True&rendering_mode=messages&render_all_tools=true'); } function toggleCollapsed() { const container = document.getElementById(CONTROL_ID); if (container) { isCollapsed = !isCollapsed; if (isCollapsed) { container.classList.add('collapsed'); } else { container.classList.remove('collapsed'); } localStorage.setItem('claudeToolCollapsed', isCollapsed); } } function createUUIDControls() { // 如果控件已存在,则不再创建 if (document.getElementById(CONTROL_ID)) return; // 创建主容器 const container = document.createElement('div'); container.id = CONTROL_ID; container.className = isCollapsed ? 'collapsed' : ''; // 创建展开/折叠按钮 const toggleButton = document.createElement('div'); toggleButton.id = TOGGLE_ID; toggleButton.innerHTML = isCollapsed ? '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>' : '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg>'; toggleButton.addEventListener('click', () => { toggleCollapsed(); toggleButton.innerHTML = isCollapsed ? '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>' : '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg>'; }); container.appendChild(toggleButton); // 创建主控件区域 const controlsArea = document.createElement('div'); controlsArea.className = 'lyra-main-controls'; // 添加标题 const title = document.createElement('div'); title.className = 'lyra-title'; title.textContent = 'Lyra Fetch Exporter'; controlsArea.appendChild(title); // 创建模式切换开关 const toggleContainer = document.createElement('div'); toggleContainer.className = 'lyra-toggle'; toggleContainer.innerHTML = ` <span> 树形(多分支)模式</span> <label class="lyra-switch"> <input type="checkbox" id="${SWITCH_ID}" ${checkUrlForTreeMode() ? 'checked' : ''}> <span class="lyra-slider"></span> </label> `; controlsArea.appendChild(toggleContainer); // 创建获取UUID按钮 const uuidButton = document.createElement('button'); uuidButton.className = 'lyra-button'; uuidButton.innerHTML = ` <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 256 256" style="margin-right: 8px;"> <path d="M128,128a32,32,0,1,0,32,32A32,32,0,0,0,128,128Zm0,48a16,16,0,1,1,16-16A16,16,0,0,1,128,176ZM128,80a32,32,0,1,0-32-32A32,32,0,0,0,128,80Zm0-48a16,16,0,1,1-16,16A16,16,0,0,1,128,32Z"/> <path d="M192,144a32,32,0,1,0,32,32A32,32,0,0,0,192,144Zm0,48a16,16,0,1,1,16-16A16,16,0,0,1,192,192Z"/> <path d="M192,128a32,32,0,1,0-32-32A32,32,0,0,0,192,128Zm0-48a16,16,0,1,1-16,16A16,16,0,0,1,192,80Z"/> <path d="M64,144a32,32,0,1,0,32,32A32,32,0,0,0,64,144Zm0,48a16,16,0,1,1,16-16A16,16,0,0,1,64,192Z"/> <path d="M64,128a32,32,0,1,0-32-32A32,32,0,0,0,64,128Zm0-48a16,16,0,1,1-16,16A16,16,0,0,1,64,80Z"/> </svg> 获取对话UUID `; // 处理UUID按钮点击事件 uuidButton.addEventListener('click', () => { const uuid = getCurrentChatUUID(); if (uuid) { if (!capturedUserId) { showToast("未能请求用户ID,请刷新页面或进行一些操作"); return; } navigator.clipboard.writeText(uuid).then(() => { console.log("UUID 已复制:", uuid); showToast("UUID已复制!"); }).catch(err => { console.error("复制失败:", err); showToast("复制失败"); }); const treeMode = document.getElementById(SWITCH_ID).checked; const jumpUrl = `https://claude.ai/api/organizations/${capturedUserId}/chat_conversations/${uuid}${treeMode ? '?tree=True&rendering_mode=messages&render_all_tools=true' : ''}`; window.open(jumpUrl, "_blank"); } else { showToast("未找到UUID!"); } }); controlsArea.appendChild(uuidButton); // 创建导出JSON按钮 const downloadJsonButton = document.createElement('button'); downloadJsonButton.className = 'lyra-button'; downloadJsonButton.innerHTML = ` <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 256 256" style="margin-right: 8px;"> <path d="M224,152v56a16,16,0,0,1-16,16H48a16,16,0,0,1-16-16V152a8,8,0,0,1,16,0v56H208V152a8,8,0,0,1,16,0Zm-101.66,5.66a8,8,0,0,0,11.32,0l40-40a8,8,0,0,0-11.32-11.32L136,132.69V40a8,8,0,0,0-16,0v92.69L93.66,106.34a8,8,0,0,0-11.32,11.32Z"></path> </svg> 导出对话JSON `; // 处理导出JSON按钮点击事件 downloadJsonButton.addEventListener('click', async () => { const uuid = getCurrentChatUUID(); if (uuid) { if (!capturedUserId) { showToast("未能请求用户ID,请刷新页面或进行一些操作"); return; } try { const treeMode = document.getElementById(SWITCH_ID).checked; const apiUrl = `https://claude.ai/api/organizations/${capturedUserId}/chat_conversations/${uuid}${treeMode ? '?tree=True&rendering_mode=messages&render_all_tools=true' : ''}`; // 获取JSON数据 showToast("正在获取数据..."); const response = await fetch(apiUrl); if (!response.ok) { throw new Error(`请求失败: ${response.status}`); } const data = await response.json(); // 创建下载 const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `claude_${uuid.substring(0, 8)}_${new Date().toISOString().slice(0,10)}.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); showToast("JSON导出成功!"); } catch (error) { console.error("导出失败:", error); showToast("导出失败: " + error.message); } } else { showToast("未找到对话UUID!"); } }); controlsArea.appendChild(downloadJsonButton); container.appendChild(controlsArea); document.body.appendChild(container); } // 初始化脚本 function initScript() { injectCustomStyle(); // 延迟执行,确保DOM加载完毕 setTimeout(() => { if (/\/chat\/[a-zA-Z0-9-]+/.test(window.location.href)) { createUUIDControls(); } }, 1000); // 监听 URL 变化(防止 SPA 页面跳转失效) let lastUrl = window.location.href; const observer = new MutationObserver(() => { if (window.location.href !== lastUrl) { lastUrl = window.location.href; setTimeout(() => { if (/\/chat\/[a-zA-Z0-9-]+/.test(lastUrl)) { createUUIDControls(); } }, 1000); } }); observer.observe(document.body, { childList: true, subtree: true }); } // 等待DOM加载完成 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initScript); } else { initScript(); } })();