Greasy Fork镜像 支持简体中文。

广告终结者优化增强版

保留完整功能说明与日志记录,支持模块批量控制

目前為 2025-02-02 提交的版本,檢視 最新版本

// ==UserScript==
// @name         广告终结者优化增强版
// @namespace    http://tampermonkey.net/
// @version      2.2
// @description  保留完整功能说明与日志记录,支持模块批量控制
// @author       TMHhz
// @match        *://*/*
// @license      GPLv3
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_notification
// ==/UserScript==

(function() {
    'use strict';

    // 核心配置
    const CONFIG = {
        maxLogs: 100,
        adKeywords: [
            'ad', 'ads', 'advert', 'banner', 'popup', '推广', '广告', 'gg', 'adv', 
            'guanggao', 'syad', 'bfad', '弹窗', '悬浮', '浮窗', 'fixed', 'sticky'
        ],
        protectionRules: {
            dynamicIdLength: 12,      // 动态ID长度阈值
            zIndexThreshold: 50,      // z-index阈值
            maxFrameDepth: 3,         // 最大iframe嵌套深度
            textAdKeywords: ['限时优惠', '立即下载', '微信', 'vx:', 'telegram']
        },
        defaultSettings: {
            dynamicSystem: true,    // 动态检测系统(合并4个子模块)
            layoutSystem: true,     // 布局检测系统(合并4个子模块)
            frameSystem: true,      // 框架过滤系统(合并3个子模块)
            mediaSystem: true,      // 媒体检测系统(合并2个子模块)
            textSystem: true,       // 文本广告检测
            thirdPartyBlock: 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) { return false; }
        }

        static isWhitelisted(element) {
            return element.closest('[data-protected]'); // 示例白名单选择器
        }
    }

    // ======================= 核心系统 =======================
    class CoreSystem {
        constructor() {
            this.initObservers();
            this.initialClean();
        }

        initObservers() {
            new MutationObserver(mutations => {
                mutations.forEach(m => {
                    m.addedNodes.forEach(n => {
                        if(n.nodeType === 1) this.processElement(n);
                    });
                });
            }).observe(document, {childList: true, subtree: true});
        }

        initialClean() {
            this.checkElements('*', el => this.processElement(el));
            this.checkIframes();
            this.checkThirdParty();
        }

        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);
            }
        }

        // 动态ID检测
        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)}`
                    });
                }
            });
        }

        // z-index检测
        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`
                });
            }
        }

        // 框架检测
        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 {}
            });
        }

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

    // ======================= 配置系统 =======================
    class Config {
        static get allKeys() {
            return Object.keys(CONFIG.defaultSettings);
        }

        static get currentDomain() {
            return location.hostname.replace(/^www\./, '');
        }

        static get(key) {
            const data = GM_getValue('config') || {};
            const domainConfig = data[this.currentDomain] || {};
            const mergedConfig = {...CONFIG.defaultSettings, ...domainConfig};
            return mergedConfig[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.keys(CONFIG.defaultSettings).reduce((acc, key) => {
                acc[key] = status;
                return acc;
            }, {});
            GM_setValue('config', data);
        }
    }

    // ======================= 用户界面 =======================
    class UIController {
        static init() {
            this.registerCommands();
            this.registerMasterSwitch();
        }

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

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

            // 实用功能
            GM_registerMenuCommand('📜 查看拦截日志', () => this.showLogs());
            GM_registerMenuCommand('🧹 清除当前日志', () => Logger.clear());
            GM_registerMenuCommand('🔄 重置当前配置', () => this.resetConfig());
        }

        static registerMasterSwitch() {
            const allEnabled = Config.allKeys.every(k => Config.get(k));
            GM_registerMenuCommand(
                `🔘 一键${allEnabled ? '禁用' : '启用'}所有模块`,
                () => this.toggleAllModules(!allEnabled)
            );
        }

        static toggleModule(key, name) {
            const value = !Config.get(key);
            Config.set(key, value);
            GM_notification({text: `${name} ${value ? '已启用' : '已禁用'}`});
            location.reload();
        }

        static toggleAllModules(status) {
            Config.toggleAll(status);
            GM_notification({text: `所有模块已${status ? '启用' : '禁用'}`});
            location.reload();
        }

        static resetConfig() {
            const data = GM_getValue('config') || {};
            delete data[Config.currentDomain];
            GM_setValue('config', data);
            location.reload();
        }

        static showLogs() {
            const logs = Logger.getLogs();
            alert(logs.length ? 
                `【拦截日志】\n\n${logs.map(l => 
                    `[${l.time}] ${l.module}\n类型: ${l.type}\n详情: ${l.detail}\n元素: ${l.element}`
                ).join('\n\n')}` : 
                '暂无拦截记录'
            );
        }
    }

    // ======================= 日志系统 =======================
    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: `Tag:${data.element.tag} ID:${data.element.id} Class:${data.element.class}`
            });
            GM_setValue('logs', logs.slice(-CONFIG.maxLogs));
        }

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

        static clear() {
            GM_setValue('logs', []);
            GM_notification({text: '日志已清除'});
        }
    }

    // ======================= 初始化 =======================
    new CoreSystem();
    UIController.init();

    // CSS防护规则
    const style = document.createElement('style');
    style.textContent = `
        [style*="fixed"], [style*="sticky"] { 
            position: static !important 
        }
        iframe[src*="ad"] { 
            display: none !important 
        }
    `;
    document.head.appendChild(style);
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址