Via Adblock 规则分析

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

目前為 2025-03-22 提交的版本,檢視 最新版本

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

        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++;
                }
            } else if (trimmed.startsWith('#@#') || trimmed.startsWith('#@##')) {
                if (extendedPatterns.scriptInject.test(trimmed)) {
                    stats.extendedRules.scriptInject++;
                    stats.unsupported++;
                } else {
                    stats.cssRules.exception++;
                }
            } 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++;
                }
            } 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;

        if (totalCssRules <= 7000) {
            score += 50;
        } else if (totalCssRules <= 9999) {
            score += 50 - ((totalCssRules - 7000) / 3000) * 25;
        } else {
            score += 25 - ((totalCssRules - 9999) / 5000) * 10;
        }

        if (stats.domainRules <= 100000) {
            score += 40;
        } else if (stats.domainRules <= 500000) {
            score += 40 - ((stats.domainRules - 100000) / 400000) * 20;
        } else if (stats.domainRules <= 2000000) {
            score += 20 - ((stats.domainRules - 500000) / 1500000) * 20;
        } else {
            score = Math.max(1, score - ((stats.domainRules - 2000000) / 500000) * 10);
        }

        if (totalExtendedRules > 500) {
            score -= 30;
        } else if (totalExtendedRules > 100) {
            score -= 10 + ((totalExtendedRules - 100) / 400) * 15;
        } else if (totalExtendedRules === 0) {
            score += 10;
        }

        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 <= 100000) {
            domainPerformance = '✅域名规则数量正常,可以流畅运行';
        } else if (stats.domainRules <= 200000) {
            domainPerformance = '❓域名规则数量较多,但仍在可接受范围内';
        } else if (stats.domainRules <= 500000) {
            domainPerformance = '🆘域名规则数量过多,可能会导致内存溢出 (OOM)';
        } else {
            domainPerformance = '‼️域名规则数量极多,强烈不建议使用,可能严重影响性能';
        }

        const report = `
Adblock规则分析结果(来源: ${source}):
📜Adblock规则信息:
  标题: ${header.title}
  描述: ${header.description}
  版本: ${header.version}
  最后更新: ${header.lastModified}
  更新周期: ${header.expires}
💯规则评级: ${score}/100
(CSS规则占比最大,独有规则减分,域名规则次要)
(评分仅供参考,具体以Via变动为主)
---------------------
📋CSS通用隐藏规则:
  常规规则 (##, ###): ${stats.cssRules.normal}
  例外规则 (#@#, #@##): ${stats.cssRules.exception}
  总CSS规则数: ${totalCssRules}
  性能评估: ${cssPerformance}
🔗域名规则 (||): ${stats.domainRules}
  性能评估: ${domainPerformance}
✋🏼uBlock/AdGuard 独有规则:
  脚本注入 (##+js): ${stats.extendedRules.scriptInject}
  AdGuard脚本 (#%#): ${stats.extendedRules.adguardScript}
  HTML过滤 ($$): ${stats.extendedRules.htmlFilter}
  CSS注入 (#$#): ${stats.extendedRules.cssInject}
  其他扩展规则 ($redirect等): ${stats.extendedRules.other}
  总计: ${totalExtendedRules}
👋不支持的规则: ${stats.unsupported}
🛠️总规则数: ${lines.length}
注:uBlock/AdGuard 独有规则在传统 Adblock Plus 中不受支持
        `;
        alert(report);
        console.log(report);
    }
})();