// ==UserScript== // @name 光鸭云盘-凭证提取 // @namespace https://scriptcat.org/ // @version 1.6.0 // @description 在光鸭云盘页面直接显示 Client ID 和 Refresh Token // @author 叽里咕噜 Q群1049357922 // @match *://www.guangyapan.com/* // @grant none // @run-at document-start // ==/UserScript== (function() { 'use strict'; // 光鸭已从 Supabase Auth 迁移到自建认证,优先匹配 credentials_ 前缀的 key const NEW_KEY_PREFIX = 'credentials_'; const OLD_KEY = '__GYP_SUPABASE_SESSION__'; let SESSION_KEY = null; // 运行时动态确定 let panel = null; let lastSessionRaw = null; function findSessionKey() { // 1. 优先找 credentials_ 开头的 key for (let i = 0; i < localStorage.length; i++) { const k = localStorage.key(i); if (k && k.startsWith(NEW_KEY_PREFIX)) return k; } // 2. 回退旧 key if (localStorage.getItem(OLD_KEY)) return OLD_KEY; return null; } // ====== 劫持 localStorage.setItem,感知账号切换 ====== const _origSetItem = Storage.prototype.setItem; Storage.prototype.setItem = function(key, value) { _origSetItem.call(this, key, value); if (panel && key === SESSION_KEY && value !== lastSessionRaw) { lastSessionRaw = value; extractCredentials(); } // 新 key 写入时自动切换 if (panel && key.startsWith(NEW_KEY_PREFIX) && key !== SESSION_KEY) { SESSION_KEY = key; lastSessionRaw = value; extractCredentials(); } }; // ====== JWT payload 解码(不验证签名,仅提取字段) ====== function decodeJWTPayload(token) { try { const parts = token.split('.'); if (parts.length !== 3) return null; const payload = parts[1].replace(/-/g, '+').replace(/_/g, '/'); const decoded = atob(payload); return JSON.parse(decoded); } catch (e) { return null; } } // ====== UI ====== function createPanel() { panel = document.createElement('div'); panel.id = 'gyp-credential-panel'; panel.style.cssText = ` position: fixed; top: 20px; right: 20px; z-index: 99999; background: #1e1e2e; color: #cdd6f4; border: 1px solid #45475a; border-radius: 12px; padding: 16px; min-width: 400px; max-width: 460px; font-family: 'Consolas', 'Monaco', monospace; font-size: 13px; box-shadow: 0 8px 32px rgba(0,0,0,0.4); `; panel.innerHTML = `
光鸭云盘-凭证提取 ×
正在读取凭证...
`; document.body.appendChild(panel); document.getElementById('gyp-close-btn').onclick = () => panel.remove(); document.getElementById('gyp-refresh-btn').onclick = () => { lastSessionRaw = null; SESSION_KEY = findSessionKey(); extractCredentials(); }; document.getElementById('gyp-copy-btn').onclick = copyCredentials; SESSION_KEY = findSessionKey(); extractCredentials(); } function extractCredentials() { const content = document.getElementById('gyp-content'); const copyBtn = document.getElementById('gyp-copy-btn'); if (!content || !copyBtn) return; try { if (!SESSION_KEY) SESSION_KEY = findSessionKey(); if (!SESSION_KEY) { content.innerHTML = '
未找到会话数据,请先登录光鸭云盘。
'; copyBtn.style.display = 'none'; return; } let raw = localStorage.getItem(SESSION_KEY); lastSessionRaw = raw; if (!raw) { content.innerHTML = '
未找到会话数据,请先登录光鸭云盘。
'; copyBtn.style.display = 'none'; return; } let clientId, refreshToken, email; if (SESSION_KEY === OLD_KEY) { // 旧版 Supabase 格式 const data = JSON.parse(raw); clientId = data.user?.id || '未找到'; refreshToken = data.refresh_token || '未找到'; email = data.user?.email || '未知'; } else { // 新版自建认证格式: { sub, refresh_token, access_token, ... } const data = JSON.parse(raw); clientId = data.sub || '未找到'; refreshToken = data.refresh_token || '未找到'; // 尝试从 JWT 解码邮箱 email = '未知'; if (data.access_token) { const jwt = decodeJWTPayload(data.access_token); if (jwt && jwt.email) email = jwt.email; else if (jwt && jwt.sub) email = jwt.sub; } } content.innerHTML = `
当前账号: ${email}
Client ID
${clientId}
Refresh Token
${refreshToken}
`; copyBtn.style.display = 'block'; copyBtn.dataset.text = `Client ID: ${clientId}\nRefresh Token: ${refreshToken}`; } catch (e) { content.innerHTML = `
解析失败: ${e.message}
`; copyBtn.style.display = 'none'; } } function copyCredentials() { const btn = document.getElementById('gyp-copy-btn'); const text = btn.dataset.text || ''; if (navigator.clipboard) { navigator.clipboard.writeText(text).then(() => { btn.textContent = '已复制!'; setTimeout(() => btn.textContent = '复制全部', 1500); }); } else { const ta = document.createElement('textarea'); ta.value = text; ta.style.cssText = 'position:fixed;opacity:0;'; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); document.body.removeChild(ta); btn.textContent = '已复制!'; setTimeout(() => btn.textContent = '复制全部', 1500); } } // ====== 跨标签页感知 ====== window.addEventListener('storage', function(e) { if (!panel) return; if (e.key && e.key.startsWith(NEW_KEY_PREFIX)) { SESSION_KEY = e.key; lastSessionRaw = e.newValue; extractCredentials(); } else if (e.key === OLD_KEY) { SESSION_KEY = e.key; lastSessionRaw = e.newValue; extractCredentials(); } }); // ====== 切回标签页检查 ====== window.addEventListener('focus', function() { if (!panel) return; if (!SESSION_KEY) SESSION_KEY = findSessionKey(); if (!SESSION_KEY) return; const cur = localStorage.getItem(SESSION_KEY); if (cur !== lastSessionRaw) { lastSessionRaw = cur; extractCredentials(); } }); // ====== 定时轮询兜底 ====== setInterval(function() { if (!panel) return; if (!SESSION_KEY) SESSION_KEY = findSessionKey(); if (!SESSION_KEY) return; const cur = localStorage.getItem(SESSION_KEY); if (cur !== lastSessionRaw) { lastSessionRaw = cur; extractCredentials(); } }, 3000); // ====== 启动 ====== document.addEventListener('DOMContentLoaded', createPanel); })();