Greasy Fork

Greasy Fork is available in English.

广告终结者

广告终结者(尝试优化性能开销)

当前为 2025-02-04 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         广告终结者
// @namespace    http://tampermonkey.net/
// @version      2.7
// @description  广告终结者(尝试优化性能开销)
// @author       TMHhz
// @match        *://*/*
// @exclude     *://*.bing.com/*
// @exclude     *://*.iqiyi.com/*
// @exclude     *://*.qq.com/*
// @exclude     *://*.v.qq.com/*
// @exclude     *://*.sohu.com/*
// @exclude     *://*.mgtv.com/*
// @exclude     *://*.ifeng.com/*
// @exclude     *://*.pptv.com/*
// @exclude     *://*.sina.com.cn/*
// @exclude     *://*.56.com/*
// @exclude     *://*.cntv.cn/*
// @exclude     *://*.tudou.com/*
// @exclude     *://*.baofeng.com/*
// @exclude     *://*.le.com/*
// @exclude     *://*.pps.tv/*
// @exclude     *://*.www.fun.tv/*
// @exclude     *://*.baidu.com/*
// @exclude     *://*.ku6.com/*
// @exclude     *://*.tvsou.com/*
// @exclude     *://*.kankan.com/*
// @exclude     *://*.douyu.com/*
// @exclude     *://*.weibo.com/*
// @exclude     *://*.people.com.cn/*
// @exclude     *://*.cctv.com/*
// @exclude     *://*.gdtv.com.cn/*
// @exclude     *://*.ahtv.cn/*
// @exclude     *://*.tvb.com/*
// @exclude     *://*.tvmao.com/*
// @exclude     *://*.douban.com/*
// @exclude     *://*.163.com/*
// @exclude     *://*.bilibili.com/*
// @exclude     *://*.www.gov.cn/*
// @exclude     *://*.thepaper.cn/*
// @exclude     *://*.xinhuanet.com/*
// @exclude     *://*.china.com/*
// @exclude     *://*.guancha.cn/*
// @exclude     *://*.jianshu.com/*
// @exclude     *://*.amazon.cn/*
// @exclude     *://*.cnblogs.com/*
// @exclude     *://*.cnstock.com/*
// @exclude     *://*.baike.com/*
// @exclude     *://*.guokr.com/*
// @exclude     *://*.360doc.com/*
// @exclude     *://*.qiushibaike.com/*
// @exclude     *://*.zol.com.cn/*
// @exclude     *://*.pconline.com.cn/*
// @exclude     *://*.pcpop.com/*
// @exclude     *://*.it168.com/*
// @exclude     *://*.gfan.com/*
// @exclude     *://*.feng.com/*
// @exclude     *://*.xiaomi.cn/*
// @exclude     *://*.10086.cn/*
// @exclude     *://*.10010.com/*
// @license      GPLv3
// @grant        GM_registerMenuCommand
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_notification
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// ==/UserScript==

(function() {
    'use strict';

    // ======================= 小说网站UA伪装 =======================
    (function detectNovelSite() {
        const novelKeywords = [
            'novel', 'xiaoshuo', '小说', '阅读', 
            'book', '章节', '文学', '小说网',
            'txt', 'download', '免费小说'
        ];
        
        const isNovelSite = () => {
            const urlCheck = novelKeywords.some(k => 
                window.location.href.toLowerCase().includes(k)
            );
            const titleCheck = novelKeywords.some(k => 
                document.title.toLowerCase().includes(k)
            );
            const metaKeywords = document.querySelector('meta[name="keywords"]')?.content || '';
            const metaDescription = document.querySelector('meta[name="description"]')?.content || '';
            const contentCheck = novelKeywords.some(k => 
                metaKeywords.includes(k) || metaDescription.includes(k)
            );
            return urlCheck || titleCheck || contentCheck;
        };

        if (isNovelSite()) {
            const symbianUA = 'NokiaN8-00/5.0 (Symbian/3; Series60/5.2 Mozilla/5.0; Profile/MIDP-2.1 Configuration/CLDC-1.1) AppleWebKit/533.4 (KHTML, like Gecko) BrowserNG/7.3.1.37';
            Object.defineProperty(window.navigator, 'userAgent', {
                value: symbianUA,
                writable: false,
                configurable: false
            });
        }
    })();

    // ======================= 核心配置 =======================
    const CONFIG = {
        maxLogs: 150,
        adKeywords: [
            'ad', 'ads', 'advert', 'banner', 'popup', '推广', '广告', 'gg', 
            'advertisement', 'sponsor', '推荐', 'adv', 'guanggao', 'syad', 
            'bfad', '男男', '女女', '弹窗', '悬浮', '浮动', '浮窗', '葡京', 
            'pop', 'sticky', 'fixed', 'tip', 'tips', 'adbox', 'adsense', 
            'adserver', 'advertmarket', 'advertising', 'cookie-sync', 
            '偷拍', '黑料', '横幅', '乱伦'
        ],
        protectionRules: {
            dynamicIdLength: 12,
            zIndexThreshold: 50,
            maxFrameDepth: 3,
            textAdKeywords: ['限时优惠', '立即下载', '微信', 'vx:', 'telegram', '偷拍', '黑料']
        },
        contentFilter: {
            scanDepth: 3,
            minLength: 2,
            maxKeywords: 50,
            timeout: 300
        },
        defaultSettings: {
            dynamicSystem: true,
            layoutSystem: true,
            frameSystem: true,
            mediaSystem: true,
            textSystem: true,
            thirdPartyBlock: true,
            contentFilter: true
        }
    };

    // ======================= 广告工具类 =======================
    class AdUtils {
        static safeRemove(node, module, reason) {
            if (!node?.parentNode || this.isWhitelisted(node)) return false;
            try {
                Logger.logRemoval({
                    module,
                    element: {
                        tag: node.tagName,
                        id: node.id,
                        class: node.className,
                        html: node.outerHTML?.slice(0, 200)
                    },
                    reason
                });
                node.parentNode.removeChild(node);
                return true;
            } catch(e) {
                console.warn('元素移除失败:', e);
                return false;
            }
        }

        static handleScriptContent(script) {
            const content = script.innerHTML.toLowerCase();
            const blacklist = KeywordManager.getBlacklist();
            
            const foundKeywords = blacklist.filter(k => {
                return content.includes(k.toLowerCase().trim());
            });

            if (foundKeywords.length > 0) {
                Logger.logRemoval({
                    module: 'ScriptFilter',
                    element: {
                        tag: script.tagName,
                        id: script.id,
                        class: script.className,
                        html: script.outerHTML.slice(0, 200)
                    },
                    reason: {
                        type: '脚本关键词拦截',
                        detail: `匹配关键词: ${foundKeywords.join(', ')}`
                    }
                });
                script.remove();
                return true;
            }
            return false;
        }

        static isWhitelisted(element) {
            return element.closest('[data-protected]') || this.hasWhitelistContent(element);
        }

        static shouldBlockByContent(element) {
            if (!Config.get('contentFilter')) return false;
            const text = this.getCleanText(element);
            const blacklist = KeywordManager.getBlacklist();
            const whitelist = KeywordManager.getWhitelist();
            return !whitelist.some(k => text.includes(k)) && 
                   blacklist.some(k => text.includes(k));
        }

        static getCleanText(element) {
            return this.normalizeText(this.extractText(element).trim());
        }

        static extractText(element, depth = 0) {
            if (depth > CONFIG.contentFilter.scanDepth) return '';
            return Array.from(element.childNodes).map(n => {
                if (n.nodeType === Node.TEXT_NODE) return n.textContent;
                if (n.nodeType === Node.ELEMENT_NODE) return this.extractText(n, depth + 1);
                return '';
            }).join(' ');
        }

        static normalizeText(text) {
            return text
                .replace(/\s+/g, ' ')
                .replace(/[【】《》「」“”‘’]/g, '')
                .toLowerCase();
        }

        static hasWhitelistContent(element) {
            const text = this.normalizeText(element.textContent);
            return KeywordManager.getWhitelist().some(k => text.includes(k));
        }
    }

    // ======================= 核心拦截系统 =======================
    class CoreSystem {
        constructor() {
            this.observerConfig = {
                childList: true, 
                subtree: true,
                attributeFilter: ['id', 'class', 'style', 'src']
            };
            this.processedElements = new WeakSet();
            this.initObservers();
            this.initialClean();
            this.injectProtectionStyles();
        }

        debounce(func, wait) {
            let timeout;
            return function(...args) {
                clearTimeout(timeout);
                timeout = setTimeout(() => func.apply(this, args), wait);
            };
        }

        initObservers() {
            const processMutations = this.debounce(this.handleMutations.bind(this), 100);
            new MutationObserver(mutations => processMutations(mutations))
                .observe(document, this.observerConfig);
        }

        handleMutations(mutations) {
            const elements = new Set();
            for (const mutation of mutations) {
                for (const node of mutation.addedNodes) {
                    if (node.nodeType === 1 && !this.processedElements.has(node)) {
                        elements.add(node);
                        this.processedElements.add(node);
                    }
                }
            }
            this.batchProcessElements([...elements]);
        }

        batchProcessElements(elements) {
            if (!elements.length) return;
            
            const BATCH_SIZE = 25;
            for (let i = 0; i < elements.length; i += BATCH_SIZE) {
                const batch = elements.slice(i, i + BATCH_SIZE);
                requestIdleCallback(() => {
                    batch.forEach(el => this.processElement(el));
                }, { timeout: 500 });
            }
        }

        initialClean() {
            this.checkPriorityElements(['script', 'iframe', 'img', 'div']);
            requestIdleCallback(() => this.checkElements('*'), { timeout: 1000 });
        }

        checkPriorityElements(selectors) {
            selectors.forEach(selector => {
                const elements = document.querySelectorAll(selector);
                this.batchProcessElements([...elements]);
            });
        }

        processElement(el) {
            if(Config.get('dynamicSystem')) {
                this.checkDynamicId(el);
                this.checkAdAttributes(el);
            }
            if(Config.get('layoutSystem')) {
                this.checkZIndex(el);
                this.checkFixedPosition(el);
            }
            if(Config.get('mediaSystem')) {
                this.checkImageAds(el);
                this.checkFloatingAds(el);
            }
            if(Config.get('textSystem')) {
                this.checkTextAds(el);
            }
        }

        scanForContent(element) {
            if (element.tagName === 'SCRIPT') {
                if (AdUtils.handleScriptContent(element)) return;
            }

            if (AdUtils.shouldBlockByContent(element)) {
                AdUtils.safeRemove(element, 'ContentFilter', {
                    type: '内容关键词匹配',
                    detail: '黑名单内容触发'
                });
            }

            Array.from(element.children).forEach(child => this.scanForContent(child));
        }

        checkDynamicId(el) {
            const id = el.id || '';
            if(id.length > CONFIG.protectionRules.dynamicIdLength || /\d{5}/.test(id)) {
                AdUtils.safeRemove(el, 'DynamicSystem', {
                    type: '动态ID检测',
                    detail: `异常ID: ${id.slice(0, 20)}`
                });
            }
        }

        checkAdAttributes(el) {
            ['id', 'class', 'src'].forEach(attr => {
                const val = el.getAttribute(attr) || '';
                if(CONFIG.adKeywords.some(k => val.includes(k))) {
                    AdUtils.safeRemove(el, 'DynamicSystem', {
                        type: '广告属性检测',
                        detail: `${attr}=${val.slice(0, 30)}`
                    });
                }
            });
        }

        checkZIndex(el) {
            const zIndex = parseInt(getComputedStyle(el).zIndex);
            if(zIndex > CONFIG.protectionRules.zIndexThreshold) {
                AdUtils.safeRemove(el, 'LayoutSystem', {
                    type: '高堆叠元素',
                    detail: `z-index=${zIndex}`
                });
            }
        }

        checkFixedPosition(el) {
            const style = getComputedStyle(el);
            if(style.position === 'fixed' && el.offsetWidth < 200) {
                AdUtils.safeRemove(el, 'LayoutSystem', {
                    type: '固定定位元素',
                    detail: `尺寸: ${el.offsetWidth}x${el.offsetHeight}`
                });
            }
        }

        checkImageAds(el) {
            if(el.tagName === 'IMG' && (el.src.includes('ad') || el.src.endsWith('.gif'))) {
                AdUtils.safeRemove(el, 'MediaSystem', {
                    type: '图片广告',
                    detail: `图片源: ${el.src.slice(0, 50)}`
                });
            }
        }

        checkFloatingAds(el) {
            const rect = el.getBoundingClientRect();
            const style = getComputedStyle(el);
            if(['fixed', 'sticky'].includes(style.position) && 
              (rect.top < 10 || rect.bottom > window.innerHeight - 10)) {
                AdUtils.safeRemove(el, 'MediaSystem', {
                    type: '浮动广告',
                    detail: `位置: ${rect.top}px`
                });
            }
        }

        checkTextAds(el) {
            const text = el.textContent?.toLowerCase() || '';
            if (CONFIG.protectionRules.textAdKeywords.some(k => text.includes(k))) {
                AdUtils.safeRemove(el, 'TextSystem', {
                    type: '文本广告',
                    detail: `关键词: ${text.slice(0, 50)}`
                });
            }
        }

        checkIframes() {
            if(!Config.get('frameSystem')) return;
            document.querySelectorAll('iframe').forEach(iframe => {
                let depth = 0, parent = iframe;
                while((parent = parent.parentNode)) {
                    if(parent.tagName === 'IFRAME') depth++;
                }
                if(depth > CONFIG.protectionRules.maxFrameDepth) {
                    AdUtils.safeRemove(iframe, 'FrameSystem', {
                        type: '深层嵌套框架',
                        detail: `嵌套层级: ${depth}`
                    });
                }

                const container = iframe.closest('div, section');
                if(container && !AdUtils.isWhitelisted(container)) {
                    AdUtils.safeRemove(container, 'FrameSystem', {
                        type: '广告容器',
                        detail: 'iframe父容器'
                    });
                }
            });
        }

        checkThirdParty() {
            if(!Config.get('thirdPartyBlock')) return;
            document.querySelectorAll('script, iframe').forEach(el => {
                try {
                    const src = new URL(el.src).hostname;
                    const current = new URL(location.href).hostname;
                    if(!src.endsWith(current)) {
                        AdUtils.safeRemove(el, 'ThirdParty', {
                            type: '第三方资源',
                            detail: `源域: ${src}`
                        });
                    }
                } catch {}
            });
        }

        injectProtectionStyles() {
            GM_addStyle(`
                [style*="fixed"], [style*="sticky"] { 
                    position: static !important;
                    top: auto !important;
                    bottom: auto !important;
                }
                iframe[src*="ad"], .ad-container { 
                    display: none !important;
                    height: 0 !important;
                    width: 0 !important;
                    opacity: 0 !important;
                }
                .ad-shield-protected {
                    border: 2px solid #4CAF50 !important;
                    padding: 5px !important;
                }
                pre {
                    white-space: pre-wrap;
                    word-wrap: break-word;
                    font-family: 'Courier New', monospace;
                    font-size: 12px;
                    background: #1e1e1e;
                    padding: 15px;
                    border-radius: 5px;
                    margin: 10px 0;
                    color: #d4d4d4;
                }
                .keyword-highlight {
                    background: #4a4a4a;
                    padding: 2px 4px;
                    border-radius: 3px;
                }
            `);
        }

        checkElements(selector, fn) {
            document.querySelectorAll(selector).forEach(fn);
        }
    }

    // ======================= 配置管理系统 =======================
    class Config {
        static get currentDomain() {
            return location.hostname.replace(/^www\./, '');
        }

        static get allKeys() {
            return Object.keys(CONFIG.defaultSettings);
        }

        static get(key) {
            const data = GM_getValue('config') || {};
            const domainConfig = data[this.currentDomain] || {};
            return domainConfig[key] ?? CONFIG.defaultSettings[key];
        }

        static set(key, value) {
            const data = GM_getValue('config') || {};
            data[this.currentDomain] = {...CONFIG.defaultSettings, ...data[this.currentDomain], [key]: value};
            GM_setValue('config', data);
        }

        static toggleAll(status) {
            const data = GM_getValue('config') || {};
            data[this.currentDomain] = Object.fromEntries(
                Config.allKeys.map(k => [k, status])
            );
            GM_setValue('config', data);
        }
    }

    // ======================= 关键词管理系统 =======================
    class KeywordManager {
        static getStorageKey(type) {
            return `content_${type}_${Config.currentDomain}`;
        }

        static getBlacklist() {
            return this.getKeywords('blacklist');
        }

        static getWhitelist() {
            return this.getKeywords('whitelist');
        }

        static getKeywords(type) {
            const raw = GM_getValue(this.getStorageKey(type), '');
            return this.parseKeywords(raw);
        }

        static parseKeywords(raw) {
            return raw.split(',')
                .map(k => k.trim())
                .filter(k => k.length >= CONFIG.contentFilter.minLength)
                .slice(0, CONFIG.contentFilter.maxKeywords)
                .map(k => k.toLowerCase());
        }

        static updateKeywords(type, keywords) {
            const valid = [...new Set(keywords)]
                .map(k => k.trim())
                .filter(k => k.length >= CONFIG.contentFilter.minLength)
                .slice(0, CONFIG.contentFilter.maxKeywords);
            GM_setValue(this.getStorageKey(type), valid.join(','));
        }

        static updateScriptKeywords(keywords) {
            GM_setValue('script_keywords', JSON.stringify(keywords));
        }

        static getScriptKeywords() {
            return JSON.parse(GM_getValue('script_keywords', '[]'));
        }
    }

    // ======================= 用户界面控制器 =======================
    class UIController {
        static init() {
            this.registerMainMenu();
            this.registerModuleCommands();
            this.registerContentMenu();
            this.registerUtilityCommands();
        }

        static registerMainMenu() {
            const allEnabled = Config.allKeys.every(k => Config.get(k));
            GM_registerMenuCommand(
                `🔘 主开关 [${allEnabled ? '✅' : '❌'}]`,
                () => this.toggleAllModules(!allEnabled)
            );
        }

        static registerModuleCommands() {
            const modules = [
                ['dynamicSystem', '动态检测系统 (ID/属性)'],
                ['layoutSystem', '布局检测系统 (定位/z-index)'],
                ['frameSystem', '框架过滤系统 (iframe)'],
                ['mediaSystem', '媒体检测系统 (图片/浮动)'],
                ['textSystem', '文本广告检测'],
                ['thirdPartyBlock', '第三方拦截'],
                ['contentFilter', '内容过滤系统']
            ];

            modules.forEach(([key, name]) => {
                GM_registerMenuCommand(
                    `${name} [${Config.get(key) ? '✅' : '❌'}]`,
                    () => this.toggleModule(key, name)
                );
            });
        }

        static registerContentMenu() {
            GM_registerMenuCommand('🔠 内容过滤管理', () => {
                GM_registerMenuCommand('➕ 添加黑名单关键词', () => 
                    this.handleAddKeyword('blacklist'));
                GM_registerMenuCommand('➕ 添加白名单关键词', () => 
                    this.handleAddKeyword('whitelist'));
                GM_registerMenuCommand('📋 显示当前关键词', () => 
                    this.showCurrentKeywords());
                GM_registerMenuCommand('🗑️ 清除所有关键词', () => 
                    this.clearKeywords());
            });
        }

        static registerUtilityCommands() {
            GM_registerMenuCommand('📜 查看拦截日志', () => this.showLogs());
            GM_registerMenuCommand('🧹 清除当前日志', () => Logger.clear());
            GM_registerMenuCommand('⚙️ 重置所有配置', () => this.resetConfig());
            GM_registerMenuCommand('📜 查看内嵌脚本', () => this.showInlineScripts());
        }

        static showInlineScripts() {
            const scripts = Array.from(document.querySelectorAll('script'))
                .filter(script => script.innerHTML.trim().length > 0);
            
            if (scripts.length === 0) {
                alert('当前网页没有内嵌脚本');
                return;
            }

            let message = `📜 发现 ${scripts.length} 个内嵌脚本:\n\n`;
            scripts.forEach((script, index) => {
                const content = script.innerHTML
                    .replace(/[\r\n]+/g, ' ')    // 替换换行为空格
                    .replace(/\s{2,}/g, ' ')     // 压缩多个空格
                    .trim()
                    .slice(0, 300);              // 截取前300字符
                
                message += `▦ 脚本 ${index + 1} ▦\n${content}\n\n`;
            });

            alert(message);
        }

        static enhanceContentFilter(keyword) {
            let scriptRemoved = false;
            
            // 处理脚本内容
            document.querySelectorAll('script').forEach(script => {
                if (script.innerHTML.includes(keyword)) {
                    script.remove();
                    Logger.logRemoval({
                        module: 'ContentFilter',
                        element: {
                            tag: script.tagName,
                            id: script.id,
                            class: script.className,
                            html: script.outerHTML.slice(0, 200)
                        },
                        reason: {
                            type: '立即拦截脚本',
                            detail: `关键词: ${keyword}`
                        }
                    });
                    scriptRemoved = true;
                }
            });

            // 重新扫描所有元素(优化性能版)
            const scanElements = () => {
                const walker = document.createTreeWalker(
                    document.documentElement,
                    NodeFilter.SHOW_ELEMENT
                );

                while (walker.nextNode()) {
                    const node = walker.currentNode;
                    if (AdUtils.shouldBlockByContent(node)) {
                        AdUtils.safeRemove(node, 'ContentFilter', {
                            type: '立即拦截元素',
                            detail: `关键词: ${keyword}`
                        });
                    }
                }
            };

            // 分批次执行防止卡顿
            setTimeout(scanElements, 0);
            setTimeout(scanElements, 300);
            setTimeout(scanElements, 600);
        }

        static toggleModule(key, name) {
            const value = !Config.get(key);
            Config.set(key, value);
            this.showNotification(`${name} ${value ? '✅ 已启用' : '❌ 已禁用'}`);
            setTimeout(() => location.reload(), 500);
        }

        static toggleAllModules(status) {
            Config.toggleAll(status);
            this.showNotification(`所有模块已${status ? '启用' : '禁用'}`);
            setTimeout(() => location.reload(), 500);
        }

        static handleAddKeyword(type) {
            const promptText = type === 'blacklist' 
                ? '输入要屏蔽的关键词(支持中文):' 
                : '输入要豁免的关键词:';
            const input = prompt(promptText);
            if (!input) return;

            const current = KeywordManager.getKeywords(type);
            KeywordManager.updateKeywords(type, [...current, input]);
            this.showNotification(`已添加${type === 'blacklist' ? '黑' : '白'}名单关键词:${input}`);

            if (type === 'blacklist') {
                this.enhanceContentFilter(input);
            }
        }

        static enhanceContentFilter(keyword) {
            let scriptRemoved = false;
            document.querySelectorAll('script').forEach(script => {
                if (script.innerHTML.includes(keyword)) {
                    script.remove();
                    Logger.logRemoval({
                        module: 'ContentFilter',
                        element: {
                            tag: script.tagName,
                            id: script.id,
                            class: script.className,
                            html: script.outerHTML.slice(0, 200)
                        },
                        reason: {
                            type: '立即拦截脚本',
                            detail: `关键词: ${keyword}`
                        }
                    });
                    scriptRemoved = true;
                }
            });

            if (!scriptRemoved) {
                document.querySelectorAll('*').forEach(element => {
                    const text = AdUtils.getCleanText(element);
                    if (text.includes(keyword)) {
                        AdUtils.safeRemove(element, 'ContentFilter', {
                            type: '立即拦截元素',
                            detail: `关键词: ${keyword}`
                        });
                    }
                });
            }
        }

        static showCurrentKeywords() {
            const black = KeywordManager.getBlacklist();
            const white = KeywordManager.getWhitelist();
            
            alert(`【当前内容过滤规则 - ${location.hostname}】
            
■ 黑名单 (${black.length}个):
${black.join(', ') || '无'}

■ 白名单 (${white.length}个):
${white.join(', ') || '无'}`);
        }

        static clearKeywords() {
            if (!confirm('确定清除所有关键词吗?')) return;
            ['blacklist', 'whitelist'].forEach(type => {
                GM_setValue(KeywordManager.getStorageKey(type), '');
            });
            this.showNotification('已清除所有关键词');
        }

        static resetConfig() {
            if (!confirm('确定重置所有配置吗?')) return;
            const data = GM_getValue('config') || {};
            delete data[Config.currentDomain];
            GM_setValue('config', data);
            this.showNotification('配置已重置');
            setTimeout(() => location.reload(), 500);
        }

        static showLogs() {
            const logs = Logger.getLogs();
            alert(logs.length ? 
                `📃 最近${CONFIG.maxLogs}条拦截记录:\n\n${logs.map(l => 
                    `[${l.time}] ${l.module}\n类型: ${l.type}\n元素: ${l.element}`
                ).join('\n\n')}` : 
                '暂无拦截记录'
            );
        }

        static showNotification(text, duration = 2000) {
            GM_notification({
                title: '广告终结者',
                text: text,
                silent: true,
                timeout: duration
            });
        }
    }

    // ======================= 日志系统 =======================
    class Logger {
        static logRemoval(data) {
            const logs = GM_getValue('logs', []);
            logs.push({
                time: new Date().toLocaleTimeString(),
                module: data.module,
                type: data.reason.type,
                detail: data.reason.detail,
                element: `${data.element.tag}#${data.element.id}`
            });
            GM_setValue('logs', logs.slice(-CONFIG.maxLogs));
        }

        static getLogs() {
            return GM_getValue('logs', []);
        }

        static clear() {
            GM_setValue('logs', []);
            UIController.showNotification('日志已清空');
        }
    }

    // ======================= 初始化入口 =======================
    (function init() {
        new CoreSystem();
        UIController.init();
        console.log('✅ 广告拦截系统已激活 - 当前域名:', location.hostname);
    })();
})();