From 7a62f839f153d17b134f832c7fb8894b38c6f6fd Mon Sep 17 00:00:00 2001 From: Orion Date: Thu, 2 Jul 2026 23:40:05 +0800 Subject: [PATCH] =?UTF-8?q?refactor(substore):=20=E5=B0=86=E9=80=9A?= =?UTF-8?q?=E7=94=A8=E5=B7=A5=E5=85=B7=E5=87=BD=E6=95=B0=E8=BF=81=E7=A7=BB?= =?UTF-8?q?=E8=87=B3=E5=85=AC=E5=85=B1=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 从各个重命名脚本中移除重复的工具函数实现 - 将 multiplierOf、multiplierValueOf、stripSubscriptionSuffix 等函数统一到 common.js - 使用 utils.multiplierOf、utils.multiplierValueOf 替代本地实现 - 引入 utils.createSequenceCounter 替代手动计数逻辑 - 更新版本号从 20260608-1 到 20260702-1 - 移除重复的正则表达式匹配逻辑 - 标准化序列计数器实现方式 - 优化 yinyun-rename.js 中的信息节点过滤逻辑 --- substore/README.md | 8 +++++-- substore/alphaair-rename.js | 32 +++++++--------------------- substore/common.js | 41 ++++++++++++++++++++++++++++++++++-- substore/iriscloud-rename.js | 25 ++++++---------------- substore/liangxin-rename.js | 2 +- substore/peiqian-rename.js | 5 ++--- substore/rename.js | 2 +- substore/sntp-rename.js | 2 +- substore/yinyun-rename.js | 36 +++++++++++++++---------------- substore/yuetong-rename.js | 7 +++--- 10 files changed, 86 insertions(+), 74 deletions(-) diff --git a/substore/README.md b/substore/README.md index 28144fe..284aeb3 100644 --- a/substore/README.md +++ b/substore/README.md @@ -5,7 +5,7 @@ `common.js` 是公共工具库,各处理脚本会按下面的 raw 地址自动加载: ```text -https://git.orionc.me/orion/script/raw/branch/main/substore/common.js?v=20260608-1 +https://git.orionc.me/orion/script/raw/branch/main/substore/common.js?v=20260702-1 ``` ## 推荐使用顺序 @@ -163,7 +163,7 @@ https://git.orionc.me/orion/script/raw/branch/main/substore/common.js?v=20260608 ## YinYun 规则 -`yinyun-rename.js` 建议挂在 YinYun 单条订阅里使用。YinYun 的节点名主要由中文地区名、`优选`/数字编号和 `(T)`/`(R)` 线路标记组成;脚本会忽略这些原始线路细节,按地区出现顺序重新连续编号,最后再交给全局 `rename.js` 追加订阅后缀。 +`yinyun-rename.js` 建议挂在 YinYun 单条订阅里使用。YinYun 的节点名主要由中文地区名、`优选`/数字编号、`(T)`/`(R)` 线路标记和可选 `ᴮᴬᴷ` 备份标签组成;脚本会忽略这些原始线路细节,按地区出现顺序重新连续编号,过滤官网/TG 信息节点,最后再交给全局 `rename.js` 追加订阅后缀。 ```text 香港 优选(T) @@ -181,6 +181,10 @@ https://git.orionc.me/orion/script/raw/branch/main/substore/common.js?v=20260608 魔法节点 (R) => MAGIC 01 => MAGIC 01 - YinYun + +泰国 (T) ᴮᴬᴷ +=> 🇹🇭 TH 01 +=> 🇹🇭 TH 01 - YinYun ``` ## 全局重命名 diff --git a/substore/alphaair-rename.js b/substore/alphaair-rename.js index c800c17..21c6f36 100644 --- a/substore/alphaair-rename.js +++ b/substore/alphaair-rename.js @@ -6,7 +6,7 @@ * - 不追加订阅后缀,整合订阅最后仍交给 rename.js 处理 */ // noinspection JSUnusedGlobalSymbols -const COMMON_SCRIPT_VERSION = '20260608-1'; +const COMMON_SCRIPT_VERSION = '20260702-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 @@ -63,15 +63,6 @@ async function operator(proxies) { ]; const regionResolver = utils.createRegionResolver(regionAliases); - const stripExistingSuffix = name => name - .replace(/\s+-\s*Alpha Air(?:\s+\d{2})?\s*$/i, '') - .trim(); - - const multiplierOf = name => { - const match = name.match(/\b(\d+(?:\.\d+)?)(?:\s*[xX]|倍)\b/i); - return match ? `${match[1]}X` : ''; - }; - const providerOf = name => { if (/SkyStroll/i.test(name)) return 'SkyStroll'; if (/isif/i.test(name)) return 'ISIF'; @@ -79,12 +70,6 @@ async function operator(proxies) { return ''; }; - const leadingRegionPrefixOf = name => { - const match = name.match(/^([A-Z]{2})(?:\s+([A-Z]{3}))?(?=\s|$|-|\d|\|)/); - if (!match) return ''; - return match[2] ? `${match[1]} ${match[2]}` : match[1]; - }; - const alphaAirRegionPrefixOf = name => { if (/\bhk\b/i.test(name)) return 'HK'; if (/\bsg\b/i.test(name)) return 'SG'; @@ -99,20 +84,20 @@ async function operator(proxies) { if (/Hinet|Seednet/i.test(name)) return 'TW'; return alphaAirRegionPrefixOf(name) || - leadingRegionPrefixOf(name) || + utils.leadingRegionPrefixOf(name) || flagRegionCode || ''; }; const normalizeProxy = (proxy, originalIndex) => { const rawName = typeof proxy.name === 'string' ? proxy.name : ''; - const cleanName = stripExistingSuffix(rawName); + const cleanName = utils.stripSubscriptionSuffix(rawName, 'Alpha Air'); const { flag, text } = utils.splitLeadingOrInlineFlag(cleanName); const flagRegionCode = utils.flagToRegionCode(flag); const regionPrefix = regionPrefixOf(text, flagRegionCode); const regionCode = regionPrefix.split(/\s+/)[0]; const cityCode = regionPrefix.split(/\s+/)[1] || ''; - const multiplier = multiplierOf(text); + const multiplier = utils.multiplierOf(text); const provider = multiplier ? '' : providerOf(text); const normalizedFlag = utils.regionCodeToFlag(regionCode); @@ -126,7 +111,7 @@ async function operator(proxies) { provider, multiplier, providerRank: provider ? 1 : 0, - multiplierValue: multiplier ? Number(multiplier.replace(/X$/i, '')) : Number.NEGATIVE_INFINITY, + multiplierValue: utils.multiplierValueOf(multiplier), normalizedName: [normalizedFlag, regionPrefix, provider, multiplier].filter(Boolean).join(' ').trim() }; }; @@ -166,18 +151,17 @@ async function operator(proxies) { return firstItem.originalIndex - secondItem.originalIndex; }); - const currentCount = Object.create(null); + const nextSequence = utils.createSequenceCounter(); return normalizedItems.map(item => { if (!item.normalizedName || !item.countryCode) return item.proxy; - currentCount[item.countryCode] = (currentCount[item.countryCode] || 0) + 1; item.proxy.name = [ item.normalizedFlag, item.regionPrefix, - String(currentCount[item.countryCode]).padStart(2, '0'), + nextSequence(item.countryCode), item.provider, item.multiplier ].filter(Boolean).join(' ').trim(); return item.proxy; }); -} \ No newline at end of file +} diff --git a/substore/common.js b/substore/common.js index 5de7384..8094561 100644 --- a/substore/common.js +++ b/substore/common.js @@ -2,10 +2,10 @@ * Sub-Store 节点命名公共工具 * * 远程引用地址: - * https://git.orionc.me/orion/script/raw/branch/main/substore/common.js?v=20260608-1 + * https://git.orionc.me/orion/script/raw/branch/main/substore/common.js?v=20260702-1 */ (function (root) { - const VERSION = '20260608-1'; + const VERSION = '20260702-1'; const regexSpecialChars = new Set(['.', '*', '+', '?', '^', '$', '{', '}', '(', ')', '|', '[', ']', '\\']); const leadingFlagRegex = /^([\u{1F1E6}-\u{1F1FF}]{2}|🏴‍☠️)\s*/u; const inlineFlagRegex = /^(.{1,24}?)([\u{1F1E6}-\u{1F1FF}]{2}|🏴‍☠️)\s*(.*)$/u; @@ -206,6 +206,38 @@ return `${nameWithoutMultiplier} ${multipliers.join(' ')}`.trim(); }; + const multiplierOf = name => { + const match = String(name).match(/\b(\d+(?:\.\d+)?)(?:\s*[xX]|倍)\b/i); + return match ? `${match[1]}X` : ''; + }; + + const multiplierValueOf = multiplier => ( + multiplier ? Number(String(multiplier).replace(/X$/i, '')) : Number.NEGATIVE_INFINITY + ); + + const stripSubscriptionSuffix = (name, subName) => { + const cleanSubName = String(subName || '').trim(); + if (!cleanSubName) return String(name).trim(); + + return String(name) + .replace(new RegExp(`\\s+-\\s*${escapeRegex(cleanSubName)}(?:\\s+\\d{2})?\\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 createSequenceCounter = () => { + const counts = Object.create(null); + return key => { + counts[key] = (counts[key] || 0) + 1; + return String(counts[key]).padStart(2, '0'); + }; + }; + const normalizeRegionSequence = (name, options) => { const allowCityCode = Boolean(options && options.allowCityCode); const cacheKey = allowCityCode ? 'city' : 'country'; @@ -341,7 +373,10 @@ escapeRegex, flagToRegionCode, joinAlias, + leadingRegionPrefixOf, moveMultiplierToEnd, + multiplierOf, + multiplierValueOf, normalizeHyphenatedRegionFeatures, normalizeLeadingFlagSpacing, normalizeLeadingMojibakeFlag, @@ -353,6 +388,8 @@ stripLeadingMojibakeFlag, splitLeadingFlag, splitLeadingOrInlineFlag, + createSequenceCounter, + stripSubscriptionSuffix, stripLineDescriptors, version: VERSION }; diff --git a/substore/iriscloud-rename.js b/substore/iriscloud-rename.js index 13ba593..eb763b6 100644 --- a/substore/iriscloud-rename.js +++ b/substore/iriscloud-rename.js @@ -6,7 +6,7 @@ * - 去掉 SZ/HZ/[Edge] 入口标签,保留倍率,压掉供应商和入口线路特征 */ // noinspection JSUnusedGlobalSymbols -const COMMON_SCRIPT_VERSION = '20260608-1'; +const COMMON_SCRIPT_VERSION = '20260702-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 @@ -45,19 +45,9 @@ async function operator(proxies) { return ''; }; - const multiplierOf = name => { - const match = name.match(/\b(\d+(?:\.\d+)?)(?:\s*[xX]|倍)\b/i); - return match ? `${match[1]}X` : ''; - }; - - const multiplierValueOf = multiplier => ( - multiplier ? Number(multiplier.replace(/X$/i, '')) : Number.NEGATIVE_INFINITY - ); - const cleanCoreName = name => { const { text } = utils.splitLeadingOrInlineFlag(name); - return text - .replace(/\s+-\s*Iris Cloud(?:\s+\d{2})?\s*$/i, '') + return utils.stripSubscriptionSuffix(text, 'Iris Cloud') .replace(/^\s*(?:SZ|HZ)\s+\d{1,2}\s+/i, '') .replace(/^\s*\[Edge]\s*/i, '') .trim(); @@ -89,7 +79,7 @@ async function operator(proxies) { originalIndex, regionPrefix, regionCode, - multiplier: multiplierOf(coreName), + multiplier: utils.multiplierOf(coreName), flag: utils.regionCodeToFlag(regionCode) }; }); @@ -109,23 +99,22 @@ async function operator(proxies) { const firstRegionOrder = regionOrder[firstItem.regionPrefix]; const secondRegionOrder = regionOrder[secondItem.regionPrefix]; if (firstRegionOrder !== secondRegionOrder) return firstRegionOrder - secondRegionOrder; - const firstMultiplierValue = multiplierValueOf(firstItem.multiplier); - const secondMultiplierValue = multiplierValueOf(secondItem.multiplier); + const firstMultiplierValue = utils.multiplierValueOf(firstItem.multiplier); + const secondMultiplierValue = utils.multiplierValueOf(secondItem.multiplier); if (firstMultiplierValue !== secondMultiplierValue) { return firstMultiplierValue - secondMultiplierValue; } return firstItem.originalIndex - secondItem.originalIndex; }); - const regionCount = Object.create(null); + const nextSequence = utils.createSequenceCounter(); return normalizedItems.map(item => { if (!item.regionPrefix) return item.proxy; - regionCount[item.regionPrefix] = (regionCount[item.regionPrefix] || 0) + 1; item.proxy.name = [ item.flag, item.regionPrefix, - String(regionCount[item.regionPrefix]).padStart(2, '0'), + nextSequence(item.regionPrefix), item.multiplier ].filter(Boolean).join(' ').trim(); return item.proxy; diff --git a/substore/liangxin-rename.js b/substore/liangxin-rename.js index 6155991..9859ada 100644 --- a/substore/liangxin-rename.js +++ b/substore/liangxin-rename.js @@ -3,7 +3,7 @@ * 负责去掉高速、BGP、CMCU 等 LiangXin 线路描述,保留地区、序号和倍率。 */ // noinspection JSUnusedGlobalSymbols -const COMMON_SCRIPT_VERSION = '20260608-1'; +const COMMON_SCRIPT_VERSION = '20260702-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 diff --git a/substore/peiqian-rename.js b/substore/peiqian-rename.js index b11ad1e..53fede8 100644 --- a/substore/peiqian-rename.js +++ b/substore/peiqian-rename.js @@ -3,7 +3,7 @@ * 负责清理推荐、用途、hy2、倍率、线路特征和提供商等 PeiQian 节点文案。 */ // noinspection JSUnusedGlobalSymbols -const COMMON_SCRIPT_VERSION = '20260608-1'; +const COMMON_SCRIPT_VERSION = '20260702-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 @@ -44,11 +44,10 @@ async function operator(proxies) { .replace(/\s*\|\s*避免晚高峰使用/gi, '') .replace(/\s*\|\s*(?:电信联通移动|电信联通|移动联通|三网)推荐(?:\s*-\s*hy2)?/gi, '') .replace(/\s*\|\s*高速专线(?:推荐)?(?:\s*-\s*hy2)?/gi, '') - .replace(/-\s*(\d+(?:\.\d+)?)倍/gi, ' $1x') - .replace(/(\d+(?:\.\d+)?)倍/gi, '$1x') .replace(/\s*\[(?:专线|移联|电联|三网|HY2)(?:\s+(?:专线|移联|电联|三网|HY2))*\]/gi, '') .replace(/\s+\[/g, ' [') .replace(/\s{2,}/g, ' '); + proxy.name = utils.normalizeMultiplierText(proxy.name); proxy.name = utils.normalizeLeadingFlagSpacing(proxy.name); return proxy; diff --git a/substore/rename.js b/substore/rename.js index 2869aac..c779f63 100644 --- a/substore/rename.js +++ b/substore/rename.js @@ -5,7 +5,7 @@ * - 基于归一化后的完整名称做精准去重 */ // noinspection JSUnusedGlobalSymbols -const COMMON_SCRIPT_VERSION = '20260608-1'; +const COMMON_SCRIPT_VERSION = '20260702-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 diff --git a/substore/sntp-rename.js b/substore/sntp-rename.js index fd669ca..5962774 100644 --- a/substore/sntp-rename.js +++ b/substore/sntp-rename.js @@ -4,7 +4,7 @@ * 维护说明:仅需修改 featureMap 字典即可,底部逻辑永远无需改动。 */ // noinspection JSUnusedGlobalSymbols -const COMMON_SCRIPT_VERSION = '20260608-1'; +const COMMON_SCRIPT_VERSION = '20260702-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 diff --git a/substore/yinyun-rename.js b/substore/yinyun-rename.js index 98d983c..419dd15 100644 --- a/substore/yinyun-rename.js +++ b/substore/yinyun-rename.js @@ -5,7 +5,7 @@ * - 按地区出现顺序连续编号,方便全局 rename.js 再追加订阅后缀 */ // noinspection JSUnusedGlobalSymbols -const COMMON_SCRIPT_VERSION = '20260608-1'; +const COMMON_SCRIPT_VERSION = '20260702-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 @@ -49,7 +49,6 @@ async function operator(proxies) { ['葡萄牙', 'PT'], ['拉脱维亚', 'LV'], ['加拿大', 'CA'], - ['加拿大 01', 'CA'], ['澳洲', 'AU'], ['捷克', 'CZ'], ['南非', 'ZA'], @@ -68,6 +67,11 @@ async function operator(proxies) { ['以色列', 'IL'], ['意大利', 'IT'], ['西班牙', 'ES'], + ['乌克兰', 'UA'], + ['德国', 'DE'], + ['英国', 'UK'], + ['印度', 'IN'], + ['泰国', 'TH'], ['香港', 'HK'], ['美国', 'US'], ['日本', 'JP'], @@ -76,49 +80,45 @@ async function operator(proxies) { ]; const regionResolver = utils.createRegionResolver(regionAliases); - const stripExistingSuffix = name => String(name) - .replace(/\s+-\s*YinYun(?:\s+\d{2})?\s*$/i, '') + const isInfoNode = name => /(?:永久官网|TG频道|YinYun\.Ltd|YINYUNLTD)/i.test(name); + + const stripBackupLabel = name => String(name) + .replace(/\s*(?:ᴮᴬᴷ|BAK|BACKUP)\s*$/iu, '') .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); + return regionResolver.aliasRegionPrefixOf(name) || utils.leadingRegionPrefixOf(name); }; - const regionCount = Object.create(null); + const nextSequence = utils.createSequenceCounter(); - return proxies.map(proxy => { + return proxies.filter(proxy => ( + typeof proxy.name !== 'string' || !isInfoNode(proxy.name) + )).map(proxy => { if (typeof proxy.name !== 'string') { return proxy; } - const cleanName = stripExistingSuffix(proxy.name); + const cleanName = utils.stripSubscriptionSuffix(proxy.name, 'YinYun'); const { text } = utils.splitLeadingOrInlineFlag(cleanName); - const coreName = stripLineType(text); + const coreName = stripLineType(stripBackupLabel(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 + nextSequence(regionPrefix) ].filter(Boolean).join(' ').trim(); return proxy; diff --git a/substore/yuetong-rename.js b/substore/yuetong-rename.js index d2a3917..72ab794 100644 --- a/substore/yuetong-rename.js +++ b/substore/yuetong-rename.js @@ -4,7 +4,7 @@ * 按地区稳定归并后重新连续编号,避免专属/普通节点撞号。 */ // noinspection JSUnusedGlobalSymbols -const COMMON_SCRIPT_VERSION = '20260608-1'; +const COMMON_SCRIPT_VERSION = '20260702-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 @@ -141,12 +141,11 @@ async function operator(proxies) { return firstItem.originalIndex - secondItem.originalIndex; }); - const regionCount = Object.create(null); + const nextSequence = utils.createSequenceCounter(); return normalizedItems.map(item => { if (!item.regionCode) return item.proxy; - regionCount[item.regionCode] = (regionCount[item.regionCode] || 0) + 1; - item.proxy.name = withFreshRegionSequence(item.proxy.name, regionCount[item.regionCode]); + item.proxy.name = withFreshRegionSequence(item.proxy.name, nextSequence(item.regionCode)); return item.proxy; }); }