您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
[完美整合] 动态检测、框架过滤、堆叠拦截、第三方拦截四大系统
当前为
// ==UserScript== // @name 广告终结者 v1.1 // @namespace http://tampermonkey.net/ // @version 1.1 // @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: 50, adKeywords: ['ad', 'ads', 'advert', 'banner', 'popup', '推广', '广告', 'gg', 'advertisement', 'sponsor', '推荐', 'adv', 'guanggao', 'syad', 'bfad'], checkAttributes: ['id', 'class', 'src', 'href', 'data-ad', 'name'], protectionRules: { minWidth: 50, minHeight: 20, iframeSizeThreshold: 2, zIndexThreshold: 50, sizeThresholdRatio: 0.15, similarityThreshold: 0.8, dynamicIdLength: 10, dynamicIdDigits: 5 }, whitelist: { scriptNamespaces: ['pswMgrDialog', 'userscript-'], protectedAttributes: [ {name: 'id', values: ['pswMgrDialog', 'userscript-quickFill']}, {name: 'class', values: ['userscript-quickFill']} ], selectors: ['#pswMgrDialog', '.userscript-quickFill', '.userscript-pswmgrDlg'], thirdparty: [] }, defaultSettings: { // 动态检测系统 dynamicIdDetection: true, attributeSimilarity: true, sizeAnomaly: true, adAttributeDetection: true, // 框架过滤系统 iframeAttributeCheck: true, parentContainerCheck: true, // 堆叠拦截系统 highZIndexDetection: true, fixedPositionCheck: true, overlayDetection: true, // 新增第三方拦截 thirdpartyCheck: true } }; // ======================= 工具类 ======================= class AdUtils { static safeRemove(node, module, reason) { if (!node || !node.parentNode || this.isWhitelisted(node)) { console.debug('[拦截跳过] 白名单元素:', node); return false; } try { Logger.logRemoval({ module, element: { tag: node.tagName, id: node.id, class: node.className, html: node.outerHTML.slice(0, 500) }, reason }); node.parentNode.removeChild(node); this.cleanAncestors(node.parentNode); console.log('[成功移除]', module, node); return true; } catch (e) { console.error('元素移除失败:', e); return false; } } static cleanAncestors(node) { let current = node; while (current && current !== document.documentElement) { if (current.children.length === 0 && !current.hasAttribute('data-keep-empty')) { const parent = current.parentNode; parent?.removeChild(current); current = parent; } else { break; } } } static isWhitelisted(element) { let currentElement = element; while (currentElement && currentElement !== document.documentElement) { const src = currentElement.getAttribute('src'); if (src && CONFIG.whitelist.thirdparty.includes(src)) return true; if (CONFIG.whitelist.selectors.some(s => currentElement.matches(s))) return true; if (CONFIG.whitelist.protectedAttributes.some(attr => { const attrValue = currentElement.getAttribute(attr.name); return attrValue && attr.values.some(v => attr.name === 'class' ? currentElement.classList.contains(v) : attrValue.startsWith(v) ); })) return true; if (CONFIG.whitelist.scriptNamespaces.some(ns => { const id = currentElement.id || ''; const className = currentElement.className || ''; return id.startsWith(ns) || className.startsWith(ns); })) return true; currentElement = currentElement.parentElement; } return false; } static calculateSimilarity(a, b) { const setA = new Set(a.split('')); const setB = new Set(b.split('')); const intersection = new Set([...setA].filter(x => setB.has(x))); return intersection.size / Math.max(setA.size, setB.size); } } // ======================= 第三方拦截模块 ======================= class ThirdpartyInterceptor { constructor() { this.baseHost = this.getCurrentBaseHost(); this.initObserver(); } getCurrentBaseHost() { const host = location.hostname; return this.isIP(host) ? host : this.getBaseDomain(host); } isIP(host) { return /^(?:\d{1,3}\.){3}\d{1,3}$/.test(host); } getBaseDomain(url) { try { const parsed = new URL(url.startsWith('http') ? url : `http://${url}`); const parts = parsed.hostname.split('.'); if (parts.length <= 1) return parsed.hostname; return parts.slice(-2).join('.'); } catch { return this.baseHost; } } isThirdparty(src) { if (!src || CONFIG.whitelist.thirdparty.includes(src)) return false; try { return this.getBaseDomain(src) !== this.baseHost; } catch { return false; } } processNode(node) { const tag = node.tagName.toUpperCase(); if (['SCRIPT', 'IFRAME'].includes(tag)) { const src = node.getAttribute('src'); if (src && this.isThirdparty(src)) { AdUtils.safeRemove(node, 'thirdparty', { type: '第三方资源', detail: `拦截源: ${this.getBaseDomain(src)}` }); } } } initObserver() { const observer = new MutationObserver(mutations => { mutations.forEach(m => { m.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE) { this.processNode(node); node.querySelectorAll('script, iframe').forEach(n => this.processNode(n)); } }); }); }); observer.observe(document, { childList: true, subtree: true }); // 初始扫描 document.querySelectorAll('script, iframe').forEach(n => this.processNode(n)); } } // ======================= 核心拦截系统 ======================= class CoreCleaner { constructor() { if (DomainConfig.getConfig('thirdpartyCheck')) { this.thirdpartyInterceptor = new ThirdpartyInterceptor(); } if (this.isAnyModuleEnabled()) { this.initEnhancedObserver(); this.initialCleanup(); } } isAnyModuleEnabled() { return Object.keys(CONFIG.defaultSettings).some( key => DomainConfig.getConfig(key) ); } initEnhancedObserver() { this.observer = new MutationObserver(mutations => { mutations.forEach(m => { m.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE) { // Shadow DOM支持 if (node.shadowRoot) { this.observer.observe(node.shadowRoot, { childList: true, subtree: true }); } this.checkGenericElements(node); if (node.tagName === 'IFRAME') this.checkIframes(node); } }); }); }); this.observer.observe(document, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class', 'id', 'src', 'href'] }); } initialCleanup() { if (!this.isAnyModuleEnabled()) return; this.runDetectionSystems(); this.checkElements('iframe', this.checkIframes.bind(this)); this.checkElements('*', this.checkGenericElements.bind(this)); } runDetectionSystems() { if (DomainConfig.getConfig('highZIndexDetection')) this.checkHighZIndex(); if (DomainConfig.getConfig('fixedPositionCheck')) this.checkFixedPosition(); if (DomainConfig.getConfig('overlayDetection')) this.checkOverlay(); } checkElements(selector, checker) { if (!this.isAnyModuleEnabled()) return; document.querySelectorAll(selector).forEach(node => checker(node)); } // ========== 框架过滤系统 ========== checkIframes(iframe) { if (DomainConfig.getConfig('iframeAttributeCheck')) { this.checkIframeAttributes(iframe); } if (DomainConfig.getConfig('parentContainerCheck')) { this.checkParentContainers(iframe); } } checkParentContainers(iframe) { const parents = ['div', 'section', 'article'] .map(s => iframe.closest(s)) .filter(Boolean); parents.forEach(parent => { if (!AdUtils.isWhitelisted(parent)) { AdUtils.safeRemove(parent, 'frame-parent', { type: '父容器过滤', detail: '可疑iframe容器' }); } }); } checkIframeAttributes(iframe) { const hasAdSrc = CONFIG.adKeywords.some(kw => iframe.src.includes(kw)); const isHidden = iframe.offsetWidth < CONFIG.protectionRules.iframeSizeThreshold || iframe.offsetHeight < CONFIG.protectionRules.iframeSizeThreshold; if (isHidden || hasAdSrc) { AdUtils.safeRemove(iframe, 'frame-attr', { type: 'iframe属性', detail: hasAdSrc ? `广告源: ${iframe.src.slice(0, 50)}` : `隐藏iframe: ${iframe.offsetWidth}x${iframe.offsetHeight}` }); } } // ========== 动态检测系统 ========== checkGenericElements(element) { if (DomainConfig.getConfig('dynamicIdDetection')) { this.checkDynamicId(element); } if (DomainConfig.getConfig('attributeSimilarity')) { this.checkAttributeSimilarity(element); } if (DomainConfig.getConfig('sizeAnomaly')) { this.checkSizeAnomaly(element); } if (DomainConfig.getConfig('adAttributeDetection')) { this.checkAdAttributes(element); } } checkDynamicId(element) { const id = element.id || ''; if (id.length > CONFIG.protectionRules.dynamicIdLength || /\d{5,}/.test(id)) { AdUtils.safeRemove(element, 'dynamic-id', { type: '动态ID检测', detail: `ID特征异常: ${id.slice(0, 50)}` }); } } checkAttributeSimilarity(element) { const id = element.id || ''; const className = element.className || ''; if (AdUtils.calculateSimilarity(id, className) > CONFIG.protectionRules.similarityThreshold) { AdUtils.safeRemove(element, 'attr-similarity', { type: '属性相似度', detail: `ID与class相似度过高` }); } } checkSizeAnomaly(element) { const rect = element.getBoundingClientRect(); if (rect.width * rect.height > window.innerWidth * window.innerHeight * CONFIG.protectionRules.sizeThresholdRatio) { AdUtils.safeRemove(element, 'size-anomaly', { type: '尺寸异常', detail: `占用面积 ${Math.round(rect.width*rect.height)}px²` }); } } checkAdAttributes(element) { const attrCheck = CONFIG.checkAttributes.find(attr => { const value = element.getAttribute(attr) || ''; return CONFIG.adKeywords.some(kw => value.includes(kw)); }); if (attrCheck) { AdUtils.safeRemove(element, 'attr-match', { type: '属性匹配', attribute: attrCheck, value: element.getAttribute(attrCheck).slice(0, 100) }); } } // ========== 堆叠拦截系统 ========== checkHighZIndex() { document.querySelectorAll('body *').forEach(el => { const zIndex = parseInt(getComputedStyle(el).zIndex); if (!isNaN(zIndex) && zIndex > CONFIG.protectionRules.zIndexThreshold) { AdUtils.safeRemove(el, 'z-index', { type: '高堆叠', detail: `z-index: ${zIndex}` }); } }); } checkFixedPosition() { document.querySelectorAll('*').forEach(el => { const position = getComputedStyle(el).position; if (position === 'fixed' && !AdUtils.isWhitelisted(el)) { AdUtils.safeRemove(el, 'fixed-pos', { type: '固定定位', detail: 'position: fixed' }); } }); } checkOverlay() { document.querySelectorAll('div, section').forEach(el => { const style = getComputedStyle(el); if (style.backgroundColor !== 'rgba(0, 0, 0, 0)' && parseFloat(style.opacity) > 0.5 && el.offsetWidth >= window.innerWidth * 0.8) { AdUtils.safeRemove(el, 'overlay', { type: '遮罩层', detail: `背景色: ${style.backgroundColor}` }); } }); } destructor() { if (this.observer) this.observer.disconnect(); } } // ======================= 配置系统 ======================= class DomainConfig { static get currentDomain() { return location.hostname.replace(/^www\./, ''); } static getConfig(key) { const allConfigs = GM_getValue('domainConfigs', {}); const domainConfig = allConfigs[this.currentDomain] || {...CONFIG.defaultSettings}; return key in domainConfig ? domainConfig[key] : CONFIG.defaultSettings[key]; } static updateConfig(key, value) { const allConfigs = GM_getValue('domainConfigs', {}); if (!allConfigs[this.currentDomain]) { allConfigs[this.currentDomain] = {...CONFIG.defaultSettings}; } allConfigs[this.currentDomain][key] = value; GM_setValue('domainConfigs', allConfigs); } static resetConfig() { const allConfigs = GM_getValue('domainConfigs', {}); delete allConfigs[this.currentDomain]; GM_setValue('domainConfigs', allConfigs); } } // ======================= 用户界面 ======================= class UIController { static init() { this.registerDomainControls(); this.registerGlobalControls(); this.registerInfoButton(); this.registerMasterSwitch(); } static registerMasterSwitch() { const allEnabled = Object.keys(CONFIG.defaultSettings).every( key => DomainConfig.getConfig(key) ); GM_registerMenuCommand( `🔘 一键${allEnabled ? '禁用' : '启用'}所有模块`, () => this.toggleAllModules() ); } static toggleAllModules() { const allKeys = Object.keys(CONFIG.defaultSettings); const currentState = allKeys.every(key => DomainConfig.getConfig(key)); allKeys.forEach(key => { DomainConfig.updateConfig(key, !currentState); }); GM_notification({ title: '总开关已切换', text: `所有模块已${!currentState ? '启用' : '禁用'}`, timeout: 2000 }); window.location.reload(); } static registerInfoButton() { GM_registerMenuCommand('📘 功能说明', () => this.showInstructions()); } static showInstructions() { const instructions = `【广告终结者功能说明】\n\n ====== 动态检测系统 ======\n 1. 动态ID检测 - 检测长度超过10字符的ID - 包含5个以上连续数字的ID 2. 属性相似度 - 比较ID和class的相似度 - 相似度阈值:80% 3. 尺寸异常检测 - 检测超过视窗15%面积的元素 4. 广告属性检测 - 扫描属性:id/class/src/href/data-ad/name ====== 框架过滤系统 ======\n 5. iframe属性检测 - 检测src包含广告关键词 6. 父容器检测 - 检查iframe父级容器 ====== 堆叠拦截系统 ======\n 7. 高z-index检测 - 阈值:z-index > 50 8. 固定定位检测 - 检测position: fixed元素 9. 遮罩层检测 - 背景色不透明检测 ====== 第三方拦截系统 ======\n 10. 跨域资源拦截 - 自动识别主域名 - 拦截第三方脚本和iframe 【系统配置】 - 各模块独立开关 - 域名级配置存储`; alert(instructions.replace(/ {4,}/g, '')); } static registerDomainControls() { const SEPARATOR = '───────────────'; GM_registerMenuCommand(SEPARATOR, () => {}); this.registerModuleControls([ 'thirdpartyCheck', 'dynamicIdDetection', 'attributeSimilarity', 'sizeAnomaly', 'adAttributeDetection' ]); GM_registerMenuCommand(SEPARATOR, () => {}); this.registerModuleControls([ 'iframeAttributeCheck', 'parentContainerCheck' ]); GM_registerMenuCommand(SEPARATOR, () => {}); this.registerModuleControls([ 'highZIndexDetection', 'fixedPositionCheck', 'overlayDetection' ]); GM_registerMenuCommand(SEPARATOR, () => {}); GM_registerMenuCommand('🔄 重置当前网站设置', () => { DomainConfig.resetConfig(); GM_notification({ title: '配置已重置', text: `${DomainConfig.currentDomain} 设置已恢复默认`, timeout: 2000 }); window.location.reload(); }); } static registerModuleControls(keys) { keys.forEach(key => { const currentValue = DomainConfig.getConfig(key); GM_registerMenuCommand( ` ${this.getKeyName(key)} [${currentValue ? '✅' : '❌'}]`, () => this.toggleConfig(key) ); }); } static registerGlobalControls() { GM_registerMenuCommand('📜 查看拦截日志', this.showLogs.bind(this)); GM_registerMenuCommand('🧹 清除当前日志', this.clearLogs.bind(this)); } static toggleConfig(key) { const current = DomainConfig.getConfig(key); DomainConfig.updateConfig(key, !current); GM_notification({ title: '配置已更新', text: `${this.getKeyName(key)} 已${!current ? '启用' : '禁用'}`, timeout: 1500 }); window.location.reload(); } static getKeyName(key) { const names = { thirdpartyCheck: '第三方拦截', dynamicIdDetection: '动态ID检测', attributeSimilarity: '属性相似度', sizeAnomaly: '尺寸异常', adAttributeDetection: '广告属性检测', iframeAttributeCheck: 'iframe属性', parentContainerCheck: '父容器检测', highZIndexDetection: '高z-index', fixedPositionCheck: '固定定位', overlayDetection: '遮罩层检测' }; return names[key] || key; } static showLogs() { const logs = Logger.getDomainLogs(); alert(logs.length ? `【${DomainConfig.currentDomain}】拦截记录:\n\n${Logger.formatLogs(logs)}` : '当前无拦截记录'); } static clearLogs() { Logger.clearDomainLogs(); GM_notification({title: '日志已清除', timeout: 1500}); } } // ======================= 日志系统 ======================= class Logger { static logRemoval(record) { const allLogs = GM_getValue('removalLogs', {}); const domainLogs = allLogs[DomainConfig.currentDomain] || []; domainLogs.push({ timestamp: new Date().toLocaleString(), ...record }); if (domainLogs.length > CONFIG.maxLogs) domainLogs.shift(); allLogs[DomainConfig.currentDomain] = domainLogs; GM_setValue('removalLogs', allLogs); } static getDomainLogs() { const allLogs = GM_getValue('removalLogs', {}); return allLogs[DomainConfig.currentDomain] || []; } static clearDomainLogs() { const allLogs = GM_getValue('removalLogs', {}); delete allLogs[DomainConfig.currentDomain]; GM_setValue('removalLogs', allLogs); } static formatLogs(logs) { return logs.map((log, index) => `[${index + 1}] ${log.timestamp}\n模块: ${log.module}\n` + `类型: ${log.reason.type}\n详情: ${log.reason.detail || ''}` ).join('\n\n'); } } // ======================= 系统初始化 ======================= let coreCleaner; function initialize() { if (coreCleaner) coreCleaner.destructor(); coreCleaner = new CoreCleaner(); } initialize(); UIController.init(); })();