From 45a1b883a6b78cb2ab6dd04bcc9635e90efd5ad1 Mon Sep 17 00:00:00 2001 From: Orion Date: Thu, 2 Jul 2026 23:01:28 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(substore):=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=20YinYun=20=E8=AE=A2=E9=98=85=E9=87=8D=E5=91=BD?= =?UTF-8?q?=E5=90=8D=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 新增 yinyun-rename.js 专门处理 YinYun 节点命名 * 实现中文地区名转短码及地区内连续编号逻辑 * 忽略优选、(T)/(R) 线路标记等原始冗余信息 * 更新 README 文档,补充 YinYun 脚本职责与规则说明 * 在文档注意事项中增加对新脚本的使用建议说明 --- substore/README.md | 28 ++++++++- substore/yinyun-rename.js | 126 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 substore/yinyun-rename.js diff --git a/substore/README.md b/substore/README.md index 4eef3c9..28144fe 100644 --- a/substore/README.md +++ b/substore/README.md @@ -18,7 +18,8 @@ https://git.orionc.me/orion/script/raw/branch/main/substore/common.js?v=20260608 4. `alphaair-rename.js`:只处理 Alpha Air 的供应商压缩、倍率排序和连续编号。 5. `iriscloud-rename.js`:只处理 Iris Cloud 的入口/落地地区冲突和供应商特征。 6. `liangxin-rename.js`:只清理 LiangXin 的高速、BGP、CMCU 等线路描述。 -7. `rename.js`:整合订阅最终重命名,负责国家短码、国旗归位、订阅后缀和去重。 +7. `yinyun-rename.js`:只处理 YinYun 的中文地区名,并按地区连续编号。 +8. `rename.js`:整合订阅最终重命名,负责国家短码、国旗归位、订阅后缀和去重。 如果某个订阅没有对应的专属脚本,直接进入最后的 `rename.js` 即可。 @@ -32,6 +33,7 @@ https://git.orionc.me/orion/script/raw/branch/main/substore/common.js?v=20260608 | `alphaair-rename.js` | Alpha Air 单订阅 | 去掉提供者/三网/家宽文案,普通节点保留地区,高级非倍率节点保留供应商,倍率节点保留倍率并连续编号 | | `iriscloud-rename.js` | Iris Cloud 单订阅 | 按 `SG`、`TYO`/`JP`、其余香港的专属规则重判落地地区,去掉入口线路和供应商文案并保留倍率 | | `liangxin-rename.js` | LiangXin 单订阅 | 去掉 `高速`、`BGP`、`CMCU`、`CM`、`CU`、`CT` 等线路描述 | +| `yinyun-rename.js` | YinYun 单订阅 | 中文国家/地区名转短码,忽略 `优选`、原始编号和 `(T)`/`(R)`,按地区连续编号 | | `rename.js` | 整合订阅 | 统一国家/城市短码,移动国旗到首部,追加 `- _subName`,处理重名编号 | | `common.js` | 公共依赖 | 被各处理脚本远程加载,不需要单独配置为处理器 | @@ -159,6 +161,28 @@ https://git.orionc.me/orion/script/raw/branch/main/substore/common.js?v=20260608 => 🇭🇰 HK 01 - LiangXin ``` +## YinYun 规则 + +`yinyun-rename.js` 建议挂在 YinYun 单条订阅里使用。YinYun 的节点名主要由中文地区名、`优选`/数字编号和 `(T)`/`(R)` 线路标记组成;脚本会忽略这些原始线路细节,按地区出现顺序重新连续编号,最后再交给全局 `rename.js` 追加订阅后缀。 + +```text +香港 优选(T) +=> 🇭🇰 HK 01 +=> 🇭🇰 HK 01 - YinYun + +香港 优选(R) +=> 🇭🇰 HK 02 +=> 🇭🇰 HK 02 - YinYun + +哈萨克斯坦 (T) +=> 🇰🇿 KZ 01 +=> 🇰🇿 KZ 01 - YinYun + +魔法节点 (R) +=> MAGIC 01 +=> MAGIC 01 - YinYun +``` + ## 全局重命名 `rename.js` 适合放在整合订阅最后。它做的事情更通用: @@ -195,6 +219,6 @@ Pro-🇭🇰 Sharon|守夜人 - Example ## 注意事项 - `sntp-rename.js` 不带 `_subName` 判断,适合挂在 SNTP 单订阅里;如果放进整合订阅,可能会误伤其他带 `BGP`、`D`、`CT` 的节点。 -- `yuetong-rename.js`、`peiqian-rename.js` 和 `liangxin-rename.js` 也不带 `_subName` 判断,适合挂在对应单订阅里;如果放进整合订阅,需要确认规则不会误伤其他订阅。 +- `yuetong-rename.js`、`peiqian-rename.js`、`liangxin-rename.js` 和 `yinyun-rename.js` 也不带 `_subName` 判断,适合挂在对应单订阅里;如果放进整合订阅,需要确认规则不会误伤其他订阅。 - `rename.js` 会剥离并重新追加 `- _subName`,重复运行不会叠加订阅后缀。 - 如果新增订阅里出现很短、很泛的关键词,例如 `D`、`CT`、`BGP`、`S1`,或像 Alpha Air 这种强绑定单机场的供应商压缩规则,优先拆成单订阅脚本,不建议放进全局 `rename.js`。 diff --git a/substore/yinyun-rename.js b/substore/yinyun-rename.js new file mode 100644 index 0000000..98d983c --- /dev/null +++ b/substore/yinyun-rename.js @@ -0,0 +1,126 @@ +/** + * YinYun 专用节点清理脚本 + * - 将中文地区名压成国家短码并补齐国旗 + * - 忽略 YinYun 原始的 优选、数字编号和 (T)/(R) 线路标记 + * - 按地区出现顺序连续编号,方便全局 rename.js 再追加订阅后缀 + */ +// noinspection JSUnusedGlobalSymbols +const COMMON_SCRIPT_VERSION = '20260608-1'; +const COMMON_SCRIPT_URL = `https://git.orionc.me/orion/script/raw/branch/main/substore/common.js?v=${COMMON_SCRIPT_VERSION}`; +const hasCurrentRenameUtils = utils => Boolean( + utils && utils.version === COMMON_SCRIPT_VERSION +); + +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(); + const regionAliases = [ + ['哈萨克斯坦', 'KZ'], + ['印度尼西亚', 'ID'], + ['孟加拉国', 'BD'], + ['澳大利亚', 'AU'], + ['保加利亚', 'BG'], + ['格鲁吉亚', 'GE'], + ['立陶宛', 'LT'], + ['俄罗斯', 'RU'], + ['新加坡', 'SG'], + ['马来西亚', 'MY'], + ['罗马尼亚', 'RO'], + ['亚美尼亚', 'AM'], + ['葡萄牙', 'PT'], + ['拉脱维亚', 'LV'], + ['加拿大', 'CA'], + ['加拿大 01', 'CA'], + ['澳洲', 'AU'], + ['捷克', 'CZ'], + ['南非', 'ZA'], + ['芬兰', 'FI'], + ['瑞士', 'CH'], + ['墨西哥', 'MX'], + ['瑞典', 'SE'], + ['比利时', 'BE'], + ['阿联酋', 'AE'], + ['法国', 'FR'], + ['波兰', 'PL'], + ['荷兰', 'NL'], + ['奥地利', 'AT'], + ['巴西', 'BR'], + ['土耳其', 'TR'], + ['以色列', 'IL'], + ['意大利', 'IT'], + ['西班牙', 'ES'], + ['香港', 'HK'], + ['美国', 'US'], + ['日本', 'JP'], + ['韩国', 'KR'], + ['台湾', 'TW'] + ]; + const regionResolver = utils.createRegionResolver(regionAliases); + + const stripExistingSuffix = name => String(name) + .replace(/\s+-\s*YinYun(?:\s+\d{2})?\s*$/i, '') + .trim(); + + const stripLineType = name => String(name) + .replace(/\s*[((]\s*[TR]\s*[))]\s*$/i, '') + .trim(); + + const leadingRegionPrefixOf = name => { + const match = String(name).match(/^([A-Z]{2})(?:\s+([A-Z]{3}))?(?=\s|$|-|\d|\|)/); + if (!match) return ''; + return match[2] ? `${match[1]} ${match[2]}` : match[1]; + }; + + const regionPrefixOf = name => { + if (/魔法节点/i.test(name)) return 'MAGIC'; + return regionResolver.aliasRegionPrefixOf(name) || leadingRegionPrefixOf(name); + }; + + const regionCount = Object.create(null); + + return proxies.map(proxy => { + if (typeof proxy.name !== 'string') { + return proxy; + } + + const cleanName = stripExistingSuffix(proxy.name); + const { text } = utils.splitLeadingOrInlineFlag(cleanName); + const coreName = stripLineType(text); + const regionPrefix = regionPrefixOf(coreName); + if (!regionPrefix) { + proxy.name = coreName; + return proxy; + } + + regionCount[regionPrefix] = (regionCount[regionPrefix] || 0) + 1; + const sequence = String(regionCount[regionPrefix]).padStart(2, '0'); + const regionCode = regionPrefix.split(/\s+/)[0]; + const flag = regionPrefix === 'MAGIC' ? '' : utils.regionCodeToFlag(regionCode); + proxy.name = [ + flag, + regionPrefix, + sequence + ].filter(Boolean).join(' ').trim(); + + return proxy; + }); +}