// ==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 // @downloadURL none // ==/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); })();