Website Data Cleaner

自动识别系统语言,一键清除当前网站各类数据(localStorage/Cookie/数据库等)

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Website Data Cleaner
// @namespace    http://tampermonkey.net/
// @version      1.3.0
// @description  One-click clear current website data(localStorage/Cookie/Database etc.)
// @description:zh-CN 自动识别系统语言,一键清除当前网站各类数据(localStorage/Cookie/数据库等)
// @author       ChiamZhang
// @match        *://*/*
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_openInTab
// @run-at       document-end
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // ================= 1. 语言包配置(统一管理中英文文本)=================
    const LANG_PACK = {
        // 中文配置
        zh: {
            menu: {
                clearAll: '🗑️ 清除当前网站所有数据',
                clearBasic: '📦 仅清除 localStorage + SessionStorage',
                clearCookie: '🍪 仅清除当前网站Cookie',
                clearDB: '🗄️ 仅清除 IndexedDB + Web SQL',
                feedback: 'Github 主页',
            },
            confirm: {
                clearAll: '⚠️ 确认清除当前网站所有数据?\n包含:localStorage、SessionStorage、Cookie、IndexedDB、Web SQL\n清除后不可恢复,登录状态会失效!',
                clearBasic: '确认清除当前网站的localStorage和SessionStorage?\n这会清除网站保存的本地设置和临时数据!',
                clearCookie: '确认清除当前网站的所有Cookie?\n这会导致登录状态失效,需要重新登录!',
                clearDB: '确认清除当前网站的IndexedDB和Web SQL数据库?\n这会清除网站保存的离线数据和缓存内容!'
            },
            notification: {
                clearAllSuccess: '✅ 当前网站所有数据清除成功!',
                clearAllPartial: '部分数据清除成功,部分数据可能未清除(详见控制台)',
                clearBasicSuccess: '✅ 基础存储数据清除成功!',
                clearCookieSuccess: '✅ Cookie清除成功!',
                clearDBSuccess: '✅ 数据库数据清除成功!',
                feedbackOpen: '已打开反馈页面,感谢你的建议!',
                error: '❌ 数据清除过程中发生错误',
                switchEnable: '%s已启用',
                switchDisable: '%s已禁用'
            },
            console: {
                init: '【网站数据清除工具】菜单已加载完成,可在油猴菜单中操作',
                clearLocalStorage: '[数据清除] localStorage 清除成功',
                clearSessionStorage: '[数据清除] sessionStorage 清除成功',
                clearCookie: '[数据清除] Cookie 清除成功(共%s个)',
                clearCookieFail: '[数据清除] Cookie 清除失败:%s',
                clearIndexedDB: '[数据清除] IndexedDB 清除完成(共%s个数据库)',
                clearIndexedDBBlocked: '[数据清除] IndexedDB %s 被占用(可能有其他标签页打开)',
                clearWebSQL: '[数据清除] Web SQL 清除完成(处理%s个匹配模式)',
                clearFail: '[数据清除%s] %s'
            }
        },
        // 英文配置
        en: {
            menu: {
                clearAll: '🗑️ Clear All Data of Current Website',
                clearBasic: '📦 Clear Only localStorage + SessionStorage',
                clearCookie: '🍪 Clear Only Current Website Cookies',
                clearDB: '🗄️ Clear Only IndexedDB + Web SQL',
                feedback: 'Project Link:Github Link',
            },
            confirm: {
                clearAll: '⚠️ Confirm to clear all data of current website?\nIncluded: localStorage, SessionStorage, Cookies, IndexedDB, Web SQL\nIrreversible, login status will be lost!',
                clearBasic: 'Confirm to clear localStorage and SessionStorage of current website?\nThis will delete local settings and temporary data saved by the website!',
                clearCookie: 'Confirm to clear all cookies of current website?\nLogin status will be lost, need to re-login!',
                clearDB: 'Confirm to clear IndexedDB and Web SQL of current website?\nThis will delete offline data and cache content saved by the website!'
            },
            notification: {
                clearAllSuccess: '✅ All data of current website cleared successfully!',
                clearAllPartial: 'Partial data cleared successfully, some data may not be deleted (see console for details)',
                clearBasicSuccess: '✅ Basic storage data cleared successfully!',
                clearCookieSuccess: '✅ Cookies cleared successfully!',
                clearDBSuccess: '✅ Database data cleared successfully!',
                feedbackOpen: 'Feedback page opened, thank you for your suggestion!',
                error: '❌ Error occurred during data cleaning',
                switchEnable: '%s enabled',
                switchDisable: '%s disabled'
            },
            console: {
                init: '[Website Data Cleaner] Menu loaded successfully, operate via Tampermonkey menu',
                clearLocalStorage: '[Data Cleanup] localStorage cleared successfully',
                clearSessionStorage: '[Data Cleanup] sessionStorage cleared successfully',
                clearCookie: '[Data Cleanup] Cookies cleared successfully (total %s)',
                clearCookieFail: '[Data Cleanup] Cookie cleanup failed: %s',
                clearIndexedDB: '[Data Cleanup] IndexedDB cleaned up (total %s databases)',
                clearIndexedDBBlocked: '[Data Cleanup] IndexedDB %s is occupied (other tabs may be open)',
                clearWebSQL: '[Data Cleanup] Web SQL cleaned up (processed %s patterns)',
                clearFail: '[Data Cleanup%s] %s'
            }
        }
    };

    // ================= 2. 自动检测系统语言 =================
    let currentLang = 'en'; // 默认英文
    const userLang = navigator.language || navigator.userLanguage; // 获取浏览器/系统语言

    // 判断是否为中文环境(支持 zh-CN、zh-TW、zh-HK 等)
    if (userLang.toLowerCase().startsWith('zh')) {
        currentLang = 'zh';
    }

    // 获取当前语言对应的文本(简化调用)
    const t = (path) => {
        const keys = path.split('.');
        return keys.reduce((obj, key) => obj?.[key] || path, LANG_PACK[currentLang]);
    };

    // ================= 3. 全局变量 =================
    const menuId = []; // 存储菜单ID,用于后续注销
    // 菜单配置(关联语言包中的菜单名称)
    const menuAll = [
        ['menu_clearAll', t('menu.clearAll'), 'clearAllData', false],
        ['menu_clearBasic', t('menu.clearBasic'), 'clearBasicData', false],
        ['menu_clearCookie', t('menu.clearCookie'), 'clearOnlyCookie', false],
        ['menu_clearDB', t('menu.clearDB'), 'clearOnlyDB', false],
        ['menu_feedback', t('menu.feedback'), 'openFeedback', false]
    ];

    // ================= 4. 核心清除函数 =================
    /**
     * 清除localStorage
     */
    function clearLocalStorage() {
        try {
            localStorage.clear();
            console.log(t('console.clearLocalStorage'));
            return true;
        } catch (e) {
            console.error(t('console.clearCookieFail'), e);
            return false;
        }
    }

    /**
     * 清除sessionStorage
     */
    function clearSessionStorage() {
        try {
            sessionStorage.clear();
            console.log(t('console.clearSessionStorage'));
            return true;
        } catch (e) {
            console.error(t('console.clearCookieFail'), e);
            return false;
        }
    }

    /**
     * 清除当前网站的Cookie
     */
    function clearCookies() {
        try {
            const cookies = document.cookie.split(';').filter(c => c.trim());
            const domain = window.location.hostname;

            // 兼容不同子域名和路径的Cookie清除
            cookies.forEach(cookie => {
                const eqPos = cookie.indexOf('=');
                const name = eqPos > -1 ? cookie.substr(0, eqPos).trim() : cookie.trim();

                // 多场景清除确保生效
                document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=${domain}; SameSite=None; Secure`;
                document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=.${domain}; SameSite=None; Secure`;
                document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; SameSite=None; Secure`;
            });

            console.log(t('console.clearCookie'), cookies.length);
            return true;
        } catch (e) {
            console.error(t('console.clearCookieFail'), e);
            return false;
        }
    }

    /**
     * 清除IndexedDB
     */
    async function clearIndexedDB() {
        try {
            if (!window.indexedDB) return true;

            const databases = await indexedDB.databases();
            if (databases.length === 0) return true;

            for (const db of databases) {
                await new Promise((resolve) => {
                    const request = indexedDB.deleteDatabase(db.name);
                    request.onsuccess = () => resolve(true);
                    request.onerror = (err) => {
                        console.warn(t('console.clearIndexedDBBlocked'), db.name, err);
                        resolve(false);
                    };
                    request.onblocked = () => {
                        console.warn(t('console.clearIndexedDBBlocked'), db.name);
                        resolve(false);
                    };
                });
            }

            console.log(t('console.clearIndexedDB'), databases.length);
            return true;
        } catch (e) {
            console.error(t('console.clearCookieFail'), e);
            return false;
        }
    }

    /**
     * 清除Web SQL
     */
    function clearWebSQL() {
        try {
            if (!window.openDatabase) return true;

            // 遍历常见的Web SQL数据库名称模式
            const dbNamePatterns = ['web sql', 'site_', 'app_', 'local_', 'data_', 'db_'];
            let clearedCount = 0;

            dbNamePatterns.forEach(pattern => {
                try {
                    const db = openDatabase(`temp_${pattern}`, '1.0', 'Temporary DB for deletion', 1024 * 1024);
                    db.transaction(tx => {
                        tx.executeSql('DROP TABLE IF EXISTS main');
                        clearedCount++;
                    });
                } catch (e) {}
            });

            console.log(t('console.clearWebSQL'), clearedCount);
            return true;
        } catch (e) {
            console.error(t('console.clearCookieFail'), e);
            return true;
        }
    }

    // ================= 5. 功能入口函数 =================
    /**
     * 显示操作结果通知
     * @param {string} messageKey 语言包中的消息键名
     * @param {boolean} success 是否成功
     * @param {Array} args 消息格式化参数(可选)
     */
    function showResult(messageKey, success = true, args = []) {
        let message = t(`notification.${messageKey}`);
        // 格式化消息(支持占位符 %s)
        if (args.length > 0) {
            args.forEach(arg => {
                message = message.replace(/%s/, arg);
            });
        }

        // 优先使用浏览器通知API
        if (Notification.permission === 'granted') {
            new Notification(
                currentLang === 'zh' ? '网站数据清除工具' : 'Website Data Cleaner',
                { body: message }
            );
        } else if (Notification.permission !== 'denied') {
            Notification.requestPermission().then(perm => {
                if (perm === 'granted') {
                    new Notification(
                        currentLang === 'zh' ? '网站数据清除工具' : 'Website Data Cleaner',
                        { body: message }
                    );
                }
            });
        }

        // 控制台打印详细信息
        console.log(t('console.clearFail'), success ? 'Success' : 'Fail', message);

        // 页面顶部临时提示
        const toast = document.createElement('div');
        toast.style.cssText = `
            position: fixed;
            top: 20px;
            left: 50%;
            transform: translateX(-50%);
            padding: 12px 24px;
            border-radius: 4px;
            background: ${success ? '#4CAF50' : '#f44336'};
            color: white;
            font-size: 14px;
            z-index: 999999;
            box-shadow: 0 2px 10px rgba(0,0,0,0.2);
            opacity: 0;
            transition: opacity 0.3s ease;
        `;
        toast.textContent = message;
        document.body.appendChild(toast);

        setTimeout(() => toast.style.opacity = '1', 10);
        setTimeout(() => {
            toast.style.opacity = '0';
            setTimeout(() => document.body.removeChild(toast), 300);
        }, 3000);
    }

    /**
     * 清除所有数据(完整模式)
     */
    async function clearAllData() {
        if (!confirm(t('confirm.clearAll'))) {
            return;
        }

        try {
            const results = [
                clearLocalStorage(),
                clearSessionStorage(),
                clearCookies(),
                await clearIndexedDB(),
                clearWebSQL()
            ];

            const hasFailure = results.some(res => !res);
            if (hasFailure) {
                showResult('clearAllPartial', false);
            } else {
                showResult('clearAllSuccess', true);
                // 可选:清除后刷新页面(取消注释启用)
                // if (confirm(currentLang === 'zh' ? '是否刷新页面使清除生效?' : 'Refresh page to take effect?')) location.reload();
            }
        } catch (e) {
            showResult('error', false);
            console.error(t('console.clearFail'), 'Error', e);
        }
    }

    /**
     * 仅清除基础存储(localStorage + SessionStorage)
     */
    function clearBasicData() {
        if (!confirm(t('confirm.clearBasic'))) {
            return;
        }

        const res1 = clearLocalStorage();
        const res2 = clearSessionStorage();

        if (res1 && res2) {
            showResult('clearBasicSuccess', true);
        } else {
            showResult('error', false);
        }
    }

    /**
     * 仅清除Cookie
     */
    function clearOnlyCookie() {
        if (!confirm(t('confirm.clearCookie'))) {
            return;
        }

        const res = clearCookies();
        if (res) {
            showResult('clearCookieSuccess', true);
        } else {
            showResult('error', false);
        }
    }

    /**
     * 仅清除数据库(IndexedDB + Web SQL)
     */
    async function clearOnlyDB() {
        if (!confirm(t('confirm.clearDB'))) {
            return;
        }

        const res1 = await clearIndexedDB();
        const res2 = clearWebSQL();

        if (res1 && res2) {
            showResult('clearDBSuccess', true);
        } else {
            showResult('clearAllPartial', false);
        }
    }

    /**
     * 反馈功能
     */
    function openFeedback() {
        GM_openInTab('https://github.com/ChiamZhang/WebsiteDataCleaner', {
            active: true,
            insert: true,
            setParent: true
        });
        showResult('feedbackOpen', true);
    }

    // ================= 6. 菜单注册函数 =================
    /**
     * 注册油猴菜单(根据当前语言显示对应名称)
     */
    function registerMenuCommand() {
        // 先注销已存在的菜单,避免重复
        if (menuId.length > 0) {
            menuId.forEach(id => {
                try { GM_unregisterMenuCommand(id); } catch (e) {}
            });
            menuId.length = 0; // 清空数组
        }

        // 循环注册菜单
        for (let i = 0; i < menuAll.length; i++) {
            const [menuKey, menuName, funcName, menuState] = menuAll[i];

            // 从存储中读取菜单状态(如果有)
            menuAll[i][3] = GM_getValue(menuKey, menuState);

            // 绑定对应的功能函数
            const funcMap = {
                clearAllData,
                clearBasicData,
                clearOnlyCookie,
                clearOnlyDB,
                openFeedback
            };
            const targetFunc = funcMap[funcName] || (() => {});

            // 注册菜单
            menuId[i] = GM_registerMenuCommand(menuName, targetFunc);
        }


    }

    /**
     * 菜单开关状态切换(预留功能)
     * @param {boolean} currentState 当前状态
     * @param {string} menuKey 菜单ID
     * @param {string} menuDesc 菜单描述
     */
    function menuSwitch(currentState, menuKey, menuDesc) {
        const newState = !currentState;
        GM_setValue(menuKey, newState);
        showResult(newState ? 'switchEnable' : 'switchDisable', true, [menuDesc]);
        registerMenuCommand(); // 重新注册菜单更新状态显示
    }

    // ================= 7. 初始化 =================
    // 检查通知权限(可选,提升用户体验)
    if (Notification.permission !== 'granted' && Notification.permission !== 'denied') {
        console.log(currentLang === 'zh'
            ? '[数据清除工具] 可在浏览器设置中开启通知权限,获取清除结果提醒'
            : '[Website Data Cleaner] Enable notification permission in browser settings for cleanup result alerts'
        );
    }

    // 注册菜单
    if (menuId.length < 4) {
        registerMenuCommand();
    }

    // 控制台初始化提示
    console.log(t('console.init'));
})();