Via Adblock 规则分析
// ==UserScript==
// @name Via Adblock 规则分析
// @namespace https://viayoo.com/
// @version 1.15
// @description 解析Adblock规则,是否值得在Via浏览器上订阅,评分仅供娱乐,自行斟酌。
// @author Grok & Via
// @match *://*
// @license MIT
// @grant GM_registerMenuCommand
// @run-at document-start
// ==/UserScript==
(function() {
'use strict';
console.log('Adblock Rule Analyzer 脚本已加载,URL:', location.href);
// 注册菜单项
GM_registerMenuCommand("分析当前页面规则", analyzeCurrentPage);
GM_registerMenuCommand("分析自定义链接规则", analyzeCustomLink);
// 分析当前页面
async function analyzeCurrentPage() {
console.log('分析当前页面');
let content;
try {
const response = await fetch(location.href);
content = await response.text();
console.log('当前页面内容获取成功,长度:', content.length);
} catch (e) {
alert('无法获取当前页面内容: ' + e.message);
console.error('当前页面内容获取失败:', e);
return;
}
analyzeContent(content, '当前页面');
}
// 分析自定义链接
function analyzeCustomLink() {
console.log('分析自定义链接');
const url = prompt('请输入Adblock规则文件的直链(如 https://raw.githubusercontent.com/...)');
if (!url || !url.trim()) {
alert('未输入有效的链接');
return;
}
fetch(url)
.then(response => {
if (!response.ok) throw new Error('网络请求失败,状态码: ' + response.status);
return response.text();
})
.then(content => {
console.log('自定义链接内容获取成功,长度:', content.length);
analyzeContent(content, url);
})
.catch(e => {
alert('无法获取链接内容: ' + e.message);
console.error('自定义链接内容获取失败:', e);
});
}
// 处理换行符,统一为 \n
function normalizeNewlines(text) {
return text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
}
// 解析头部信息
function parseHeader(content) {
const header = {
title: '未知标题',
description: '未添加任何描述',
version: '未知版本',
lastModified: '未知时间',
expires: '未给出更新周期',
};
const headerLines = content.split('\n')
.filter(line => line.trim().startsWith('!'))
.map(line => line.trim().substring(1).trim());
headerLines.forEach(line => {
if (line.startsWith('Title:')) {
header.title = line.substring(6).trim();
} else if (line.startsWith('Description:')) {
header.description = line.substring(12).trim();
} else if (line.startsWith('Version:')) {
header.version = line.substring(8).trim();
} else if (line.startsWith('TimeUpdated:') || line.startsWith('Last modified:') || line.startsWith('Update Time:')) {
header.lastModified = line.split(':').slice(1).join(':').trim();
} else if (line.startsWith('Expires:')) {
header.expires = line.substring(8).trim();
}
});
return header;
}
// 通用分析函数
function analyzeContent(content, source) {
if (!content.startsWith('[Adblock') && !content.startsWith('![Adblock')) {
alert('这不是一个Adblock规则文件(未找到[Adblock开头),来源: ' + source);
console.log('非Adblock文件,来源:', source);
return;
}
content = normalizeNewlines(content);
const header = parseHeader(content);
const lines = content.split('\n')
.filter(line => line.trim() !== '' && !line.trim().startsWith('!') && !line.trim().startsWith('['));
const stats = {
cssRules: {
normal: 0,
exception: 0,
hasNotPseudo: 0
},
domainRules: {
count: 0,
duplicateRules: 0
},
unsupported: 0,
extendedRules: {
scriptInject: 0,
adguardScript: 0,
htmlFilter: 0,
cssInject: 0,
other: 0
}
};
const extendedPatterns = {
scriptInject: /(##|@#+)\+js\(/,
adguardScript: /#@?%#/,
htmlFilter: /\$\$/,
cssInject: /#@?\$#/,
other: /\$(\s*)(redirect|rewrite|csp|removeparam|badfilter|empty|generichide|match-case|object|object-subrequest|important|popup|document)|,(\s*)(redirect=|app=|replace=|csp=|denyallow=|permissions=)|:matches-path|:remove|redirect-rule/
};
const rulePatternMap = new Map(); // 规则主体 -> {domains: Set, count: Number}
lines.forEach(line => {
const trimmed = line.trim();
// 先检查扩展规则
if (extendedPatterns.scriptInject.test(trimmed)) {
stats.extendedRules.scriptInject++;
stats.unsupported++;
} else if (extendedPatterns.adguardScript.test(trimmed)) {
stats.extendedRules.adguardScript++;
stats.unsupported++;
} else if (extendedPatterns.htmlFilter.test(trimmed)) {
stats.extendedRules.htmlFilter++;
stats.unsupported++;
} else if (extendedPatterns.cssInject.test(trimmed)) {
stats.extendedRules.cssInject++;
stats.unsupported++;
} else if (extendedPatterns.other.test(trimmed)) {
stats.extendedRules.other++;
stats.unsupported++;
}
// 再检查 CSS 和域名规则
else if (trimmed.startsWith('##') || trimmed.startsWith('###')) {
stats.cssRules.normal++;
if (/:has|:not/.test(trimmed)) {
stats.cssRules.hasNotPseudo++;
}
} else if (trimmed.startsWith('#@#') || trimmed.startsWith('#@##')) {
stats.cssRules.exception++;
if (/:has|:not/.test(trimmed)) {
stats.cssRules.hasNotPseudo++;
}
} else if (trimmed.startsWith('||')) {
stats.domainRules.count++;
let rulePattern = trimmed;
let domains = [];
const domainMatch = trimmed.match(/[,|$]domain=([^$|,]+)/);
if (domainMatch) {
rulePattern = trimmed.replace(/[,|$]domain=[^$|,]+/, '').replace(/[,|$].*$/, '');
domains = domainMatch[1].split('|');
}
if (rulePatternMap.has(rulePattern)) {
const ruleData = rulePatternMap.get(rulePattern);
ruleData.count++;
stats.domainRules.duplicateRules += domains.length;
domains.forEach(domain => ruleData.domains.add(domain));
} else {
rulePatternMap.set(rulePattern, {
domains: new Set(domains),
count: 1
});
}
}
});
const totalCssRules = stats.cssRules.normal + stats.cssRules.exception;
const totalExtendedRules = stats.extendedRules.scriptInject + stats.extendedRules.adguardScript +
stats.extendedRules.htmlFilter + stats.extendedRules.cssInject +
stats.extendedRules.other;
// 计算各项得分
let score = 0;
let cssCountScore = 0;
let cssPseudoScore = 0;
let domainCountScore = 0;
let domainDuplicateScore = 0;
let extendedScore = 0;
if (totalCssRules <= 5000) {
cssCountScore = 35; // 满分35分
} else if (totalCssRules <= 7000) {
cssCountScore = 35 - ((totalCssRules - 5000) / 2000) * 10; // 从35降到25
} else if (totalCssRules <= 9999) {
cssCountScore = 25 - ((totalCssRules - 7000) / 2999) * 15; // 从25降到10
} else {
cssCountScore = 10 - ((totalCssRules - 9999) / 5000) * 10; // 从10降到0
}
cssCountScore = Math.max(0, cssCountScore); // 确保分数不低于0
score += cssCountScore;
// CSS伪类评分 (15分)
if (stats.cssRules.hasNotPseudo <= 30) {
cssPseudoScore = 15;
} else if (stats.cssRules.hasNotPseudo <= 100) {
cssPseudoScore = 10;
} else if (stats.cssRules.hasNotPseudo <= 120) {
cssPseudoScore = 5;
} else {
cssPseudoScore = 0;
}
score += cssPseudoScore;
// 域名数量评分 (30分)
if (stats.domainRules.count <= 100000) {
domainCountScore = 30;
} else if (stats.domainRules.count <= 200000) {
domainCountScore = 30 - ((stats.domainRules.count - 100000) / 100000) * 10;
} else if (stats.domainRules.count <= 500000) {
domainCountScore = 20 - ((stats.domainRules.count - 200000) / 300000) * 15;
} else {
domainCountScore = Math.max(0, 5 - ((stats.domainRules.count - 500000) / 500000) * 5);
}
score += domainCountScore;
// 重复规则评分 (10分)
if (stats.domainRules.duplicateRules <= 100) {
domainDuplicateScore = 10;
} else if (stats.domainRules.duplicateRules <= 300) {
domainDuplicateScore = 10 - ((stats.domainRules.duplicateRules - 50) / 150) * 5;
} else {
domainDuplicateScore = Math.max(0, 5 - ((stats.domainRules.duplicateRules - 200) / 200) * 5);
}
score += domainDuplicateScore;
// 扩展规则评分 (±10分)
if (totalExtendedRules === 0) {
extendedScore = 10;
} else if (totalExtendedRules <= 100) {
extendedScore = 10 - (totalExtendedRules / 100) * 5;
} else if (totalExtendedRules <= 300) {
extendedScore = 5 - ((totalExtendedRules - 100) / 200) * 5;
} else {
extendedScore = Math.max(-10, 0 - ((totalExtendedRules - 300) / 300) * 10);
}
score += extendedScore;
// 总分限制在1-100之间
score = Math.max(1, Math.min(100, Math.round(score)));
let cssPerformance;
if (totalCssRules <= 5000) {
cssPerformance = '✅CSS规则数量正常,可以流畅运行';
} else if (totalCssRules <= 7000) {
cssPerformance = '❓CSS规则数量较多,可能会导致设备运行缓慢';
} else if (totalCssRules < 9999) {
cssPerformance = '⚠️CSS规则数量接近上限,可能明显影响设备性能';
} else {
cssPerformance = '🆘CSS规则数量过多,不建议订阅此规则';
}
let domainPerformance;
if (stats.domainRules.count <= 100000) {
domainPerformance = '✅域名规则数量正常,可以流畅运行';
} else if (stats.domainRules.count <= 200000) {
domainPerformance = '❓域名规则数量较多,但仍在可接受范围内';
} else if (stats.domainRules.count <= 500000) {
domainPerformance = '🆘域名规则数量过多,可能会导致内存溢出 (OOM)';
} else {
domainPerformance = '‼️域名规则数量极多,强烈不建议使用,可能严重影响性能';
}
const report = `
Adblock规则分析结果(来源: ${source}):
📜Adblock规则信息:
标题: ${header.title}
描述: ${header.description}
版本: ${header.version}
最后更新: ${header.lastModified}
更新周期: ${header.expires}
---------------------
💯规则评级: ${score}/100
(评分仅供参考,具体以Via变动为主)
📊各部分得分:
CSS数量得分: ${Math.round(cssCountScore)}/35
CSS伪类得分: ${cssPseudoScore}/15
域名数量得分: ${Math.round(domainCountScore)}/30
重复规则得分: ${Math.round(domainDuplicateScore)}/10
扩展规则加减分: ${Math.round(extendedScore)} (±10)
---------------------
🛠️总规则数: ${lines.length}
👋不支持的规则: ${stats.unsupported}
📋CSS通用隐藏规则:
常规规则 (##, ###): ${stats.cssRules.normal}
例外规则 (#@#, #@##): ${stats.cssRules.exception}
含:has/:not伪类规则: ${stats.cssRules.hasNotPseudo}
总CSS规则数: ${totalCssRules}
性能评估: ${cssPerformance}
🔗域名规则 (||):
总数: ${stats.domainRules.count}
重复规则域名数: ${stats.domainRules.duplicateRules}
性能评估: ${domainPerformance}
✋🏼uBlock/AdGuard 独有规则:
脚本注入 (##+js): ${stats.extendedRules.scriptInject}
AdGuard脚本 (#%#): ${stats.extendedRules.adguardScript}
HTML过滤 ($$): ${stats.extendedRules.htmlFilter}
CSS注入 (#$#): ${stats.extendedRules.cssInject}
其他扩展规则 ($redirect等): ${stats.extendedRules.other}
总计: ${totalExtendedRules}
注:uBlock/AdGuard 独有规则在传统 Adblock Plus 中不受支持
`;
alert(report);
console.log(report);
}
})();