// ==UserScript== // @name 广告终结者(实验性版本) // @namespace http://tampermonkey.net/ // @version 3.0.4 // @description 更多修复和优化 // @author DeepSeek // @match *://*/* // @license MIT // @grant GM_registerMenuCommand // @grant GM_notification // @grant GM_getValue // @grant GM_setValue // @run-at document-start // ==/UserScript== (function() { 'use strict'; const HOSTNAME = location.hostname.replace(/\./g, '\\.'); const REGEX = { dynamicId: /^(?:[0-9a-f]{16,}|[\dA-F-]{20,})$/i, adAttributes: /(?:ad(?:s|vert|vertisement|box|frame|container|wrap|content|label)?|推广|广告|gg|sponsor|推荐|guanggao|syad|bfad|弹窗|悬浮|葡京|banner|pop(?:up|under)|track(?:ing)?)[_-]?/i, thirdParty: new RegExp(`^(https?:\\/\\/(.*\\.)?${HOSTNAME}(\\/|$)|data:|about:blank)`, 'i'), textAdKeywords: /限时(?:优惠|免费)|立即(?:下载|注册|咨询)|微信(?:号|客服)?[::]?|vx[::]\w+|telegram[::]\w+|免广告|偷拍|黑料|点击下载/ }; const CONFIG = { modules: { main: false, dynamicSystem: false, layoutSystem: false, frameSystem: false, mediaSystem: false, textSystem: false, thirdPartyBlock: false, obfuscatedAdSystem: false, floating: false }, protectionRules: { dynamicIdLength: 30, zIndexThreshold: 100, maxFrameDepth: 4, textAdKeywords: ['限时优惠', '立即下载', '微信', 'vx:', 'telegram', '偷拍', '黑料', '扫码关注', '点击下载'] }, csp: { enabled: false, rules: [ { id: 1, name: '阻止外部脚本加载', rule: "script-src 'self'", enabled: true }, { id: 2, name: '阻止内联脚本执行', rule: "script-src 'unsafe-inline'", enabled: false }, { id: 3, name: '阻止动态脚本执行', rule: "script-src 'unsafe-eval'", enabled: false }, { id: 4, name: '阻止外部样式加载', rule: "style-src 'self'", enabled: false }, { id: 5, name: '阻止内联样式执行', rule: "style-src 'unsafe-inline'", enabled: false }, { id: 6, name: '阻止外部图片加载', rule: "img-src 'self'", enabled: true }, { id: 7, name: '禁止所有框架加载', rule: "frame-src 'none'", enabled: false }, { id: 8, name: '禁止媒体资源加载', rule: "media-src 'none'", enabled: false }, { id: 9, name: '禁止对象嵌入', rule: "object-src 'none'", enabled: false } ] }, mobileOptimizations: { lazyLoadImages: true, removeImagePlaceholders: true }, moduleNames: { dynamicSystem: '动态检测系统', layoutSystem: '布局检测系统', frameSystem: '框架过滤系统', mediaSystem: '媒体检测系统', textSystem: '文本广告检测', thirdPartyBlock: '第三方拦截', obfuscatedAdSystem: '混淆广告检测', floating: '浮动广告检测' }, modulePriority: [ 'thirdPartyBlock', 'frameSystem', 'obfuscatedAdSystem', 'dynamicSystem', 'mediaSystem', 'layoutSystem', 'textSystem', 'floating' ], keywordConfig: { useDefault: true, custom: [], blockThreshold: 2 }, performance: { highRiskModules: ['thirdPartyBlock', 'frameSystem', 'obfuscatedAdSystem'], visibleAreaPriority: true, styleCacheTimeout: 600000, mutationProcessLimit: 40, mutationProcessTimeLimit: 10, idleCallbackTimeout: 2500, throttleScrollDelay: 200, debounceMutationDelay: 300, longTaskThreshold: 500, degradeThreshold: 3 }, obfuscatedAdConfig: { useDefault: true, customKeywords: [], customRegex: [], blockThreshold: 2 } }; const FIRST_SCRIPT_DEFAULT_KEYWORDS = [ '.substr(10)', '.substr(22)', 'htmlAds', 'ads_codes', '{return void 0!==b[a]?b[a]:a}).join("")}', '-${scripts[randomIndex]}', '/image/202${scripts[Math.random()', '"https://"+Date.parse(new Date())+', '"https://"+(new Date().getDate())+', 'new Function(t)()', 'new Function(b)()', 'new Function(\'d\',e)', 'Math.floor(2147483648 * Math.random());', 'Math.floor(Math.random()*url.length)', '&&navigator[', '=navigator;', 'navigator.platform){setTimeout(function', 'disableDebugger', 'sojson.v', '<\\/\'+\'s\'+\'c\'+\'ri\'+\'pt\'+\'>\');', '\');', ]; const FIRST_SCRIPT_REGEX = { obfuscatedAds: { adUrl: /(?:\/[a-z0-9]{12,}\/|(?:adservice|tracking|promo|adsense|doubleclick)\.[a-z]{3,10}\.(?:com|net|org))(?:\/|$)(?:ads?|promo|banner|track|stat|click|log)/i, dynamicCode: /(?:eval|new\s+Function|Function\.constructor|(?:set(?:Timeout|Interval))\s*\(\s*)(?:['"`]|atob|decodeURIComponent|unescape|fromCharCode)/i, base64Data: /(?:data:.*?(?:ad|banner)|(?:[A-Za-z0-9+/]{4}){10,}(?:ad|banner)|data:image\/(?:png|jpeg|gif);base64,|%[0-9A-Fa-f]{2,})/i, inlineScript: /]*>[\s\S]*?(?:ad|banner|popup|promotion|sponsor|[a-zA-Z0-9]{8})[\s\S]*?<\/script>/i, encodedString: /(?:base64|hex|url)\([^)]+\)/i, jsFramework: /(?:jQuery|React|Vue)\.min\.js/i } }; const Logs = { logs: [], maxLogs: 30, add(moduleKey, element, reason) { const moduleName = CONFIG.moduleNames[moduleKey] || '未知模块'; this.logs.push({ module: moduleName, element: `${element.tagName}#${element.id || ''}.${element.className || ''}`, content: this.getContent(element), reason: reason || {}, timestamp: new Date().toISOString() }); if (this.logs.length > this.maxLogs) this.logs.shift(); }, getContent(element) { return element.outerHTML.slice(0, 100) + (element.outerHTML.length > 100 ? '...' : ''); }, clear() { this.logs = []; GM_notification('日志已清空'); } }; const perf = { pendingTasks: [], isProcessing: false, processed: new WeakSet(), adElements: new Set(), styleCache: new Map(), lastStyleCacheClear: Date.now(), longTaskCount: 0, degraded: false }; const AdUtils = { safeRemove(element, moduleKey, reason) { if (!element?.parentNode) return false; try { Logs.add(moduleKey, element, reason); element.style.display = 'none'; element.style.visibility = 'hidden'; element.style.opacity = '0'; element.setAttribute('data-ad-removed', 'true'); this.removeEventListeners(element); return true; } catch (e) { console.warn('元素隐藏失败:', e); return false; } }, removeEventListeners(element) { element.removeAttribute('onload'); element.removeAttribute('onerror'); } }; const StyleManager = { styleElement: null, inject() { if (this.styleElement) return; this.styleElement = document.createElement('style'); this.styleElement.id = 'ad-blocker-styles'; let cssRules = []; cssRules.push(` [data-ad-removed] { display: none !important; visibility: hidden !important; opacity: 0 !important; position: absolute !important; z-index: -9999 !important; width: 0 !important; height: 0 !important; } iframe[src*="ad"], .ad-container { display: none !important; height: 0 !important; width: 0 !important; opacity: 0 !important; } `); this.styleElement.textContent = cssRules.join('\n'); document.head.appendChild(this.styleElement); }, remove() { if (this.styleElement?.parentNode) { this.styleElement.parentNode.removeChild(this.styleElement); this.styleElement = null; } }, toggle(enable) { enable ? this.inject() : this.remove(); } }; const CSPManager = { currentPolicy: null, generatePolicy() { return CONFIG.csp.rules .filter(rule => rule.enabled) .map(rule => rule.rule) .join('; '); }, inject() { this.remove(); if (!CONFIG.csp.enabled) return; const policy = this.generatePolicy(); this.currentPolicy = policy; const meta = document.createElement('meta'); meta.httpEquiv = "Content-Security-Policy"; meta.content = policy; document.head.appendChild(meta); }, remove() { const meta = document.querySelector('meta[http-equiv="Content-Security-Policy"]'); if (meta) meta.remove(); }, toggleRule(ruleId, enable) { const rule = CONFIG.csp.rules.find(r => r.id === ruleId); if (rule) rule.enabled = enable; }, injectInitialCSP() { if (!CONFIG.csp.enabled) return; const initialRules = CONFIG.csp.rules .filter(rule => [1, 6].includes(rule.id) && rule.enabled) .map(rule => rule.rule) .join('; '); if (initialRules) { const meta = document.createElement('meta'); meta.httpEquiv = "Content-Security-Policy"; meta.content = initialRules; document.head.appendChild(meta); } } }; const Detector = { checkModule(moduleKey, element) { if (!CONFIG.modules.main || !CONFIG.modules[moduleKey]) return false; return this[`check${moduleKey.charAt(0).toUpperCase() + moduleKey.slice(1)}`](element); }, checkThirdPartyBlock(el) { if (!CONFIG.modules.thirdPartyBlock) return false; if (['SCRIPT', 'IFRAME'].includes(el.tagName)) { const src = el.src || el.getAttribute('data-src') || ''; if (!REGEX.thirdParty.test(src)) { return AdUtils.safeRemove(el, 'thirdPartyBlock', { type: '第三方资源拦截', detail: `源: ${src.slice(0, 80)}` }); } } return false; }, checkFrameSystem(el) { if (!CONFIG.modules.frameSystem) return false; if (el.tagName === 'IFRAME') { let depth = 0, parent = el; while ((parent = parent.parentElement) && depth <= CONFIG.protectionRules.maxFrameDepth) { if (parent.tagName === 'IFRAME') depth++; } if (depth > CONFIG.protectionRules.maxFrameDepth) { return AdUtils.safeRemove(el, 'frameSystem', { type: '深层嵌套框架', detail: `嵌套层级: ${depth}` }); } } return false; }, checkDynamicSystem(el) { if (!CONFIG.modules.dynamicSystem) return false; const attrs = el.getAttributeNames().map(name => `${name}=${el.getAttribute(name)}` ).join(' '); if (REGEX.adAttributes.test(attrs)) { return AdUtils.safeRemove(el, 'dynamicSystem', { type: '广告属性检测', detail: `属性: ${attrs.slice(0, 100)}` }); } if (el.id && ( el.id.length > CONFIG.protectionRules.dynamicIdLength || REGEX.dynamicId.test(el.id) )) { return AdUtils.safeRemove(el, 'dynamicSystem', { type: '动态ID检测', detail: `异常ID: ${el.id}` }); } return false; }, checkMediaSystem(el) { if (!CONFIG.modules.mediaSystem) return false; if (el.tagName === 'IMG' && (el.src.includes('ad') || el.src.match(/\.(gif|webp)(\?|$)/))) { return AdUtils.safeRemove(el, 'mediaSystem', { type: '图片广告', detail: `图片源: ${el.src.slice(0, 50)}` }); } const style = this.getCachedStyle(el); const rect = el.getBoundingClientRect(); if (['fixed', 'sticky'].includes(style.position)) { const isTopBanner = rect.top < 50; const isBottomBanner = rect.bottom > window.innerHeight - 50; if (isTopBanner || isBottomBanner) { return AdUtils.safeRemove(el, 'mediaSystem', { type: '浮动广告', detail: `位置: ${rect.top}px` }); } } return false; }, checkLayoutSystem(el) { if (!CONFIG.modules.layoutSystem) return false; const style = this.getCachedStyle(el); const zIndex = parseInt(style.zIndex); if (zIndex > CONFIG.protectionRules.zIndexThreshold) { return AdUtils.safeRemove(el, 'layoutSystem', { type: '高堆叠元素', detail: `z-index=${zIndex}` }); } const rect = el.getBoundingClientRect(); const isVisible = rect.width > 0 && rect.height > 0; if (!isVisible && el.children.length > 0) { return AdUtils.safeRemove(el, 'layoutSystem', { type: '隐藏容器元素', detail: '包含子元素的不可见容器' }); } return false; }, checkTextSystem(el) { if (!CONFIG.modules.textSystem) return false; const text = el.textContent?.toLowerCase() || ''; const match = text.match(REGEX.textAdKeywords); if (match) { return AdUtils.safeRemove(el, 'textSystem', { type: '文本广告', detail: `触发规则: ${match}`, keywords: [match] }); } return false; }, checkFloating(el) { if (!CONFIG.modules.floating) return false; const style = this.getCachedStyle(el); if (['fixed', 'sticky'].includes(style.position)) { return AdUtils.safeRemove(el, 'floating', { type: '浮动元素', detail: `定位方式: ${style.position}` }); } return false; }, getCachedStyle(el) { if (Date.now() - perf.lastStyleCacheClear > CONFIG.performance.styleCacheTimeout) { perf.styleCache.clear(); perf.lastStyleCacheClear = Date.now(); } const key = el.nodeName + Array.from(el.classList).join(''); if (!perf.styleCache.has(key)) { try { perf.styleCache.set(key, getComputedStyle(el)); } catch (e) { console.warn("Error getting computed style:", e); return {}; } } return perf.styleCache.get(key); }, isVisible(el) { const rect = el.getBoundingClientRect(); return ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth) ); } }; function throttle(func, limit) { let inThrottle; return function() { const args = arguments; const context = this; if (!inThrottle) { func.apply(context, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } function debounce(func, delay) { let timeout; return function() { const context = this; const args = arguments; clearTimeout(timeout); timeout = setTimeout(() => func.apply(context, args), delay); }; } const Processor = { collectAds() { const elements = [...document.getElementsByTagName('*')]; perf.adElements.clear(); elements.forEach(element => { if (perf.processed.has(element)) return; let modulePriority = CONFIG.modulePriority; if (CONFIG.performance.visibleAreaPriority && Detector.isVisible(element)) { modulePriority = [...CONFIG.performance.highRiskModules, ...modulePriority.filter(m => !CONFIG.performance.highRiskModules.includes(m))]; } for (const moduleKey of modulePriority) { if (moduleKey === 'obfuscatedAdSystem') continue; if (Detector.checkModule(moduleKey, element)) { perf.adElements.add(element); perf.processed.add(element); break; } } }); } }; const observer = new MutationObserver(mutations => { if (!CONFIG.modules.main) return; for (const mutation of mutations) { for (const node of mutation.addedNodes) { if (node.nodeType === 1 && !perf.processed.has(node)) { perf.pendingTasks.push(node); } } } debouncedScheduleProcess(); }); function processElements(deadline) { const start = performance.now(); let processed = 0; while (perf.pendingTasks.length && processed < CONFIG.performance.mutationProcessLimit && deadline.timeRemaining() > 0) { const element = perf.pendingTasks.shift(); if (!perf.processed.has(element)) { let modulePriority = CONFIG.modulePriority; if (CONFIG.performance.visibleAreaPriority && Detector.isVisible(element)) { modulePriority = [...CONFIG.performance.highRiskModules, ...modulePriority.filter(m => !CONFIG.performance.highRiskModules.includes(m))]; } for (const moduleKey of modulePriority) { if (moduleKey === 'obfuscatedAdSystem') continue; if (Detector.checkModule(moduleKey, element)) { perf.adElements.add(element); perf.processed.add(element); break; } } processed++; } } if (perf.pendingTasks.length) { scheduledProcess = requestIdleCallback(processElements, { timeout: CONFIG.performance.idleCallbackTimeout }); } else { perf.isProcessing = false; } const duration = performance.now() - start; if (duration > CONFIG.performance.longTaskThreshold) { perf.longTaskCount++; console.warn("Long task detected:", duration, "ms"); if (perf.longTaskCount > CONFIG.performance.degradeThreshold && !perf.degraded) { perf.degraded = true; console.warn("System degraded: Disabling non-critical modules."); CONFIG.modules.layoutSystem = false; CONFIG.modules.textSystem = false; CONFIG.modules.floating = false; } } else { perf.longTaskCount = Math.max(0, perf.longTaskCount - 1); } } let scheduledProcess = null; function scheduleProcess() { if (!perf.isProcessing && CONFIG.modules.main) { perf.isProcessing = true; scheduledProcess = requestIdleCallback(processElements, { timeout: CONFIG.performance.idleCallbackTimeout }); } } const debouncedScheduleProcess = debounce(scheduleProcess, CONFIG.performance.debounceMutationDelay); const FirstScriptAdUtils = { safeRemove(element, reason) { if (!element?.parentNode || element.dataset.firstScriptAdRemoved) return false; try { Logs.add('obfuscatedAdSystem', element, reason); element.parentNode.removeChild(element); element.dataset.firstScriptAdRemoved = true; return true; } catch (e) { console.warn('元素移除失败:', e); return false; } }, checkKeywords(content) { const keywords = [ ...(CONFIG.obfuscatedAdConfig.useDefault ? FIRST_SCRIPT_DEFAULT_KEYWORDS : []), ...CONFIG.obfuscatedAdConfig.customKeywords ]; return keywords.filter(k => content.includes(k)); }, checkRegex(content) { const regexList = [ ...(CONFIG.obfuscatedAdConfig.useDefault ? Object.values(FIRST_SCRIPT_REGEX.obfuscatedAds) : []), ...CONFIG.obfuscatedAdConfig.customRegex.map(r => new RegExp(r.pattern, r.flags)) ]; return regexList.filter(r => r.test(content)); } }; const FirstScriptObfuscatedAdDetector = { observer: null, init() { this.observer = new MutationObserver(mutations => { mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.nodeType === 1) this.processElement(node); }); }); }); this.observer.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['id', 'class', 'style', 'src'] }); this.scanExistingElements(); }, scanExistingElements() { document.querySelectorAll('script, iframe, div, span, img').forEach(el => { this.processElement(el); }); }, processElement(element) { if (element.dataset.firstScriptProcessed) return; element.dataset.firstScriptProcessed = true; const content = element.innerHTML || element.src || ''; const matchedKeywords = FirstScriptAdUtils.checkKeywords(content); const matchedRegex = FirstScriptAdUtils.checkRegex(content); if (matchedKeywords.length + matchedRegex.length >= CONFIG.obfuscatedAdConfig.blockThreshold) { FirstScriptAdUtils.safeRemove(element, { type: '规则匹配', keywords: matchedKeywords, regex: matchedRegex.map(r => r.toString()) }); } } }; const UIController = { init() { this.loadConfig(); this.registerMainSwitch(); this.registerModuleSwitches(); this.registerLogCommands(); this.registerCSPCommands(); this.registerObfuscatedAdCommands(); this.registerResetCommand(); StyleManager.toggle(CONFIG.modules.main); }, registerMainSwitch() { GM_registerMenuCommand( `🔘 主开关 [${CONFIG.modules.main ? '✅' : '❌'}]`, () => this.toggleMain() ); }, registerModuleSwitches() { Object.entries(CONFIG.moduleNames).forEach(([key, name]) => { GM_registerMenuCommand( `${name} [${CONFIG.modules[key] ? '✅' : '❌'}]`, () => this.toggleModule(key, name) ); }); }, registerLogCommands() { GM_registerMenuCommand('📜 查看拦截日志', () => this.showLogs()); GM_registerMenuCommand('🧹 清空日志', () => Logs.clear()); }, registerCSPCommands() { GM_registerMenuCommand('🛡️ CSP策略管理', () => this.manageCSP()); }, registerObfuscatedAdCommands() { GM_registerMenuCommand('🔍 查看内嵌脚本', () => this.showInlineScripts()); GM_registerMenuCommand('🛠 管理混淆规则', () => this.manageObfuscatedRules()); }, registerResetCommand() { GM_registerMenuCommand('🔄 重置设置', () => this.resetSettings()); }, showInlineScripts() { const scripts = Array.from(document.querySelectorAll('script')) .filter(s => s.innerHTML.trim().length > 0); const displayContent = scripts.map((s, i) => `脚本 ${i+1}:\n${s.innerHTML.slice(0, 200).replace(/[\n\t]/g, '')}...` ).join('\n\n') || '无内嵌脚本'; alert(displayContent); this.manageObfuscatedRules(); }, manageObfuscatedRules() { const menu = [ '1. 添加关键词/正则式', '2. 恢复默认规则', '3. 查看当前规则', '4. 清空所有规则', '5. 查看内嵌脚本' ].join('\n'); const choice = prompt(`混淆广告规则管理(${location.hostname}):\n${menu}`); if (choice === null) return; switch (choice) { case '1': this.addCustomRules(); break; case '2': this.restoreDefaultRules(); break; case '3': this.showCurrentRules(); break; case '4': this.clearAllRules(); break; case '5': this.showInlineScripts(); break; default: alert('无效操作'); } this.manageObfuscatedRules(); }, addCustomRules() { const input = prompt('请输入要添加的内容(支持关键词和正则式,用逗号分隔):\n示例:广告词1,/regex_pattern/i'); if (!input) return; const newItems = input.split(/[,,]/g).map(s => s.trim()); let addedCount = 0; newItems.forEach(item => { if (/^\/.+\/[gimuy]*$/.test(item)) { try { const [_, pattern, flags] = item.match(/\/(.*)\/([gimuy]*)$/); CONFIG.obfuscatedAdConfig.customRegex.push({ pattern, flags }); addedCount++; } catch(e) { alert(`无效正则表达式:${item}`); } } else if (item) { CONFIG.obfuscatedAdConfig.customKeywords.push(item); addedCount++; } }); if (addedCount > 0) { this.saveConfig(); alert(`成功添加 ${addedCount} 条规则`); } }, restoreDefaultRules() { if (confirm('确认恢复默认规则?')) { CONFIG.obfuscatedAdConfig.useDefault = true; CONFIG.obfuscatedAdConfig.customKeywords = []; CONFIG.obfuscatedAdConfig.customRegex = []; this.saveConfig(); alert('已恢复全局默认规则'); } }, showCurrentRules() { const defaultKeywords = CONFIG.obfuscatedAdConfig.useDefault ? FIRST_SCRIPT_DEFAULT_KEYWORDS : []; const customKeywords = CONFIG.obfuscatedAdConfig.customKeywords; const defaultRegex = CONFIG.obfuscatedAdConfig.useDefault ? Object.values(FIRST_SCRIPT_REGEX.obfuscatedAds).map(r => r.toString()) : []; const customRegex = CONFIG.obfuscatedAdConfig.customRegex.map(r => new RegExp(r.pattern, r.flags).toString()); alert([ '默认关键词 (' + (CONFIG.obfuscatedAdConfig.useDefault ? '✅' : '❌') + '):', ...defaultKeywords.map((k, i) => `${i+1}. ${k}`), '\n自定义关键词:', ...customKeywords.map((k, i) => `${i+1}. ${k}`), '\n默认正则式 (' + (CONFIG.obfuscatedAdConfig.useDefault ? '✅' : '❌') + '):', ...defaultRegex.map((r, i) => `${i+1}. ${r}`), '\n自定义正则式:', ...customRegex.map((r, i) => `${i+1}. ${r}`) ].join('\n')); }, clearAllRules() { if (confirm('⚠️ 将清除所有自定义规则并禁用默认规则?')) { CONFIG.obfuscatedAdConfig.useDefault = false; CONFIG.obfuscatedAdConfig.customKeywords = []; CONFIG.obfuscatedAdConfig.customRegex = []; this.saveConfig(); alert('已清空所有规则'); } }, toggleMain() { const newState = !CONFIG.modules.main; Object.keys(CONFIG.modules).forEach(key => { if (key !== 'main') CONFIG.modules[key] = newState; }); CONFIG.modules.main = newState; this.saveConfig(); StyleManager.toggle(newState); GM_notification(`主开关已${newState ? '启用' : '禁用'}`, `所有模块${newState ? '✅ 已激活' : '❌ 已停用'}`); location.reload(); }, toggleModule(key, name) { CONFIG.modules[key] = !CONFIG.modules[key]; CONFIG.modules.main = Object.values(CONFIG.modules) .slice(1).some(v => v); this.saveConfig(); StyleManager.toggle(CONFIG.modules.main); GM_notification(`${name} ${CONFIG.modules[key] ? '✅ 已启用' : '❌ 已禁用'}`); location.reload(); }, showLogs() { const logText = Logs.logs.map(log => { const parts = []; if (log.reason.type) parts.push(`类型: ${log.reason.type}`); if (log.reason.detail) parts.push(`详细: ${log.reason.detail}`); if (log.reason.keywords) parts.push(`关键词: ${log.reason.keywords.join(', ')}`); if (log.reason.regex) parts.push(`正则式: ${log.reason.regex.join(', ')}`); return `${log.module}\n元素: ${log.element}\n内容: ${log.content}\n${parts.join('\n')}`; }).join('\n\n'); alert(`广告拦截日志(最近${Logs.maxLogs}条):\n\n${logText || '暂无日志'}`); }, manageCSP() { const rulesDisplay = CONFIG.csp.rules .map(r => `${r.id}. ${r.name} (${r.enabled ? '✅' : '❌'})`) .join('\n'); let input = prompt( `当前CSP策略状态: ${CONFIG.csp.enabled ? '✅ 已启用' : '❌ 已禁用'}\n\n` + "当前策略规则:\n" + rulesDisplay + "\n\n" + "输入选项:\n1. 开启策略 (输入 'enable')\n2. 关闭策略 (输入 'disable')\n" + "修改规则示例:输入 '1on' 启用规则1\n输入 '23off' 禁用规则2和3\n\n"+ "请输入你的选择:", "" ); if (input === null) return; input = input.trim().toLowerCase(); switch (input) { case 'enable': CONFIG.csp.enabled = true; this.saveConfig(); CSPManager.inject(); alert("CSP策略已启用"); location.reload(); break; case 'disable': CONFIG.csp.enabled = false; this.saveConfig(); CSPManager.remove(); alert("CSP策略已禁用"); location.reload(); break; default: if (/^\d+(?:on|off)$/i.test(input)) { this.modifyCSPRules(input); } else { alert("无效输入"); } } }, modifyCSPRules(actionInput) { const matches = actionInput.match(/^(\d+)(on|off)$/i); if (matches) { const ruleIds = matches.split('').map(Number); const enable = matches.toLowerCase() === 'on'; let updatedRules = []; ruleIds.forEach(id => { const rule = CONFIG.csp.rules.find(r => r.id === id); if (rule) { rule.enabled = enable; updatedRules.push(id); } }); this.saveConfig(); CSPManager.inject(); alert(`规则 ${updatedRules.join(',')} 已更新为 ${enable ? '启用' : '禁用'}`); location.reload(); } else { alert("输入格式错误,请按示例输入如'1on'或'123off'"); } }, resetSettings() { if (confirm("确定要重置所有设置吗?")) { Object.assign(CONFIG, { modules: { main: false, dynamicSystem: false, layoutSystem: false, frameSystem: false, mediaSystem: false, textSystem: false, thirdPartyBlock: false, obfuscatedAdSystem: false, floating: false }, csp: { enabled: false, rules: [ { id: 1, name: '阻止外部脚本加载', rule: "script-src 'self'", enabled: true }, { id: 2, name: '阻止内联脚本执行', rule: "script-src 'unsafe-inline'", enabled: false }, { id: 3, name: '阻止动态脚本执行', rule: "script-src 'unsafe-eval'", enabled: false }, { id: 4, name: '阻止外部样式加载', rule: "style-src 'self'", enabled: false }, { id: 5, name: '阻止内联样式执行', rule: "style-src 'unsafe-inline'", enabled: false }, { id: 6, name: '阻止外部图片加载', rule: "img-src 'self'", enabled: true }, { id: 7, name: '禁止所有框架加载', rule: "frame-src 'none'", enabled: false }, { id: 8, name: '禁止媒体资源加载', rule: "media-src 'none'", enabled: false }, { id: 9, name: '禁止对象嵌入', rule: "object-src 'none'", enabled: false } ] } }); this.saveConfig(); CSPManager.remove(); alert("设置已重置为默认值"); location.reload(); } }, saveConfig() { GM_setValue(`adblockConfig_${location.hostname}`, JSON.stringify({ modules: CONFIG.modules, csp: CONFIG.csp, obfuscatedAdConfig: { ...CONFIG.obfuscatedAdConfig, customRegex: CONFIG.obfuscatedAdConfig.customRegex.map(r => ({ pattern: r.pattern, flags: r.flags })) } })); }, loadConfig() { try { const savedConfig = JSON.parse(GM_getValue(`adblockConfig_${location.hostname}`, '{}')); if (savedConfig.modules) { Object.keys(CONFIG.modules).forEach(key => { if (savedConfig.modules.hasOwnProperty(key)) { CONFIG.modules[key] = savedConfig.modules[key]; } }); } if (savedConfig.csp) { Object.assign(CONFIG.csp, savedConfig.csp); } if (savedConfig.obfuscatedAdConfig) { CONFIG.obfuscatedAdConfig.useDefault = savedConfig.obfuscatedAdConfig.useDefault; CONFIG.obfuscatedAdConfig.customKeywords = savedConfig.obfuscatedAdConfig.customKeywords || []; CONFIG.obfuscatedAdConfig.customRegex = (savedConfig.obfuscatedAdConfig.customRegex || []) .map(r => { try { return new RegExp(r.pattern, r.flags); } catch(e) { return null; } }) .filter(r => r); } } catch (e) { console.error('配置加载失败:', e); } }, }; UIController.loadConfig(); CSPManager.injectInitialCSP(); (function initSystem() { UIController.init(); observer.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['id', 'class', 'style', 'src', 'href'] }); if (CONFIG.modules.obfuscatedAdSystem) FirstScriptObfuscatedAdDetector.init(); if (CONFIG.mobileOptimizations.lazyLoadImages) { document.addEventListener('scroll', throttle(() => { [...document.getElementsByTagName('img')].forEach(img => { if (img.getBoundingClientRect().top < window.innerHeight + 500) { img.src = img.dataset.src || img.src; } }); }, CONFIG.performance.throttleScrollDelay), { passive: true }); } if (CONFIG.mobileOptimizations.removeImagePlaceholders) { [...document.querySelectorAll('img[width="1"], img[height="1"]')].forEach(img => { AdUtils.safeRemove(img, 'mediaSystem', { type: '图片占位符' }); }); } Processor.collectAds(); scheduleProcess(); })(); })();