diff --git a/substore/README.md b/substore/README.md index 7b495b9..e1552e7 100644 --- a/substore/README.md +++ b/substore/README.md @@ -1,23 +1,37 @@ -# 守候网络节点命名脚本 +# Sub-Store 节点命名脚本 -`sntp-rename.js` 是一个专用于守候网络节点命名的 Sub-Store 处理脚本。它会根据节点名称中的线路关键词,自动注入统一的线路标签,让节点列表更容易筛选、分组和识别。 +这个目录放的是一组 Sub-Store 节点处理脚本。推荐把“单订阅专属规则”和“整合订阅全局规则”分开使用,避免一个机场的关键词误伤其他机场。 -## 功能 +## 推荐使用顺序 -脚本会遍历传入的 `proxies` 数组,检查每个代理节点的 `name` 字段。命中关键词后,会把对应标签追加到节点名中: +单订阅脚本先跑,整合订阅最后跑: -- 如果节点名带有 `- SNTP...` 后缀,标签会插入到 `- SNTP` 之前。 -- 如果节点名没有该后缀,标签会追加到名称末尾。 -- 如果已经包含同样标签,不会重复注入。 +1. `sntp-rename.js`:只给 SNTP 节点注入线路标签。 +2. `yuetong-rename.js`:只清理 YueTong 的品牌前缀和装饰符号。 +3. `peiqian-rename.js`:只清理 PeiQian 的推荐/用途描述。 +4. `rename.js`:整合订阅最终重命名,负责国家短码、国旗归位、订阅后缀和去重。 -脚本只处理节点名称,不会修改协议、地址、端口或其他连接参数。 +如果某个订阅没有对应的专属脚本,直接进入最后的 `rename.js` 即可。 -## 默认规则 +## 脚本职责 + +| 脚本 | 建议配置位置 | 作用 | +| --- | --- | --- | +| `sntp-rename.js` | SNTP 单订阅 | 注入 `[三网]`、`[CF]`、`[直连]`、`[cn2]` 等线路标签 | +| `yuetong-rename.js` | YueTong 单订阅 | 去掉 `悦·`、`💎`,把 `AI解锁` 压成 `AI` | +| `peiqian-rename.js` | PeiQian 单订阅 | 压短 `电信联通推荐`、`移动联通推荐`、`高速专线`、`hy2`、倍率和用途描述 | +| `rename.js` | 整合订阅 | 统一国家/城市短码,移动国旗到首部,追加 `- _subName`,处理重名编号 | + +## SNTP 规则 + +`sntp-rename.js` 只建议用于 SNTP。像 `BGP`、`D`、`CT` 这类关键词比较短,全局使用容易误伤其他订阅。 | 命中关键词 | 注入标签 | | --- | --- | | `GTM 0.5x` | `[三网]` | | `GTM` | `[三网]` | +| `CT` | `[CF]` | +| `D` | `[直连]` | | `S1` | `[广移]` | | `S2` | `[广电]` | | `S3` | `[广移]` | @@ -25,75 +39,82 @@ | `BGP` | `[cn2]` | | `Anytls` | `[直连]` | -## 示例 - -输入: +示例: ```text -香港 GTM 0.5x - SNTP 01 -广州 S1 - SNTP 02 -日本 BGP 01 -深圳 Anytls - SNTP 03 -普通节点 01 +🇭🇰 HK-CT-1 0.2x - SNTP +=> 🇭🇰 HK-CT-1 0.2x [CF] - SNTP + +🇯🇵 JP-D-1-0.2x - SNTP +=> 🇯🇵 JP-D-1-0.2x [直连] - SNTP ``` -输出: +## YueTong 规则 + +`yuetong-rename.js` 只处理 `_subName === 'YueTong'`: ```text -香港 GTM 0.5x [三网] - SNTP 01 -广州 S1 [广移] - SNTP 02 -日本 BGP 01 [cn2] -深圳 Anytls [直连] - SNTP 03 -普通节点 01 +🇯🇵 悦·日本专属2💎 - YueTong +=> 🇯🇵 日本专属2 - YueTong ``` -## 在 Sub-Store 中使用 +再经过 `rename.js` 后: -1. 打开 Sub-Store。 -2. 进入订阅或节点处理配置。 -3. 添加 JavaScript 脚本处理器。 -4. 粘贴 [sntp-rename.js](sntp-rename.js) 内容。 -5. 保存并刷新订阅。 - -脚本暴露的入口函数为: - -```javascript -function operator(proxies) { - // ... -} +```text +🇯🇵 日本专属2 - YueTong +=> 🇯🇵 JP 专属 2 - YueTong ``` -## 修改规则 +## PeiQian 规则 -只需要编辑脚本顶部的 `featureMap`。左侧是节点名称中要识别的关键词,右侧是要注入的标签内容: +`peiqian-rename.js` 只处理 `_subName === 'PeiQian'`: -```javascript -const featureMap = { - "GTM 0.5x": "三网", - "GTM": "三网", - "S1": "广移", - "S2": "广电", - "S3": "广移", - "S4": "广联", - "BGP": "cn2", - "Anytls": "直连" -}; +```text +🇯🇵 日本东京03-0.1倍 | 电信联通推荐 - PeiQian +=> 🇯🇵 日本东京03 0.1x [电联] - PeiQian + +🇺🇸 美国01 | 合适下载使用-0.01倍 - PeiQian +=> 🇺🇸 美国01 0.01x - PeiQian + +🇼🇸 台湾 | 避免晚高峰使用 - PeiQian +=> 🇼🇸 台湾 - PeiQian ``` -新增规则示例: +再经过 `rename.js` 后会变成: -```javascript -"IEPL": "专线" +```text +🇯🇵 JP TYO 03 0.1x [电联] - PeiQian +🇺🇸 US 01 0.01x - PeiQian +🇼🇸 TW - PeiQian ``` -## 匹配逻辑 +## 全局重命名 -- 关键词匹配不区分大小写。 -- 关键词会按长度从长到短生成正则,避免 `GTM` 先抢走 `GTM 0.5x` 的匹配。 -- 纯字母数字关键词会自动加单词边界,避免 `S1` 错误匹配到 `US1`。 -- 映射时会忽略关键词中的空格,例如 `GTM 0.5x` 与 `GTM0.5X` 会映射到同一个标签。 +`rename.js` 适合放在整合订阅最后。它做的事情更通用: + +- 国家/地区中文名、英文名转短码,例如 `香港` -> `HK`、`Japan` -> `JP`。 +- 常见城市转短码,例如 `Los Angeles` -> `LA`、`Seattle` -> `SEA`、`东京` -> `TYO`。 +- 支持国旗在中间的节点名,例如 `Pro-🇭🇰 Sharon|守夜人` -> `🇭🇰 HK Pro-Sharon|守夜人`。 +- 清理 `[DIP China-Hongkong]` 为 `[DIP]`。 +- 清理 `IEPL 专线` 这类冗余描述。 +- 追加订阅后缀 `- _subName`。 +- 对最终重名节点追加两位编号。 + +示例: + +```text +🇭🇰 Hong Kong 01 [DIP China-Hongkong] - Nexitally +=> 🇭🇰 HK 01 [DIP] - Nexitally + +🇭🇰 香港实验性 IEPL 专线 1 - Alpha Flower +=> 🇭🇰 HK 实验性 1 - Alpha Flower + +Pro-🇭🇰 Sharon|守夜人 - Alpha 8K Snell +=> 🇭🇰 HK Pro-Sharon|守夜人 - Alpha 8K Snell +``` ## 注意事项 -- 如果节点命中多个关键词,只会按当前正则排序匹配第一个。 -- 如需支持更多后缀位置,可修改脚本中的 `suffixRegex`。 \ No newline at end of file +- 专属脚本都带 `_subName` 判断,放错位置通常也不会处理其他订阅。 +- `rename.js` 会剥离并重新追加 `- _subName`,重复运行不会叠加订阅后缀。 +- 如果新增订阅里出现很短、很泛的关键词,例如 `D`、`CT`、`BGP`、`S1`,优先拆成单订阅脚本,不建议放进全局 `rename.js`。 diff --git a/substore/peiqian-rename.js b/substore/peiqian-rename.js new file mode 100644 index 0000000..7831d91 --- /dev/null +++ b/substore/peiqian-rename.js @@ -0,0 +1,33 @@ +/** + * PeiQian 专用节点清理脚本 + * 只处理 _subName 为 PeiQian 的节点,避免把“推荐/用途/hy2”等文案规则误伤到其他订阅。 + */ +function operator(proxies) { + return proxies.map(proxy => { + if (proxy._subName !== 'PeiQian' || typeof proxy.name !== 'string') { + return proxy; + } + + proxy.name = proxy.name + .replace(/^AWS\s*新加坡\s*/i, '新加坡 AWS ') + .replace(/\s*\|\s*合适下载使用\s*-\s*(\d+(?:\.\d+)?)倍/gi, ' $1x') + .replace(/\s*\|\s*合适下载使用/gi, '') + .replace(/\s*\|\s*避免晚高峰使用/gi, '') + .replace(/\s*\|\s*电信联通移动推荐\s*-\s*hy2/gi, ' [三网 HY2]') + .replace(/\s*\|\s*电信联通移动推荐/gi, ' [三网]') + .replace(/\s*\|\s*电信联通推荐\s*-\s*hy2/gi, ' [电联 HY2]') + .replace(/\s*\|\s*电信联通推荐/gi, ' [电联]') + .replace(/\s*\|\s*移动联通推荐\s*-\s*hy2/gi, ' [移联 HY2]') + .replace(/\s*\|\s*移动联通推荐/gi, ' [移联]') + .replace(/\s*\|\s*高速专线(?:推荐)?\s*-\s*hy2/gi, ' [HY2]') + .replace(/\s*\|\s*高速专线推荐/gi, ' [专线]') + .replace(/\s*\|\s*三网推荐/gi, ' [三网]') + .replace(/-\s*(\d+(?:\.\d+)?)倍/gi, ' $1x') + .replace(/(\d+(?:\.\d+)?)倍/gi, '$1x') + .replace(/\s+\[/g, ' [') + .replace(/\s{2,}/g, ' ') + .trim(); + + return proxy; + }); +} diff --git a/substore/rename.js b/substore/rename.js new file mode 100644 index 0000000..0af4244 --- /dev/null +++ b/substore/rename.js @@ -0,0 +1,371 @@ +/** + * Sub-Store 节点重命名脚本 + * - 统一节点前缀为地区短码,保留旗帜与订阅后缀 + * - 清理常见冗余线路描述,例如 DIP 地区说明、IEPL 专线 + * - 基于归一化后的完整名称做精准去重 + */ +function operator(proxies) { + const globalCount = {}; + const currentCount = {}; + + const regionAliases = [ + ['United Arab Emirates', 'AE'], + ['United States Los Angeles', 'US LA'], + ['United States San Jose', 'US SJ'], + ['United States Seattle', 'US SEA'], + ['Australia Sydney', 'AU SYD'], + ['Russia St. Petersburg', 'RU LED'], + ['Russia Moscow', 'RU MOW'], + ['USA Los Angeles', 'US LA'], + ['USA San Jose', 'US SJ'], + ['USA Seattle', 'US SEA'], + ['United Kingdom', 'UK'], + ['United States', 'US'], + ['South Africa', 'ZA'], + ['Saudi Arabia', 'SA'], + ['New Zealand', 'NZ'], + ['Hong Kong', 'HK'], + ['Netherlands', 'NL'], + ['Switzerland', 'CH'], + ['Singapore', 'SG'], + ['Indonesia', 'ID'], + ['Philippines', 'PH'], + ['Azerbaijan', 'AZ'], + ['Kazakhstan', 'KZ'], + ['Kyrgyzstan', 'KG'], + ['Argentina', 'AR'], + ['Australia', 'AU'], + ['Macedonia', 'MK'], + ['Lithuania', 'LT'], + ['Colombia', 'CO'], + ['Malaysia', 'MY'], + ['Thailand', 'TH'], + ['Vietnam', 'VN'], + ['Romania', 'RO'], + ['Nigeria', 'NG'], + ['Bulgaria', 'BG'], + ['Germany', 'DE'], + ['Hungary', 'HU'], + ['Austria', 'AT'], + ['Ireland', 'IE'], + ['Turkey', 'TR'], + ['Taiwan', 'TW'], + ['Canada', 'CA'], + ['Brazil', 'BR'], + ['Russia', 'RU'], + ['France', 'FR'], + ['Sweden', 'SE'], + ['Belgium', 'BE'], + ['Denmark', 'DK'], + ['Iceland', 'IS'], + ['Norway', 'NO'], + ['Morocco', 'MA'], + ['Mexico', 'MX'], + ['Israel', 'IL'], + ['Spain', 'ES'], + ['Italy', 'IT'], + ['Czech', 'CZ'], + ['Egypt', 'EG'], + ['Chile', 'CL'], + ['India', 'IN'], + ['Korea', 'KR'], + ['Japan', 'JP'], + ['Macau', 'MO'], + ['Dubai', 'AE DXB'], + ['Sydney', 'AU SYD'], + ['Melbourne', 'AU MEL'], + ['Toronto', 'CA TOR'], + ['Casablanca', 'MA CMN'], + ['USA', 'US'], + + ['美国圣何塞', 'US SJ'], + ['美国洛杉矶', 'US LA'], + ['美国阿什本', 'US IAD'], + ['日本东京', 'JP TYO'], + ['韩国首尔', 'KR SEL'], + ['英国伦敦', 'UK LON'], + ['瑞士苏黎世', 'CH ZRH'], + ['印度尼西亚', 'ID'], + ['所罗门群岛', 'SB'], + ['阿塞拜疆', 'AZ'], + ['哈萨克斯坦', 'KZ'], + ['吉尔吉斯斯坦', 'KG'], + ['卡萨布兰卡', 'MA CMN'], + ['澳大利亚', 'AU'], + ['新西兰', 'NZ'], + ['马来西亚', 'MY'], + ['尼日利亚', 'NG'], + ['摩尔多瓦', 'MD'], + ['巴基斯坦', 'PK'], + ['阿根廷', 'AR'], + ['阿联酋', 'AE'], + ['柬埔寨', 'KH'], + ['东帝汶', 'TL'], + ['梵蒂冈', 'VA'], + ['百慕大', 'BM'], + ['格陵兰', 'GL'], + ['南极洲', 'AQ'], + ['罗马尼亚', 'RO'], + ['哥伦比亚', 'CO'], + ['菲律宾', 'PH'], + ['以色列', 'IL'], + ['比利时', 'BE'], + ['立陶宛', 'LT'], + ['马其顿', 'MK'], + ['俄罗斯', 'RU'], + ['乌克兰', 'UA'], + ['匈牙利', 'HU'], + ['西班牙', 'ES'], + ['土耳其', 'TR'], + ['加拿大', 'CA'], + ['意大利', 'IT'], + ['奥地利', 'AT'], + ['阿富汗', 'AF'], + ['卡塔尔', 'QA'], + ['索马里', 'SO'], + ['新加坡', 'SG'], + ['印度', 'IN'], + ['印尼', 'ID'], + ['日本', 'JP'], + ['香港', 'HK'], + ['台湾', 'TW'], + ['美国', 'US'], + ['韩国', 'KR'], + ['英国', 'UK'], + ['德国', 'DE'], + ['荷兰', 'NL'], + ['法国', 'FR'], + ['巴西', 'BR'], + ['智利', 'CL'], + ['澳门', 'MO'], + ['泰国', 'TH'], + ['越南', 'VN'], + ['瑞典', 'SE'], + ['丹麦', 'DK'], + ['多哥', 'TG'], + ['埃及', 'EG'], + ['冰岛', 'IS'], + ['挪威', 'NO'], + ['南非', 'ZA'], + ['古巴', 'CU'], + ['斐济', 'FJ'], + ['关岛', 'GU'], + ['缅甸', 'MM'], + ['蒙古', 'MN'], + ['老挝', 'LA'], + ['捷克', 'CZ'], + ['沙特', 'SA'], + ['墨西哥', 'MX'], + ['迪拜', 'AE DXB'], + ['悉尼', 'AU SYD'], + ['墨尔本', 'AU MEL'], + ['多伦多', 'CA TOR'] + ]; + + const sortedRegionAliases = regionAliases + .slice() + .sort((firstAlias, secondAlias) => secondAlias[0].length - firstAlias[0].length); + + const flagCodeOverrides = { + '🇬🇧': 'UK', + '🇼🇸': 'TW' + }; + + const knownRegionCodes = new Set( + regionAliases.map(([, replacement]) => replacement.split(/\s+/)[0]) + ); + Object.values(flagCodeOverrides).forEach(code => knownRegionCodes.add(code)); + + const escapeRegex = value => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + + const flagToRegionCode = flag => { + if (!flag) return ''; + if (flagCodeOverrides[flag]) return flagCodeOverrides[flag]; + + const codePoints = [...flag].map(char => char.codePointAt(0)); + if ( + codePoints.length !== 2 || + codePoints.some(codePoint => codePoint < 0x1F1E6 || codePoint > 0x1F1FF) + ) { + return ''; + } + + return codePoints + .map(codePoint => String.fromCharCode(codePoint - 0x1F1E6 + 65)) + .join(''); + }; + + const stripExistingSuffix = (name, subName) => { + if (!subName) return name.trim(); + const suffixRegex = new RegExp(`\\s+-\\s*${escapeRegex(subName)}(?:\\s+\\d{2})?\\s*$`, 'i'); + return name.replace(suffixRegex, '').trim(); + }; + + const joinAlias = (replacement, rest) => { + const cleanRest = rest.trim(); + if (!cleanRest) return replacement; + if (/^[|/\-]/.test(cleanRest)) { + return `${replacement}${cleanRest}`; + } + return `${replacement} ${cleanRest}`; + }; + + const normalizeLeadingRegion = name => { + const cleanName = name.trim(); + + for (const [alias, replacement] of sortedRegionAliases) { + if (!cleanName.toLowerCase().startsWith(alias.toLowerCase())) continue; + + const rest = cleanName.slice(alias.length); + if (/^[A-Za-z]/.test(alias) && rest && !/^[\s\d[|\-.x]/i.test(rest)) { + continue; + } + + return joinAlias(replacement, rest); + } + + return cleanName; + }; + + const leadingCodeOf = name => { + const match = name.match(/^([A-Z]{2})(?=\b|\s|-|$|\d)/); + return match ? match[1] : ''; + }; + + const hasKnownRegionCode = name => { + const leadingCode = leadingCodeOf(name); + if (knownRegionCodes.has(leadingCode)) return true; + + const proMatch = name.match(/^Pro-\s*(.+)$/i); + return proMatch ? knownRegionCodes.has(leadingCodeOf(proMatch[1])) : false; + }; + + const hasSpecificRegionCode = (name, regionCode) => { + if (!regionCode) return false; + if (leadingCodeOf(name) === regionCode) return true; + + const proMatch = name.match(/^Pro-\s*(.+)$/i); + return proMatch ? leadingCodeOf(proMatch[1]) === regionCode : false; + }; + + const moveProRegionToFront = (name, regionCode) => { + if (!regionCode) return name; + + const proRegionRegex = new RegExp(`^Pro-\\s*${escapeRegex(regionCode)}(?=\\s|[\\u4e00-\\u9fff]|\\d)(.*)$`, 'i'); + const proRegionMatch = name.match(proRegionRegex); + if (!proRegionMatch) return name; + + const rest = proRegionMatch[1].trim(); + return rest ? `${regionCode} Pro-${rest}` : regionCode; + }; + + const simplifyCoreName = name => { + return name + .replace(/[\u{1F300}-\u{1FAFF}\u{2600}-\u{27BF}\uFE0F]/gu, '') + .replace(/\u200d/g, '') + .replace(/-\s*(\d+(?:\.\d+)?)倍/gi, ' $1x') + .replace(/(\d+(?:\.\d+)?)倍/gi, '$1x') + .replace(/\b(\d+(?:\.\d+)?)X\b/g, '$1x') + .replace(/([A-Z]{2})\s*(专属|家宽|AI|高速|专线)(\d+)/g, '$1 $2 $3') + .replace(/\b(AI)(\d+)/g, '$1 $2') + .replace(/\b(Pro-)\s+/gi, '$1') + .replace(/^((?:Pro-\s*)?)([A-Z]{2})(?=[\u4e00-\u9fff]|\d)/u, '$1$2 ') + .replace(/\s+\[/g, ' [') + .replace(/\s{2,}/g, ' ') + .trim(); + }; + + const normalizeBilingualName = name => { + const bilingualMatch = name.match(/^(.+?)\s*|\s*(.+)$/); + if (!bilingualMatch) return name; + + const leftName = normalizeLeadingRegion(bilingualMatch[1]); + const rightName = normalizeLeadingRegion(bilingualMatch[2]); + const leftCode = leadingCodeOf(leftName); + const rightCode = leadingCodeOf(rightName); + + if (leftCode && rightCode && leftCode === rightCode) { + return rightName === rightCode ? leftName : rightName; + } + if (rightCode) return rightName; + if (leftCode) return leftName; + + return name.replace(/\s*|\s*/g, ' '); + }; + + const normalizeCoreName = name => { + const flagPattern = '([\\u{1F1E6}-\\u{1F1FF}]{2}|🏴‍☠️)'; + const leadingFlagMatch = name.match(new RegExp(`^${flagPattern}\\s*`, 'u')); + const inlineFlagMatch = leadingFlagMatch + ? null + : name.match(new RegExp(`^(.{1,24}?)${flagPattern}\\s*(.*)$`, 'u')); + const flag = leadingFlagMatch ? leadingFlagMatch[1] : inlineFlagMatch ? inlineFlagMatch[2] : ''; + let core = name.trim(); + + if (leadingFlagMatch) { + core = name.slice(leadingFlagMatch[0].length).trim(); + } else if (inlineFlagMatch) { + const prefix = inlineFlagMatch[1].trim(); + const rest = inlineFlagMatch[3].trim(); + core = rest ? `${prefix}${prefix.endsWith('-') ? '' : ' '}${rest}` : prefix; + } + + core = core + .replace(/\[DIP[^\]]*\]/gi, '[DIP]'); + + core = normalizeBilingualName(core); + core = normalizeLeadingRegion(core); + + if (/^Pro-\s*/i.test(core)) { + core = core.replace(/^(Pro-\s*)(.+)$/i, (fullMatch, proPrefix, restName) => { + return `${proPrefix}${normalizeLeadingRegion(restName)}`; + }); + } + + core = core + .replace(/^([A-Z]{2})\s*(实验性|标准|高级)\s+IEPL\s+专线\s*(\d+)(.*)$/i, '$1 $2 $3$4') + .replace(/搬瓦工荷兰/g, '搬瓦工') + .replace(/\s{2,}/g, ' ') + .trim(); + + const flagRegionCode = flagToRegionCode(flag); + core = moveProRegionToFront(core, flagRegionCode); + + if ( + flagRegionCode && + !hasSpecificRegionCode(core, flagRegionCode) && + !hasKnownRegionCode(core) + ) { + core = joinAlias(flagRegionCode, core); + } + + core = simplifyCoreName(core); + + return flag ? `${flag} ${core}` : core; + }; + + proxies.forEach(proxy => { + const proxyName = typeof proxy.name === 'string' ? proxy.name : ''; + const subName = typeof proxy._subName === 'string' ? proxy._subName.trim() : ''; + const coreName = stripExistingSuffix(proxyName, subName); + const normalizedCoreName = normalizeCoreName(coreName); + const baseName = subName ? `${normalizedCoreName} - ${subName}` : normalizedCoreName; + + proxy._tempBaseName = baseName; + globalCount[baseName] = (globalCount[baseName] || 0) + 1; + }); + + return proxies.map(proxy => { + const baseName = proxy._tempBaseName; + + if (globalCount[baseName] > 1) { + currentCount[baseName] = (currentCount[baseName] || 0) + 1; + proxy.name = `${baseName} ${currentCount[baseName].toString().padStart(2, '0')}`; + } else { + proxy.name = baseName; + } + + delete proxy._tempBaseName; + return proxy; + }); +} diff --git a/substore/sntp-rename.js b/substore/sntp-rename.js index fd23300..253effb 100644 --- a/substore/sntp-rename.js +++ b/substore/sntp-rename.js @@ -10,11 +10,13 @@ function operator(proxies) { const featureMap = { "GTM 0.5x": "三网", "GTM": "三网", + "CT": "CF", "S1": "广移", "S2": "广电", "S3": "广移", "S4": "广联", "BGP": "cn2", + "D": "直连", "Anytls": "直连" // 日后如果要加 IEPL,只需在这里写 "IEPL": "专线" }; // ============================================== diff --git a/substore/yuetong-rename.js b/substore/yuetong-rename.js new file mode 100644 index 0000000..641b5d6 --- /dev/null +++ b/substore/yuetong-rename.js @@ -0,0 +1,20 @@ +/** + * YueTong 专用节点清理脚本 + * 只处理 _subName 为 YueTong 的节点,负责去掉“悦·”品牌前缀、钻石等装饰,以及压短 AI 解锁文案。 + */ +function operator(proxies) { + return proxies.map(proxy => { + if (proxy._subName !== 'YueTong' || typeof proxy.name !== 'string') { + return proxy; + } + + proxy.name = proxy.name + .replace(/悦[·・]\s*/g, '') + .replace(/AI解锁/g, 'AI') + .replace(/[💎]/gu, '') + .replace(/\s{2,}/g, ' ') + .trim(); + + return proxy; + }); +}