/** * Sub-Store 节点名称属性注入脚本 (全自动配置驱动版) * 核心架构:动态按长度排序编译正则 + 智能边界识别 + O(1) 映射提取 * 维护说明:仅需修改 featureMap 字典即可,底部逻辑永远无需改动。 */ // noinspection JSUnusedGlobalSymbols const COMMON_SCRIPT_URL = 'https://git.orionc.me/orion/script/raw/branch/main/substore/common.js?v=20260603-6'; const hasCurrentRenameUtils = utils => Boolean( utils && typeof utils.normalizeLeadingMojibakeFlag === 'function' ); async function loadRenameUtils() { if (typeof globalThis !== 'undefined' && hasCurrentRenameUtils(globalThis.SubStoreRenameUtils)) { return globalThis.SubStoreRenameUtils; } const cache = typeof scriptResourceCache !== 'undefined' ? scriptResourceCache : null; let script = cache && cache.get(COMMON_SCRIPT_URL); if (!script) { const response = await $substore.http.get({ url: COMMON_SCRIPT_URL }); script = response && (response.body || response.data || response); if (cache) cache.set(COMMON_SCRIPT_URL, script); } new Function(String(script))(); if (!hasCurrentRenameUtils(globalThis.SubStoreRenameUtils)) { throw new Error('Loaded common.js is missing current rename utilities'); } return globalThis.SubStoreRenameUtils; } async function operator(proxies) { const utils = await loadRenameUtils(); // ================= 唯一配置区 ================= // 在这里新增或修改关键词。格式:"节点关键词": "想要注入的标签" // 支持大小写混合,支持包含空格或特殊符号。 // 日后如果要加 IEPL,只需在这里写 "IEPL": "专线" const featureMap = { "GTM 0.5x": "三网", "GTM": "三网", "CT": "CFW", "S1": "广移", "S2": "广电", "S3": "广移", "S4": "广联", "BGP": "cn2", "D": "直连", "Anytls": "直连", "Hy2": "直连" }; // 已经由标签表达含义的特征码,会自动从展示名中移除。 // 维护方式:只改 featureMap 即可,这里不需要单独配置隐藏词。 // 示例:🇭🇰 HK06-GTM Oilguy专线 [三网] - SNTP // 输出:🇭🇰 HK 06 O专线 [三网]-SNTP // ============================================== // ================= 核心逻辑区 (无需修改) ================= // 1. 初始化预处理:生成稳定的查找字典 (全大写+去空格) // 目的:无论节点名叫 "gtm 0.5x" 还是 "GTM0.5X",都能稳定映射 const normalizedFeatureMap = {}; const featureKeywords = Object.keys(featureMap); const normalizeFeatureKeyword = keyword => keyword.toUpperCase().replace(/\s+/g, ''); const normalizeHiddenFeatureKeyword = keyword => keyword.trim().replace(/\s+\d+(?:\.\d+)?x$/i, ''); for (const featureKeyword of featureKeywords) { normalizedFeatureMap[normalizeFeatureKeyword(featureKeyword)] = featureMap[featureKeyword]; } const buildKeywordPattern = keyword => { const keywordPattern = keyword.trim().split(/\s+/).filter(Boolean).map(utils.escapeRegex).join('\\s*'); const trimmedKeyword = keyword.trim(); if (/^[A-Za-z0-9]/.test(trimmedKeyword) && /[A-Za-z0-9]$/.test(trimmedKeyword)) { return `\\b${keywordPattern}\\b`; } return keywordPattern; }; const hiddenFeatureKeywords = [...new Set(featureKeywords.map(normalizeHiddenFeatureKeyword))]; const hiddenFeatureRegexList = [...hiddenFeatureKeywords] .sort((firstKeyword, secondKeyword) => secondKeyword.length - firstKeyword.length) .map(hiddenFeatureKeyword => new RegExp(`(^|[\\s-]+)${buildKeywordPattern(hiddenFeatureKeyword)}(?=$|[\\s-]+)`, 'ig')); const stripHiddenFeatureKeywords = proxyName => hiddenFeatureRegexList .reduce((strippedName, hiddenFeatureRegex) => strippedName.replace(hiddenFeatureRegex, ''), proxyName) .replace(/\s{2,}/g, ' ') .replace(/\s+-/g, '-') .replace(/-\s+/g, ' ') .replace(/^-+|-+$/g, '') .trim(); const abbreviateDedicatedLineProvider = proxyName => proxyName.replace( /\b([A-Za-z])[A-Za-z]*\s*专线/g, (match, providerInitial) => `${providerInitial.toUpperCase()}专线` ); const normalizeDisplayName = proxyName => abbreviateDedicatedLineProvider( utils.normalizeRegionSequence(proxyName.replace(/\b(\d+(?:\.\d+)?)x\b/gi, '$1X')) .replace(/\b(\d+(?:\.\d+)?)X\b/g, '$1x') ); // 2. 动态构建复合正则表达式 (核心引擎) // 按字符串长度降序排序,彻底解决 "短路匹配" (Short-Circuit) 问题 const sortedFeatureKeywords = [...featureKeywords].sort( (firstFeatureKeyword, secondFeatureKeyword) => secondFeatureKeyword.length - firstFeatureKeyword.length ); const featureRegexParts = sortedFeatureKeywords.map(featureKeyword => { // 自动转义正则特殊字符 (防注入报错) return buildKeywordPattern(featureKeyword); }); // 动态拼接出类似: /(GTM\s*0\.5x|\bAnytls\b|\bBGP\b|...)/i const featureRegex = new RegExp(`(${featureRegexParts.join('|')})`, 'i'); // 缓存后缀匹配正则,避免循环内重复创建 const suffixRegex = /\s*-\s*(SNTP.*)$/i; const normalizeSntpSuffixSpacing = proxyName => proxyName.replace(suffixRegex, '-$1'); const normalizeCloudflareLabel = proxyName => proxyName .replace(/\s*\[(?:CF|Cloudflare)]\s*/gi, ' [CFW] ') .replace(/\s{2,}/g, ' ') .trim(); // 3. 执行节点遍历与注入 return proxies.map(proxy => { if (typeof proxy.name !== 'string') return proxy; let proxyName = normalizeCloudflareLabel(normalizeSntpSuffixSpacing(proxy.name)); // 单次复合正则扫描提取 const featureMatch = proxyName.match(featureRegex); if (!featureMatch) { proxy.name = normalizeDisplayName(proxyName); return proxy; // 未命中配置库,仅保留缩写结果后放行 } // 清洗提取到的特征词,并去 O(1) 字典中取值 const normalizedMatchedKeyword = normalizeFeatureKeyword(featureMatch[1]); const injectedFeatureLabel = normalizedFeatureMap[normalizedMatchedKeyword]; if (!injectedFeatureLabel) return proxy; // 兜底安全校验 proxyName = normalizeDisplayName(stripHiddenFeatureKeywords(proxyName)); // 格式化组装与幂等校验 (防重复注入 Bug) const formattedFeatureLabel = ` [${injectedFeatureLabel}]`; if (proxyName.includes(formattedFeatureLabel)) { proxy.name = proxyName; return proxy; } // 实施中缀注入 (Infix Injection) if (suffixRegex.test(proxyName)) { proxy.name = proxyName.replace(suffixRegex, `${formattedFeatureLabel}-$1`); } else { // 纯净节点无后缀时,直接挂载在末尾 proxy.name = proxyName + formattedFeatureLabel; } return proxy; }); }