🐛 fix(substore): 修复重命名脚本中的节点序号与基础名称逻辑

This commit is contained in:
Orion
2026-06-03 09:17:55 +08:00
parent e6861f53ef
commit 0dafef595a
+232 -6
View File
@@ -7,7 +7,11 @@
function operator(proxies) {
const globalCount = Object.create(null);
const currentCount = Object.create(null);
const serialCount = Object.create(null);
const currentSerial = Object.create(null);
const countryOrder = Object.create(null);
const baseNames = new Array(proxies.length);
const renamedItems = new Array(proxies.length);
const suffixRegexCache = Object.create(null);
const regionAliases = [
@@ -29,6 +33,7 @@ function operator(proxies) {
['United States Portland', 'US PDX'],
['United States Boston', 'US BOS'],
['United States Seattle', 'US SEA'],
['United States Hawaii', 'US HNL'],
['Australia Sydney', 'AU SYD'],
['Australia Melbourne', 'AU MEL'],
['Australia Brisbane', 'AU BNE'],
@@ -92,6 +97,7 @@ function operator(proxies) {
['USA Portland', 'US PDX'],
['USA Boston', 'US BOS'],
['USA Seattle', 'US SEA'],
['USA Hawaii', 'US HNL'],
['United Kingdom', 'UK'],
['United States', 'US'],
['South Africa', 'ZA'],
@@ -314,6 +320,7 @@ function operator(proxies) {
['美国拉斯维加斯', 'US LAS'],
['美国波特兰', 'US PDX'],
['美国波士顿', 'US BOS'],
['美国夏威夷', 'US HNL'],
['日本东京', 'JP TYO'],
['日本大阪', 'JP OSA'],
['日本名古屋', 'JP NGO'],
@@ -443,6 +450,7 @@ function operator(proxies) {
['德国', 'DE'],
['荷兰', 'NL'],
['法国', 'FR'],
['美西', 'US'],
['巴西', 'BR'],
['智利', 'CL'],
['澳门', 'MO'],
@@ -464,6 +472,7 @@ function operator(proxies) {
['捷克', 'CZ'],
['沙特', 'SA'],
['墨西哥', 'MX'],
['夏威夷', 'US HNL'],
['迪拜', 'AE DXB'],
['悉尼', 'AU SYD'],
['墨尔本', 'AU MEL'],
@@ -545,6 +554,9 @@ function operator(proxies) {
const knownRegionCodes = new Set(
regionAliases.map(([, replacement]) => replacement.split(/\s+/)[0])
);
const knownRegionPrefixes = new Set(
regionAliases.map(([, replacement]) => replacement)
);
Object.values(flagCodeOverrides).forEach(code => knownRegionCodes.add(code));
const escapeRegex = value => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
@@ -568,6 +580,26 @@ function operator(proxies) {
.join('');
};
const regionCodeToFlag = regionCode => {
if (!regionCode) return '';
const flagCode = regionCode === 'UK' ? 'GB' : regionCode;
if (!/^[A-Z]{2}$/.test(flagCode)) return '';
return flagCode
.split('')
.map(char => String.fromCodePoint(char.charCodeAt(0) - 65 + 0x1F1E6))
.join('');
};
const normalizedFlagForCore = (flag, coreName) => {
if (!flag) return '';
const regionPrefix = leadingRegionPrefixOf(coreName) || regionPrefixInName(coreName);
const regionCode = regionPrefix.split(/\s+/)[0];
return regionCodeToFlag(regionCode) || flag;
};
const stripExistingSuffix = (name, subName) => {
if (!subName) return name.trim();
const suffixRegex = suffixRegexCache[subName] || (
@@ -639,18 +671,171 @@ function operator(proxies) {
.replace(/[\u{1F300}-\u{1FAFF}\u{2600}-\u{27BF}\uFE0F]/gu, '')
.replace(/\u200d/g, '')
.replace(/\s*\|\s*流媒体/gi, '')
.replace(/-\s*(\d+(?:\.\d+)?)倍/gi, ' $1x')
.replace(/(\d+(?:\.\d+)?)倍/gi, '$1x')
.replace(/\b(\d+(?:\.\d+)?)X\b/g, '$1x')
.replace(/-\s*(\d+(?:\.\d+)?)倍/gi, ' $1X')
.replace(/(\d+(?:\.\d+)?)倍/gi, '$1X')
.replace(/\b(\d+(?:\.\d+)?)x\b/gi, '$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*\|\s*/g, '|')
.replace(/\s+\[/g, ' [')
.replace(/\s{2,}/g, ' ')
.trim();
};
const isProviderTag = tag => /^(JC|毕业)$/i.test(tag.trim());
const isRouteTag = tag => /^(三网\d*|COP|CM)$/i.test(tag.trim());
const splitPipeFields = name => name
.split('|')
.map(part => part.trim())
.filter(Boolean);
const cleanupPipeFields = name => {
const fields = splitPipeFields(name);
if (fields.length <= 1) return name.trim();
const [head, ...tailFields] = fields;
const keptFields = tailFields.filter(field => !isRouteTag(field));
return [head, ...keptFields].join('|').trim();
};
const leadingRegionPrefixOf = name => {
const match = name.match(/^([A-Z]{2})(?:\s+([A-Z]{3}))?(?=\s|$|-|\d|\|)/);
if (!match) return '';
const fullPrefix = match[2] ? `${match[1]} ${match[2]}` : match[1];
if (match[2] && knownRegionPrefixes.has(fullPrefix)) return fullPrefix;
return knownRegionCodes.has(match[1]) ? match[1] : '';
};
const hasAliasInName = (name, alias, aliasLower) => {
const nameLower = name.toLowerCase();
const aliasIndex = nameLower.indexOf(aliasLower);
if (aliasIndex === -1) return false;
if (/^[A-Za-z]/.test(alias)) {
const before = aliasIndex > 0 ? name[aliasIndex - 1] : '';
const after = name[aliasIndex + alias.length] || '';
return !/[A-Za-z]/.test(before) && !/[A-Za-z]/.test(after);
}
return true;
};
const regionPrefixInName = name => {
for (const [alias, replacement, aliasLower] of sortedRegionAliases) {
if (hasAliasInName(name, alias, aliasLower)) return replacement;
}
return leadingRegionPrefixOf(name);
};
const multiplierOf = name => {
const match = name.match(/\b(\d+(?:\.\d+)?X)\b/i);
return match ? match[1].toUpperCase() : '';
};
const compactHomeBroadbandName = name => {
if (!/家宽/.test(name)) return name;
const regionPrefix = /(?:Hinet|Seednet)/i.test(name) ? 'TW' : regionPrefixInName(name);
const multiplier = multiplierOf(name);
const compactFields = [regionPrefix, multiplier].filter(Boolean);
return compactFields.join(' ').trim();
};
const compactRegionOnlyName = name => {
const regionPrefix = regionPrefixInName(name);
return regionPrefix || name;
};
const compactKnownLineName = name => {
const cleanName = cleanupPipeFields(name);
const compactName = /家宽/.test(cleanName)
? compactHomeBroadbandName(cleanName)
: compactRegionOnlyName(cleanName);
return compactName
.replace(/\s*\|\s*/g, '|')
.replace(/\s{2,}/g, ' ')
.trim();
};
const serialGroupOf = coreName => {
const nameWithoutFlag = coreName.replace(leadingFlagRegex, '').trim();
const regionPrefix = leadingRegionPrefixOf(nameWithoutFlag) || regionPrefixInName(nameWithoutFlag);
if (!regionPrefix) return '';
return `${regionPrefix.split(/\s+/)[0]}|region`;
};
const appendSerialToCoreName = (coreName, serialNumber) => {
const fields = splitPipeFields(coreName);
const serialText = String(serialNumber).padStart(2, '0');
if (fields.length <= 1) return `${coreName} ${serialText}`;
const providers = [];
const bodyFields = [];
fields.forEach((field, fieldIndex) => {
if (fieldIndex > 0 && isProviderTag(field)) {
providers.push(field);
} else {
bodyFields.push(field);
}
});
const body = `${bodyFields.join('|')} ${serialText}`.trim();
return providers.length ? `${body}|${providers.join('|')}` : body;
};
const sortInfoOf = (coreName, originalIndex) => {
const nameWithoutFlag = coreName.replace(leadingFlagRegex, '').trim();
const regionPrefix = leadingRegionPrefixOf(nameWithoutFlag) || regionPrefixInName(nameWithoutFlag);
const [countryCode, cityCode = ''] = regionPrefix.split(/\s+/);
const multiplierMatch = nameWithoutFlag.match(/\b(\d+(?:\.\d+)?)X\b/i);
return {
originalIndex,
countryCode: countryCode || '',
cityCode,
regionPrefix,
multiplier: multiplierMatch ? Number(multiplierMatch[1]) : Number.NEGATIVE_INFINITY
};
};
const compareSortInfo = (firstInfo, secondInfo) => {
if (!firstInfo.countryCode || !secondInfo.countryCode) {
return firstInfo.originalIndex - secondInfo.originalIndex;
}
const firstCountryOrder = countryOrder[firstInfo.countryCode];
const secondCountryOrder = countryOrder[secondInfo.countryCode];
if (firstCountryOrder !== secondCountryOrder) {
return firstCountryOrder - secondCountryOrder;
}
if (firstInfo.multiplier !== secondInfo.multiplier) {
return firstInfo.multiplier - secondInfo.multiplier;
}
const firstCityRank = firstInfo.cityCode ? 1 : 0;
const secondCityRank = secondInfo.cityCode ? 1 : 0;
if (firstCityRank !== secondCityRank) {
return firstCityRank - secondCityRank;
}
if (firstInfo.regionPrefix !== secondInfo.regionPrefix) {
return firstInfo.regionPrefix < secondInfo.regionPrefix ? -1 : 1;
}
return firstInfo.originalIndex - secondInfo.originalIndex;
};
const normalizeBilingualName = name => {
const bilingualMatch = name.match(/^(.+?)\s*\s*(.+)$/);
if (!bilingualMatch) return name;
@@ -714,9 +899,10 @@ function operator(proxies) {
core = joinAlias(flagRegionCode, core);
}
core = simplifyCoreName(core);
core = compactKnownLineName(simplifyCoreName(core));
return flag ? `${flag} ${core}` : core;
const normalizedFlag = normalizedFlagForCore(flag, core);
return normalizedFlag ? `${normalizedFlag} ${core}` : core;
};
proxies.forEach((proxy, proxyIndex) => {
@@ -725,13 +911,53 @@ function operator(proxies) {
const coreName = stripExistingSuffix(proxyName, subName);
const normalizedCoreName = normalizeCoreName(coreName);
const baseName = subName ? `${normalizedCoreName} - ${subName}` : normalizedCoreName;
const serialGroup = serialGroupOf(normalizedCoreName);
const scopedSerialGroup = serialGroup ? `${serialGroup}|${subName}` : '';
const sortInfo = sortInfoOf(normalizedCoreName, proxyIndex);
if (sortInfo.countryCode && countryOrder[sortInfo.countryCode] === undefined) {
countryOrder[sortInfo.countryCode] = Object.keys(countryOrder).length;
}
baseNames[proxyIndex] = baseName;
renamedItems[proxyIndex] = {
normalizedCoreName,
subName,
scopedSerialGroup,
sortInfo
};
globalCount[baseName] = (globalCount[baseName] || 0) + 1;
if (scopedSerialGroup) {
serialCount[scopedSerialGroup] = (serialCount[scopedSerialGroup] || 0) + 1;
}
});
return proxies.map((proxy, proxyIndex) => {
const sortedProxyIndexes = proxies
.map((proxy, proxyIndex) => proxyIndex)
.sort((firstIndex, secondIndex) => {
return compareSortInfo(
renamedItems[firstIndex].sortInfo,
renamedItems[secondIndex].sortInfo
);
});
return sortedProxyIndexes.map(proxyIndex => {
const proxy = proxies[proxyIndex];
const baseName = baseNames[proxyIndex];
const renamedItem = renamedItems[proxyIndex];
if (renamedItem.scopedSerialGroup) {
currentSerial[renamedItem.scopedSerialGroup] = (
currentSerial[renamedItem.scopedSerialGroup] || 0
) + 1;
const serialCoreName = appendSerialToCoreName(
renamedItem.normalizedCoreName,
currentSerial[renamedItem.scopedSerialGroup]
);
proxy.name = renamedItem.subName ? `${serialCoreName} - ${renamedItem.subName}` : serialCoreName;
return proxy;
}
if (globalCount[baseName] > 1) {
currentCount[baseName] = (currentCount[baseName] || 0) + 1;