// ==UserScript==
// @name 广告终结者(实验性版本)
// @namespace http://tampermonkey.net/
// @version 3.0.5
// @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\'+\'>\');', '</\'+\'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: /<script[^>]*>[\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 (el.tagName === 'SCRIPT') {
const src = el.src || el.getAttribute('data-src') || '';
if (!REGEX.thirdParty.test(src)) {
return FirstScriptAdUtils.safeRemove(
el,
'thirdPartyBlock',
{
type: '第三方脚本拦截',
detail: `源: ${src.slice(0, 80)}`
}
);
}
}
else if (el.tagName === 'IFRAME') {
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, moduleKey, reason) {
if (!element?.parentNode || element.dataset.firstScriptAdRemoved) return false;
try {
Logs.add(moduleKey, 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,
'obfuscatedAdSystem',
{
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();
})();
})();