diff --git a/substore/rename.js b/substore/rename.js index d779888..873494d 100644 --- a/substore/rename.js +++ b/substore/rename.js @@ -584,6 +584,11 @@ async function operator(proxies) { regionAliases.map(([, replacement]) => replacement.split(/\s+/)[0]) ); Object.values(flagCodeOverrides).forEach(code => knownRegionCodes.add(code)); + const knownCityCodes = new Set( + regionAliases + .map(([, replacement]) => replacement.split(/\s+/)[1]) + .filter(code => /^[A-Z]{3}$/.test(code || '')) + ); const escapeRegex = utils.escapeRegex; const leadingFlagRegex = /^([\u{1F1E6}-\u{1F1FF}]{2}|🏴‍☠️)\s*/u; @@ -594,6 +599,16 @@ async function operator(proxies) { const flagToRegionCode = flag => utils.flagToRegionCode(flag, flagCodeOverrides); + const providerAliases = [ + ['搬瓦工', 'BWH'], + ['Bandwagon', 'BWH'], + ['BWG', 'BWH'], + ['BWH', 'BWH'], + ['SkyStroll', 'SkyStroll'], + ['ISIF', 'ISIF'], + ['RFC', 'RFC'] + ]; + const stripExistingSuffix = (name, subName) => { if (!subName) return name.trim(); const suffixRegex = suffixRegexCache[subName] || ( @@ -653,6 +668,59 @@ async function operator(proxies) { return rest ? `${regionCode} Pro-${rest}` : regionCode; }; + const providerOf = name => { + const cleanName = String(name); + for (const [alias, provider] of providerAliases) { + const aliasRegex = /^[A-Za-z0-9.]+$/.test(alias) + ? new RegExp(`(^|[^A-Za-z0-9])${escapeRegex(alias)}([^A-Za-z0-9]|$)`, 'i') + : new RegExp(escapeRegex(alias), 'i'); + if (aliasRegex.test(cleanName)) return provider; + } + return ''; + }; + + const embeddedRegionPrefixOf = (name, fallbackRegionCode) => { + const cleanName = String(name); + const candidates = []; + if (fallbackRegionCode) { + candidates.push({ index: -1, region: fallbackRegionCode }); + } + + for (const [alias, replacement, aliasLower] of sortedRegionAliases) { + const aliasRegex = /^[A-Za-z0-9 ]+$/.test(alias) + ? new RegExp(`(^|[^A-Za-z])${escapeRegex(alias)}(?=$|[^A-Za-z])`, 'i') + : new RegExp(escapeRegex(alias), 'i'); + const match = cleanName.match(aliasRegex); + if (!match) continue; + + const matchIndex = match.index + (match[1] ? match[1].length : 0); + candidates.push({ index: matchIndex, region: replacement }); + } + + for (const match of cleanName.matchAll(/(^|[^A-Za-z])([A-Z]{2})(?:\d{1,2})?(?=$|[^A-Za-z])/g)) { + const regionCode = match[2].toUpperCase(); + if (!knownRegionCodes.has(regionCode)) continue; + + candidates.push({ + index: match.index + (match[1] ? match[1].length : 0), + region: regionCode + }); + } + + candidates.sort((firstCandidate, secondCandidate) => firstCandidate.index - secondCandidate.index); + return candidates.length ? candidates[0].region : ''; + }; + + const normalizeProviderRegionName = (name, fallbackRegionCode) => { + if (/^[A-Z]{2}(?:\s+[A-Z]{3})?\s+\d{2}(?=$|\s)/.test(name)) return name; + + const provider = providerOf(name); + if (!provider) return name; + + const regionPrefix = embeddedRegionPrefixOf(name, fallbackRegionCode); + return regionPrefix ? `${regionPrefix} ${provider}` : name; + }; + const simplifyCoreName = name => { return name .replace(/[\u{1F300}-\u{1FAFF}\u{2600}-\u{27BF}\uFE0F]/gu, '') @@ -686,9 +754,14 @@ async function operator(proxies) { if (!sequenceMatch) return null; const prefix = sequenceMatch[1] || ''; - const regionCode = sequenceMatch[2]; + let regionCode = sequenceMatch[2]; const sequenceNumber = sequenceMatch[3] || ''; - const rest = sequenceMatch[4] || ''; + let rest = sequenceMatch[4] || ''; + const cityCodeMatch = regionCode.match(/^([A-Z]{2})\s+([A-Z]{3})$/); + if (cityCodeMatch && !knownCityCodes.has(cityCodeMatch[2])) { + regionCode = cityCodeMatch[1]; + rest = ` ${cityCodeMatch[2]}${rest}`; + } return { prefix, @@ -777,6 +850,8 @@ async function operator(proxies) { .trim(); const flagRegionCode = flagToRegionCode(flag); + core = normalizeProviderRegionName(core, flagRegionCode); + core = moveProRegionToFront(core, flagRegionCode); if (