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