// ==UserScript== // @name 网页数据清除与管理 // @namespace http://tampermonkey.net/ // @version 3.1 // @description 简洁数据清理工具,所有功能集中于对话框,一键清除网站各类存储数据 // @author Chaos // @match *://*/* // @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NCA2NCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjNDQ0IiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PHBhdGggZD0iTTIwIDI0TDQ0IDI0TDQyIDUyTDIyIDUyWiIgLz48cGF0aCBkPSJNMjggMjhMMjggNDggTTM2IDI4TDM2IDQ4IiAvPjxwYXRoIGQ9Ik0xNiAyMEw0OCAyMEw0NSAxNkwxOSAxNloiIC8+PC9zdmc+ // @grant GM_registerMenuCommand // @grant GM_setClipboard // @grant GM_getValue // @grant GM_setValue // @run-at document-end // @license MIT // ==/UserScript== (function() { 'use strict'; // ----- 语言包(精简版)----- const i18n = { 'zh-CN': { title: '🧹 网页数据清除工具', clearAll: '🗑️ 清理当前网站所有数据', clearCookies: '🍪 清理当前网站Cookie', clearLocal: '💾 清理本地存储', clearSession: '📝 清理会话存储', clearIndexedDB: '🗄️ 清理IndexedDB', clearCache: '📦 清理缓存存储', clearSW: '⚙️ 注销Service Worker', clearFrame: '🖼️ 清理同域框架存储', copyCookies: '📋 复制当前Cookie', guide: '📖 查看清理指南', langSwitch: '🌐 切换语言', langZh: '简体中文', langEn: 'English', close: '关闭', allCleared: '✅ 已清除 %s 的所有数据:\n%s\n\n⚠️ 注意:部分受保护数据(如HttpOnly Cookie)可能无法清除。', cookieCleared: '🍪 Cookie已清除 (%s)\n- 已清除:%d 个\n- 剩余:%d 个', localCleared: '💾 本地存储已清除 (%s)\n- 共清除 %d 项数据', sessionCleared: '📝 会话存储已清除 (%s)\n- 共清除 %d 项数据', indexedDBCleared: '🗄️ IndexedDB已清除 (%s)\n- 共清除 %d 个数据库', cacheCleared: '📦 缓存存储已清除 (%s)\n- 共清除 %d 个缓存', swCleared: '⚙️ Service Worker已注销 (%s)\n- 共注销 %d 个', frameCleared: '🖼️ 同域框架存储已清除 (%s)\n- 清理框架:%d 个\n- 清除数据项:%d 个', noCookies: '🍪 未检测到 %s 的Cookie', noLocal: '💾 未检测到 %s 的本地存储数据', noSession: '📝 未检测到 %s 的会话存储数据', noIndexedDB: '🗄️ 未检测到 %s 的IndexedDB数据库', noCache: '📦 未检测到 %s 的缓存存储', noSW: '⚙️ 未检测到 %s 的Service Worker', noFrame: '🖼️ 未检测到 %s 的同域框架', nothingCleared: '⚠️ 未清除 %s 的任何数据', cookiesCopied: '📋 Cookie已复制到剪贴板', noCookiesToCopy: '📭 无Cookie可复制', guideText: `📖 网页数据清理指南: ━━━━━━━━━━━━━━━━━━━━━━ 1️⃣ 本工具可清除的数据类型: ✅ Cookie(非HttpOnly类型) ✅ LocalStorage / SessionStorage ✅ IndexedDB数据库 ✅ Cache Storage缓存 ✅ Service Worker ✅ 同域iframe内存储 2️⃣ 无法自动清除的数据: ❌ HttpOnly Cookie(浏览器保护) ❌ 跨域第三方数据 ❌ 浏览器DNS缓存 3️⃣ 手动彻底清理(推荐): 🔧 浏览器设置 → 隐私与安全 → 清除浏览数据 → 勾选“Cookie和网站数据” → 时间范围选择“所有时间” → 点击清除 4️⃣ 清理后建议强制刷新: 🔄 Ctrl+Shift+R (Win) / Cmd+Shift+R (Mac)` }, 'en': { title: '🧹 Web Data Cleaner', clearAll: '🗑️ Clear All Site Data', clearCookies: '🍪 Clear Cookies', clearLocal: '💾 Clear Local Storage', clearSession: '📝 Clear Session Storage', clearIndexedDB: '🗄️ Clear IndexedDB', clearCache: '📦 Clear Cache Storage', clearSW: '⚙️ Unregister Service Worker', clearFrame: '🖼️ Clear Same-Domain Frame Storage', copyCookies: '📋 Copy Cookies', guide: '📖 Data Clearing Guide', langSwitch: '🌐 Language', langZh: '简体中文', langEn: 'English', close: 'Close', allCleared: '✅ All data cleared for %s:\n%s\n\n⚠️ Note: Some protected data (e.g., HttpOnly Cookies) may remain.', cookieCleared: '🍪 Cookies cleared (%s)\n- Cleared: %d\n- Remaining: %d', localCleared: '💾 Local storage cleared (%s)\n- Items cleared: %d', sessionCleared: '📝 Session storage cleared (%s)\n- Items cleared: %d', indexedDBCleared: '🗄️ IndexedDB cleared (%s)\n- Databases cleared: %d', cacheCleared: '📦 Cache storage cleared (%s)\n- Caches cleared: %d', swCleared: '⚙️ Service workers unregistered (%s)\n- Unregistered: %d', frameCleared: '🖼️ Frame storage cleared (%s)\n- Frames processed: %d\n- Items cleared: %d', noCookies: '🍪 No cookies detected for %s', noLocal: '💾 No local storage data for %s', noSession: '📝 No session storage data for %s', noIndexedDB: '🗄️ No IndexedDB databases for %s', noCache: '📦 No cache storage for %s', noSW: '⚙️ No service workers for %s', noFrame: '🖼️ No same-domain frames for %s', nothingCleared: '⚠️ No data cleared for %s', cookiesCopied: '📋 Cookies copied to clipboard', noCookiesToCopy: '📭 No cookies to copy', guideText: `📖 Web Data Clearing Guide: ━━━━━━━━━━━━━━━━━━━━━━ 1️⃣ Data that can be cleared: ✅ Cookies (non-HttpOnly) ✅ LocalStorage / SessionStorage ✅ IndexedDB databases ✅ Cache Storage ✅ Service Workers ✅ Same-origin iframe storage 2️⃣ Data requiring manual clearing: ❌ HttpOnly Cookies ❌ Cross-domain data ❌ Browser DNS cache 3️⃣ Manual clearing steps: 🔧 Browser Settings → Privacy & Security → Clear browsing data → Select "Cookies and site data" → Time range: "All time" → Clear data 4️⃣ Force refresh after clearing: 🔄 Ctrl+Shift+R (Win) / Cmd+Shift+R (Mac)` } }; // ----- 当前语言 ----- let lang = (() => { let saved = GM_getValue('preferred_language'); if (saved && i18n[saved]) return saved; return navigator.language.startsWith('zh') ? 'zh-CN' : 'en'; })(); const t = (key, ...args) => { let str = i18n[lang][key] || i18n['en'][key] || key; args.forEach(arg => { str = str.replace(/%s|%d/, arg); }); return str; }; // ----- 当前域名 & 子域名变体(用于彻底清除Cookie)----- const domain = window.location.hostname; const getSubdomains = (host) => { let parts = host.split('.'); let set = new Set([host, host.replace(/^www\./i, ''), '.' + host]); for (let i = 0; i < parts.length; i++) { for (let j = i; j < parts.length; j++) { let sub = parts.slice(i, j + 1).join('.'); if (sub) set.add(sub).add('.' + sub); } } return Array.from(set); }; const subdomains = getSubdomains(domain); // ----- 清理函数(异步/同步)----- const cleaners = { clearCookies: () => { let before = document.cookie ? document.cookie.split('; ').filter(c => c).length : 0; if (before === 0) return { cleared: 0, remaining: 0 }; let paths = ['/', '/login', '/account', '/api', '/admin']; document.cookie.split('; ').forEach(cookie => { let name = cookie.split('=')[0]; paths.forEach(path => { subdomains.forEach(sub => { document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=${path}; domain=${sub};`; document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=${path};`; }); }); }); let after = document.cookie ? document.cookie.split('; ').filter(c => c).length : 0; return { cleared: before - after, remaining: after }; }, clearLocal: () => { let c = localStorage.length; if (c) localStorage.clear(); return c; }, clearSession: () => { let c = sessionStorage.length; if (c) sessionStorage.clear(); return c; }, clearIndexedDB: () => new Promise(resolve => { if (!window.indexedDB) resolve(0); else indexedDB.databases().then(dbs => { let count = 0, total = dbs.length; if (!total) resolve(0); dbs.forEach((db, i) => { if (db.name) { let req = indexedDB.deleteDatabase(db.name); req.onsuccess = () => { count++; if (i === total - 1) resolve(count); }; req.onerror = () => { if (i === total - 1) resolve(count); }; } else if (i === total - 1) resolve(count); }); }).catch(() => resolve(0)); }), clearCache: () => new Promise(resolve => { if (!('caches' in window)) resolve(0); else caches.keys().then(names => { let count = 0, total = names.length; if (!total) resolve(0); names.forEach((name, i) => { if (name.includes(domain) || name.includes(domain.replace(/^www\./, ''))) { caches.delete(name).then(success => { if (success) count++; if (i === total - 1) resolve(count); }); } else if (i === total - 1) resolve(count); }); }).catch(() => resolve(0)); }), clearSW: () => new Promise(resolve => { if (!('serviceWorker' in navigator)) resolve(0); else navigator.serviceWorker.getRegistrations().then(regs => { let count = 0, total = regs.length; if (!total) resolve(0); regs.forEach((reg, i) => { if (reg.scope.includes(domain)) { reg.unregister().then(success => { if (success) count++; if (i === total - 1) resolve(count); }); } else if (i === total - 1) resolve(count); }); }).catch(() => resolve(0)); }), clearFrame: () => { let frameCount = 0, itemCount = 0; document.querySelectorAll('iframe, frame').forEach(frame => { try { if (frame.contentWindow && frame.contentWindow.location.hostname && (subdomains.includes(frame.contentWindow.location.hostname) || frame.contentWindow.location.hostname.includes(domain))) { frameCount++; let local = frame.contentWindow.localStorage.length; if (local) { frame.contentWindow.localStorage.clear(); itemCount += local; } let session = frame.contentWindow.sessionStorage.length; if (session) { frame.contentWindow.sessionStorage.clear(); itemCount += session; } } } catch(e) {} }); return { frameCount, itemCount }; } }; // ----- 结果展示与处理 ----- const showResult = async (action, successMsg, failMsg, reload = false) => { let result = await action(); let isSuccess = (typeof result === 'object' ? result.cleared || result.itemCount : result); if (isSuccess) { if (typeof result === 'object') { if (result.cleared !== undefined) alert(successMsg(domain, result.cleared, result.remaining)); else alert(successMsg(domain, result.frameCount, result.itemCount)); } else { alert(successMsg(domain, result)); } if (reload) location.reload(); } else { alert(failMsg(domain)); } }; const handlers = { clearAll: async () => { let cookie = cleaners.clearCookies(); let local = cleaners.clearLocal(); let session = cleaners.clearSession(); let idb = await cleaners.clearIndexedDB(); let cache = await cleaners.clearCache(); let sw = await cleaners.clearSW(); let frame = cleaners.clearFrame(); let results = []; if (cookie.cleared) results.push(`🍪 Cookie:${cookie.cleared} 个 (剩余 ${cookie.remaining})`); if (local) results.push(`💾 本地存储:${local} 项`); if (session) results.push(`📝 会话存储:${session} 项`); if (idb) results.push(`🗄️ IndexedDB:${idb} 个数据库`); if (cache) results.push(`📦 缓存存储:${cache} 个`); if (sw) results.push(`⚙️ Service Worker:${sw} 个`); if (frame.itemCount) results.push(`🖼️ 同域框架:${frame.itemCount} 项 (${frame.frameCount} 个框架)`); if (results.length) alert(t('allCleared', domain, results.join('\n'))); else alert(t('nothingCleared', domain)); if (results.length) location.reload(); }, clearCookies: () => showResult(() => cleaners.clearCookies(), (d, c, r) => t('cookieCleared', d, c, r), (d) => t('noCookies', d), true), clearLocal: () => showResult(() => cleaners.clearLocal(), (d, c) => t('localCleared', d, c), (d) => t('noLocal', d), true), clearSession: () => showResult(() => cleaners.clearSession(), (d, c) => t('sessionCleared', d, c), (d) => t('noSession', d), true), clearIndexedDB: () => showResult(() => cleaners.clearIndexedDB(), (d, c) => t('indexedDBCleared', d, c), (d) => t('noIndexedDB', d), true), clearCache: () => showResult(() => cleaners.clearCache(), (d, c) => t('cacheCleared', d, c), (d) => t('noCache', d), true), clearSW: () => showResult(() => cleaners.clearSW(), (d, c) => t('swCleared', d, c), (d) => t('noSW', d), true), clearFrame: () => showResult(() => cleaners.clearFrame(), (d, fc, ic) => t('frameCleared', d, fc, ic), (d) => t('noFrame', d), true), copyCookies: () => { if (document.cookie) { GM_setClipboard(document.cookie); alert(t('cookiesCopied')); } else alert(t('noCookiesToCopy')); }, showGuide: () => alert(t('guideText')) }; // ----- 创建对话框 ----- const showDialog = () => { let old = document.querySelector('#data-cleaner-dialog'); if (old) old.remove(); let overlay = document.createElement('div'); overlay.style.cssText = `position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);z-index:999998;`; let dialog = document.createElement('div'); dialog.id = 'data-cleaner-dialog'; dialog.style.cssText = `position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#fff;border-radius:12px;box-shadow:0 4px 20px rgba(0,0,0,0.2);z-index:999999;width:420px;max-width:90%;max-height:80vh;overflow-y:auto;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;padding:20px;`; // 标题 + 域名 let titleDiv = document.createElement('div'); titleDiv.innerHTML = `
🌐 ${domain}
`; dialog.appendChild(titleDiv); // 工具按钮网格 let grid = document.createElement('div'); grid.style.display = 'grid'; grid.style.gridTemplateColumns = 'repeat(2, 1fr)'; grid.style.gap = '8px'; grid.style.marginBottom = '20px'; let tools = [ { text: 'clearAll', handler: handlers.clearAll }, { text: 'clearCookies', handler: handlers.clearCookies }, { text: 'clearLocal', handler: handlers.clearLocal }, { text: 'clearSession', handler: handlers.clearSession }, { text: 'clearIndexedDB', handler: handlers.clearIndexedDB }, { text: 'clearCache', handler: handlers.clearCache }, { text: 'clearSW', handler: handlers.clearSW }, { text: 'clearFrame', handler: handlers.clearFrame }, { text: 'copyCookies', handler: handlers.copyCookies }, { text: 'guide', handler: handlers.showGuide } ]; tools.forEach(tool => { let btn = document.createElement('button'); btn.textContent = t(tool.text); btn.style.cssText = `padding:8px;border:1px solid #ddd;border-radius:8px;background:#f8f8f8;cursor:pointer;transition:0.2s;font-size:13px;`; btn.onmouseover = () => btn.style.background = '#e8e8e8'; btn.onmouseout = () => btn.style.background = '#f8f8f8'; btn.onclick = () => { tool.handler(); dialog.remove(); overlay.remove(); }; grid.appendChild(btn); }); dialog.appendChild(grid); // 语言切换 let langBox = document.createElement('div'); langBox.style.marginBottom = '20px'; langBox.innerHTML = `