Via Adblock 规则分析

解析Adblock规则,是否值得在Via浏览器上订阅,评分仅供娱乐,自行斟酌。

目前为 2025-03-23 提交的版本。查看 最新版本

// ==UserScript==
// @name         Via Adblock 规则分析
// @namespace    https://viayoo.com/
// @version      1.14
// @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/
        };

        // 用于检测重复规则的Map
        const rulePatternMap = new Map(); // 规则主体 -> {domains: Set, count: Number}

        lines.forEach(line => {
            const trimmed = line.trim();

            if (trimmed.startsWith('##') || trimmed.startsWith('###')) {
                if (extendedPatterns.scriptInject.test(trimmed)) {
                    stats.extendedRules.scriptInject++;
                    stats.unsupported++;
                } else {
                    stats.cssRules.normal++;
                    if (/:has|:not/.test(trimmed)) {
                        stats.cssRules.hasNotPseudo++;
                    }
                }
            } else if (trimmed.startsWith('#@#') || trimmed.startsWith('#@##')) {
                if (extendedPatterns.scriptInject.test(trimmed)) {
                    stats.extendedRules.scriptInject++;
                    stats.unsupported++;
                } else {
                    stats.cssRules.exception++;
                    if (/:has|:not/.test(trimmed)) {
                        stats.cssRules.hasNotPseudo++;
                    }
                }
            } else if (trimmed.startsWith('#')) {
                if (extendedPatterns.adguardScript.test(trimmed)) {
                    stats.extendedRules.adguardScript++;
                    stats.unsupported++;
                } else if (extendedPatterns.cssInject.test(trimmed)) {
                    stats.extendedRules.cssInject++;
                    stats.unsupported++;
                } else {
                    stats.unsupported++;
                }
            } else if (trimmed.startsWith('||')) {
                if (extendedPatterns.other.test(trimmed)) {
                    stats.extendedRules.other++;
                    stats.unsupported++;
                } else {
                    stats.domainRules.count++;
                    // 提取规则主体,去除 $domain= 及其后的内容
                    let rulePattern = trimmed;
                    let domains = [];
                    const domainMatch = trimmed.match(/[,|$]domain=([^$|,]+)/);
                    if (domainMatch) {
                        rulePattern = trimmed.replace(/[,|$]domain=[^$|,]+/, '').replace(/[,|$].*$/, ''); // 去除 $domain= 及之后的修饰符
                        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
                        });
                    }
                }
            } else if (extendedPatterns.htmlFilter.test(trimmed)) {
                stats.extendedRules.htmlFilter++;
                stats.unsupported++;
            }
        });

        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;

        // CSS数量评分 (35分)
        if (totalCssRules <= 7000) {
            cssCountScore = 35;
        } else if (totalCssRules <= 9999) {
            cssCountScore = 35 - ((totalCssRules - 7000) / 3000) * 17.5;
        } else {
            cssCountScore = 17.5 - ((totalCssRules - 9999) / 5000) * 7;
        }
        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 <= 500000) {
            domainCountScore = 30 - ((stats.domainRules.count - 100000) / 400000) * 15;
        } else if (stats.domainRules.count <= 2000000) {
            domainCountScore = 15 - ((stats.domainRules.count - 500000) / 1500000) * 15;
        } else {
            domainCountScore = Math.max(0, 15 - ((stats.domainRules.count - 2000000) / 500000) * 7.5);
        }
        score += domainCountScore;

        // 重复规则评分 (10分)
        if (stats.domainRules.duplicateRules <= 50) {
            domainDuplicateScore = 10;
        } else if (stats.domainRules.duplicateRules <= 200) {
            domainDuplicateScore = 10 - 5 - ((stats.domainRules.duplicateRules - 50) / 150) * 5;
        } else {
            domainDuplicateScore = 0;
        }
        score += domainDuplicateScore;

        // 扩展规则评分 (10分)
        if (totalExtendedRules > 500) {
            extendedScore = -30;
        } else if (totalExtendedRules > 100) {
            extendedScore = -10 - ((totalExtendedRules - 100) / 400) * 15;
        } else if (totalExtendedRules === 0) {
            extendedScore = 10;
        } else {
            extendedScore = 0;
        }
        score += extendedScore;

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

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址