// ==UserScript== // @name 广告终结者 // @namespace http://tampermonkey.net/ // @version 2.8 // @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_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @grant GM_notification // @grant GM_addStyle // @downloadURL none // ==/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', '偷拍', '黑料'] }, defaultSettings: { dynamicSystem: true, layoutSystem: true, frameSystem: true, mediaSystem: true, 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) { console.warn('元素移除失败:', e); return false; } } static handleScriptContent(script) { const content = script.innerHTML.toLowerCase(); const foundKeywords = CONFIG.adKeywords.filter(k => 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]'); } } // ======================= 核心拦截系统 ======================= 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); } } 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; } `); } 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 UIController { static init() { this.registerMainMenu(); this.registerModuleCommands(); 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', '第三方拦截'] ]; modules.forEach(([key, name]) => { GM_registerMenuCommand( `${name} [${Config.get(key) ? '✅' : '❌'}]`, () => this.toggleModule(key, name) ); }); } static registerUtilityCommands() { GM_registerMenuCommand('📜 查看拦截日志', () => this.showLogs()); GM_registerMenuCommand('🧹 清除当前日志', () => Logger.clear()); GM_registerMenuCommand('⚙️ 重置所有配置', () => this.resetConfig()); } 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 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); })(); })();