♻️ refactor(substore): 重构节点重命名脚本以支持更稳定的线路标识过滤与地区归并

This commit is contained in:
Orion
2026-06-03 11:58:20 +08:00
parent 00d3080f91
commit 37d257af6d
2 changed files with 134 additions and 5 deletions
+20 -1
View File
@@ -716,6 +716,25 @@ function operator(proxies) {
.trim();
};
const withInferredRegionSequence = sequenceInfo => {
const featureSequenceMatch = sequenceInfo.rest.match(/^\s+(专属|家宽|AI|高速|专线)\s+(\d{1,2})(?!\d)(?!\s*(?:[xX倍]|\.\d))(.*)$/i);
if (!featureSequenceMatch) return withRegionSequence(sequenceInfo, '01');
const featureName = featureSequenceMatch[1].toUpperCase() === 'AI'
? 'AI'
: featureSequenceMatch[1];
const inferredSequence = featureSequenceMatch[2].padStart(2, '0');
const remainingRest = featureSequenceMatch[3] || '';
return withRegionSequence(
{
...sequenceInfo,
rest: ` ${featureName}${remainingRest}`
},
inferredSequence
);
};
const normalizeBilingualName = name => {
const bilingualMatch = name.match(/^(.+?)\s*\s*(.+)$/);
if (!bilingualMatch) return name;
@@ -802,7 +821,7 @@ function operator(proxies) {
const sequenceInfo = sequenceInfos[proxyIndex];
const needsLeadingSequence = sequenceInfo && !sequenceInfo.sequenceNumber;
const coreName = needsLeadingSequence
? withRegionSequence(sequenceInfo, '01')
? withInferredRegionSequence(sequenceInfo)
: normalizedCoreName;
const baseName = subName ? `${coreName} - ${subName}` : coreName;
+114 -4
View File
@@ -1,21 +1,131 @@
/**
* YueTong 专用节点清理脚本
* 负责去掉“悦·”品牌前缀、钻石等装饰,以及压短 AI 解锁文案。
* 负责去掉“悦·”品牌前缀、钻石、专属/家宽等线路标识,以及压短 AI 解锁文案。
* 保留线路标识后面的序号,并按地区稳定归并。
*/
// noinspection JSUnusedGlobalSymbols
function operator(proxies) {
return proxies.map(proxy => {
const regionAliases = [
['Hong Kong', 'HK'],
['香港', 'HK'],
['HK', 'HK'],
['Japan', 'JP'],
['日本', 'JP'],
['JP', 'JP'],
['Singapore', 'SG'],
['新加坡', 'SG'],
['狮城', 'SG'],
['SG', 'SG'],
['Taiwan', 'TW'],
['台湾', 'TW'],
['台灣', 'TW'],
['TW', 'TW'],
['United States', 'US'],
['America', 'US'],
['USA', 'US'],
['美国', 'US'],
['美國', 'US'],
['美西', 'US'],
['美东', 'US'],
['美東', 'US'],
['US', 'US'],
['Korea', 'KR'],
['韩国', 'KR'],
['韓國', 'KR'],
['KR', 'KR'],
['United Kingdom', 'UK'],
['Britain', 'UK'],
['英国', 'UK'],
['英國', 'UK'],
['UK', 'UK'],
['Germany', 'DE'],
['德国', 'DE'],
['德國', 'DE'],
['DE', 'DE'],
['Netherlands', 'NL'],
['荷兰', 'NL'],
['荷蘭', 'NL'],
['NL', 'NL']
];
const sortedRegionAliases = regionAliases
.map(([alias, replacement]) => [alias, replacement, alias.toLowerCase()])
.sort((firstAlias, secondAlias) => secondAlias[0].length - firstAlias[0].length);
const leadingFlagRegex = /^([\u{1F1E6}-\u{1F1FF}]{2})\s*/u;
const flagToRegionCode = flag => {
if (!flag) return '';
const codePoints = [...flag].map(char => char.codePointAt(0));
if (
codePoints.length !== 2 ||
codePoints.some(codePoint => codePoint < 0x1F1E6 || codePoint > 0x1F1FF)
) {
return '';
}
const regionCode = codePoints
.map(codePoint => String.fromCharCode(codePoint - 0x1F1E6 + 65))
.join('');
return regionCode === 'GB' ? 'UK' : regionCode;
};
const regionCodeOf = name => {
const leadingFlagMatch = name.match(leadingFlagRegex);
if (leadingFlagMatch) return flagToRegionCode(leadingFlagMatch[1]);
const nameLower = name.toLowerCase();
for (const [alias, replacement, aliasLower] of sortedRegionAliases) {
if (/^[A-Z]{2}$/.test(alias)) {
const codeMatch = name.match(new RegExp(`(^|[^A-Za-z])${alias}([^A-Za-z]|$)`, 'i'));
if (codeMatch) return replacement;
continue;
}
if (nameLower.includes(aliasLower)) return replacement;
}
return '';
};
const normalizedItems = proxies.map((proxy, originalIndex) => {
if (typeof proxy.name !== 'string') {
return proxy;
return {
proxy,
originalIndex,
regionCode: ''
};
}
proxy.name = proxy.name
.replace(/悦[·・]\s*/g, '')
.replace(/AI解锁/g, 'AI')
.replace(/\s*(?:专属|家宽|高速|专线)(?:\s*(\d+)(?!\s*(?:[xX倍]|\.\d)))?/g, '$1')
.replace(/💎/gu, '')
.replace(/([\u4e00-\u9fff])AI\b/g, '$1 AI')
.replace(/\s{2,}/g, ' ')
.trim();
return proxy;
return {
proxy,
originalIndex,
regionCode: regionCodeOf(proxy.name)
};
});
const regionOrder = Object.create(null);
const groupOrder = normalizedItems.map(item => {
if (!item.regionCode) return item.originalIndex;
if (regionOrder[item.regionCode] === undefined) {
regionOrder[item.regionCode] = item.originalIndex;
}
return regionOrder[item.regionCode];
});
normalizedItems.sort((firstItem, secondItem) => {
const firstGroupOrder = groupOrder[firstItem.originalIndex];
const secondGroupOrder = groupOrder[secondItem.originalIndex];
if (firstGroupOrder !== secondGroupOrder) return firstGroupOrder - secondGroupOrder;
return firstItem.originalIndex - secondItem.originalIndex;
});
return normalizedItems.map(item => item.proxy);
}