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