feat: 增加对多个国家的支持并更新相关测试用例

This commit is contained in:
QLHazyCoder
2026-05-04 14:04:38 +08:00
parent 2fd51e57a9
commit 104e629394
6 changed files with 63 additions and 51 deletions
+4 -4
View File
@@ -993,7 +993,7 @@ function normalizeHeroSmsCountryFallback(value = []) {
} }
} }
if (!Number.isFinite(countryId) || countryId <= 0 || !HERO_SMS_SUPPORTED_COUNTRY_ID_SET.has(String(countryId)) || seenIds.has(countryId)) { if (!Number.isFinite(countryId) || countryId <= 0 || seenIds.has(countryId)) {
continue; continue;
} }
seenIds.add(countryId); seenIds.add(countryId);
@@ -1128,7 +1128,7 @@ function normalizeFiveSimCountryId(value, fallback = FIVE_SIM_COUNTRY_ID) {
? rootScope.PhoneSmsFiveSimProvider.normalizeFiveSimCountryId(value, '') ? rootScope.PhoneSmsFiveSimProvider.normalizeFiveSimCountryId(value, '')
: String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, ''); : String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '');
const normalized = String(rawNormalized || '').trim().toLowerCase(); const normalized = String(rawNormalized || '').trim().toLowerCase();
if (FIVE_SIM_SUPPORTED_COUNTRY_ID_SET.has(normalized)) { if (normalized) {
return normalized; return normalized;
} }
const fallbackSource = fallback === undefined || fallback === null ? FIVE_SIM_COUNTRY_ID : fallback; const fallbackSource = fallback === undefined || fallback === null ? FIVE_SIM_COUNTRY_ID : fallback;
@@ -1136,7 +1136,7 @@ function normalizeFiveSimCountryId(value, fallback = FIVE_SIM_COUNTRY_ID) {
if (!normalizedFallback) { if (!normalizedFallback) {
return ''; return '';
} }
return FIVE_SIM_SUPPORTED_COUNTRY_ID_SET.has(normalizedFallback) ? normalizedFallback : FIVE_SIM_COUNTRY_ID; return normalizedFallback || FIVE_SIM_COUNTRY_ID;
} }
function normalizeFiveSimCountryCode(value = '', fallback = 'thailand') { function normalizeFiveSimCountryCode(value = '', fallback = 'thailand') {
@@ -2225,7 +2225,7 @@ function normalizePersistentSettingValue(key, value) {
return normalizeHeroSmsMaxPrice(value); return normalizeHeroSmsMaxPrice(value);
case 'heroSmsCountryId': { case 'heroSmsCountryId': {
const parsed = Math.floor(Number(value)); const parsed = Math.floor(Number(value));
if (Number.isFinite(parsed) && HERO_SMS_SUPPORTED_COUNTRY_ID_SET.has(String(parsed))) { if (Number.isFinite(parsed) && parsed > 0) {
return parsed; return parsed;
} }
return HERO_SMS_COUNTRY_ID; return HERO_SMS_COUNTRY_ID;
+8 -8
View File
@@ -15,6 +15,10 @@
const SUPPORTED_COUNTRY_ITEMS = Object.freeze([ const SUPPORTED_COUNTRY_ITEMS = Object.freeze([
{ id: 'indonesia', label: '印度尼西亚 (Indonesia)' }, { id: 'indonesia', label: '印度尼西亚 (Indonesia)' },
{ id: 'thailand', label: '泰国 (Thailand)' }, { id: 'thailand', label: '泰国 (Thailand)' },
{ id: 'england', label: '英国 (England)' },
{ id: 'usa', label: '美国 (United States)' },
{ id: 'japan', label: '日本 (Japan)' },
{ id: 'germany', label: '德国 (Germany)' },
{ id: 'vietnam', label: '越南 (Vietnam)' }, { id: 'vietnam', label: '越南 (Vietnam)' },
]); ]);
const SUPPORTED_COUNTRY_ID_SET = new Set(SUPPORTED_COUNTRY_ITEMS.map((item) => item.id)); const SUPPORTED_COUNTRY_ID_SET = new Set(SUPPORTED_COUNTRY_ITEMS.map((item) => item.id));
@@ -115,7 +119,7 @@
function normalizeFiveSimCountryId(value, fallback = DEFAULT_COUNTRY_ID) { function normalizeFiveSimCountryId(value, fallback = DEFAULT_COUNTRY_ID) {
const normalized = String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, ''); const normalized = String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '');
if (SUPPORTED_COUNTRY_ID_SET.has(normalized)) { if (normalized) {
return normalized; return normalized;
} }
const fallbackSource = fallback === undefined || fallback === null ? DEFAULT_COUNTRY_ID : fallback; const fallbackSource = fallback === undefined || fallback === null ? DEFAULT_COUNTRY_ID : fallback;
@@ -123,7 +127,7 @@
if (!normalizedFallback) { if (!normalizedFallback) {
return ''; return '';
} }
return SUPPORTED_COUNTRY_ID_SET.has(normalizedFallback) ? normalizedFallback : DEFAULT_COUNTRY_ID; return normalizedFallback || DEFAULT_COUNTRY_ID;
} }
function getCountryIdFromPayload(record = {}, fallback = DEFAULT_COUNTRY_ID) { function getCountryIdFromPayload(record = {}, fallback = DEFAULT_COUNTRY_ID) {
@@ -414,12 +418,8 @@
].filter(Boolean).join(' '), ].filter(Boolean).join(' '),
}; };
}) })
.filter((entry) => entry && SUPPORTED_COUNTRY_ID_SET.has(String(entry.id))) .filter(Boolean)
.sort((left, right) => { .sort((left, right) => String(left.label || '').localeCompare(String(right.label || '')));
const leftIndex = SUPPORTED_COUNTRY_ITEMS.findIndex((item) => item.id === String(left.id));
const rightIndex = SUPPORTED_COUNTRY_ITEMS.findIndex((item) => item.id === String(right.id));
return leftIndex - rightIndex;
});
} }
function collectPriceEntries(payload, entries = []) { function collectPriceEntries(payload, entries = []) {
+22 -29
View File
@@ -516,6 +516,11 @@ const DEFAULT_HERO_SMS_ACQUIRE_PRIORITY = HERO_SMS_ACQUIRE_PRIORITY_COUNTRY;
const HERO_SMS_SUPPORTED_COUNTRY_ITEMS = Object.freeze([ const HERO_SMS_SUPPORTED_COUNTRY_ITEMS = Object.freeze([
{ id: 6, chn: '印度尼西亚', eng: 'Indonesia' }, { id: 6, chn: '印度尼西亚', eng: 'Indonesia' },
{ id: 52, chn: '泰国', eng: 'Thailand' }, { id: 52, chn: '泰国', eng: 'Thailand' },
{ id: 187, chn: '美国(物理)', eng: 'USA' },
{ id: 16, chn: '英国', eng: 'United Kingdom' },
{ id: 151, chn: '日本', eng: 'Japan' },
{ id: 43, chn: '德国', eng: 'Germany' },
{ id: 73, chn: '法国', eng: 'France' },
{ id: 10, chn: '越南', eng: 'Vietnam' }, { id: 10, chn: '越南', eng: 'Vietnam' },
]); ]);
const HERO_SMS_SUPPORTED_COUNTRY_ID_SET = new Set(HERO_SMS_SUPPORTED_COUNTRY_ITEMS.map((item) => String(item.id))); const HERO_SMS_SUPPORTED_COUNTRY_ID_SET = new Set(HERO_SMS_SUPPORTED_COUNTRY_ITEMS.map((item) => String(item.id)));
@@ -759,11 +764,17 @@ const DEFAULT_FIVE_SIM_COUNTRY_LABEL = '越南 (Vietnam)';
const FIVE_SIM_SUPPORTED_COUNTRY_ITEMS = Object.freeze([ const FIVE_SIM_SUPPORTED_COUNTRY_ITEMS = Object.freeze([
{ id: 'indonesia', chn: '印度尼西亚', eng: 'Indonesia', searchText: 'indonesia 印度尼西亚 印尼 Indonesia ID +62' }, { id: 'indonesia', chn: '印度尼西亚', eng: 'Indonesia', searchText: 'indonesia 印度尼西亚 印尼 Indonesia ID +62' },
{ id: 'thailand', chn: '泰国', eng: 'Thailand', searchText: 'thailand 泰国 Thailand TH +66' }, { id: 'thailand', chn: '泰国', eng: 'Thailand', searchText: 'thailand 泰国 Thailand TH +66' },
{ id: 'england', chn: '英国', eng: 'England', searchText: 'england 英国 England UK GB United Kingdom +44' },
{ id: 'usa', chn: '美国', eng: 'United States', searchText: 'usa 美国 United States US +1' },
{ id: 'japan', chn: '日本', eng: 'Japan', searchText: 'japan 日本 Japan JP +81' },
{ id: 'germany', chn: '德国', eng: 'Germany', searchText: 'germany 德国 Germany DE +49' },
{ id: 'vietnam', chn: '越南', eng: 'Vietnam', searchText: 'vietnam 越南 Vietnam VN +84' }, { id: 'vietnam', chn: '越南', eng: 'Vietnam', searchText: 'vietnam 越南 Vietnam VN +84' },
]); ]);
const FIVE_SIM_SUPPORTED_COUNTRY_ID_SET = new Set(FIVE_SIM_SUPPORTED_COUNTRY_ITEMS.map((item) => item.id)); const FIVE_SIM_SUPPORTED_COUNTRY_ID_SET = new Set(FIVE_SIM_SUPPORTED_COUNTRY_ITEMS.map((item) => item.id));
const NEX_SMS_FALLBACK_COUNTRY_ITEMS = Object.freeze([ const NEX_SMS_FALLBACK_COUNTRY_ITEMS = Object.freeze([
{ id: 1, label: 'Country #1', searchText: 'Country #1 1' }, { id: 1, label: 'Ukraine (#1)', searchText: 'Ukraine 1 UA' },
{ id: 6, label: 'Indonesia (#6)', searchText: 'Indonesia 6 ID' },
{ id: 7, label: 'Malaysia (#7)', searchText: 'Malaysia 7 MY' },
]); ]);
const DEFAULT_IP_PROXY_SERVICE = '711proxy'; const DEFAULT_IP_PROXY_SERVICE = '711proxy';
const SUPPORTED_IP_PROXY_SERVICES = ['711proxy', 'lumiproxy', 'iproyal', 'omegaproxy']; const SUPPORTED_IP_PROXY_SERVICES = ['711proxy', 'lumiproxy', 'iproyal', 'omegaproxy'];
@@ -3280,22 +3291,19 @@ function normalizePhoneSmsMaxPriceValue(value = '', provider = getSelectedPhoneS
} }
function normalizeFiveSimCountryId(value, fallback = DEFAULT_FIVE_SIM_COUNTRY_ID) { function normalizeFiveSimCountryId(value, fallback = DEFAULT_FIVE_SIM_COUNTRY_ID) {
const supportedCountryIds = typeof FIVE_SIM_SUPPORTED_COUNTRY_ID_SET !== 'undefined'
? FIVE_SIM_SUPPORTED_COUNTRY_ID_SET
: new Set(['indonesia', 'thailand', 'vietnam']);
const fallbackSource = fallback === undefined || fallback === null ? DEFAULT_FIVE_SIM_COUNTRY_ID : fallback; const fallbackSource = fallback === undefined || fallback === null ? DEFAULT_FIVE_SIM_COUNTRY_ID : fallback;
const normalizedFallback = String(fallbackSource).trim().toLowerCase().replace(/[^a-z0-9_-]+/g, ''); const normalizedFallback = String(fallbackSource).trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '');
const rawNormalized = typeof window !== 'undefined' && window.PhoneSmsFiveSimProvider?.normalizeFiveSimCountryId const rawNormalized = typeof window !== 'undefined' && window.PhoneSmsFiveSimProvider?.normalizeFiveSimCountryId
? window.PhoneSmsFiveSimProvider.normalizeFiveSimCountryId(value, '') ? window.PhoneSmsFiveSimProvider.normalizeFiveSimCountryId(value, '')
: String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, ''); : String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '');
const normalized = String(rawNormalized || '').trim().toLowerCase(); const normalized = String(rawNormalized || '').trim().toLowerCase();
if (supportedCountryIds.has(normalized)) { if (normalized) {
return normalized; return normalized;
} }
if (!normalizedFallback) { if (!normalizedFallback) {
return ''; return '';
} }
return supportedCountryIds.has(normalizedFallback) ? normalizedFallback : DEFAULT_FIVE_SIM_COUNTRY_ID; return normalizedFallback || DEFAULT_FIVE_SIM_COUNTRY_ID;
} }
function normalizeFiveSimCountryLabel(value = '', fallback = DEFAULT_FIVE_SIM_COUNTRY_LABEL) { function normalizeFiveSimCountryLabel(value = '', fallback = DEFAULT_FIVE_SIM_COUNTRY_LABEL) {
@@ -3384,15 +3392,12 @@ function normalizeFiveSimCountryFallbackList(value = []) {
} }
function normalizeHeroSmsCountryId(value, fallback = DEFAULT_HERO_SMS_COUNTRY_ID) { function normalizeHeroSmsCountryId(value, fallback = DEFAULT_HERO_SMS_COUNTRY_ID) {
const supportedCountryIds = typeof HERO_SMS_SUPPORTED_COUNTRY_ID_SET !== 'undefined'
? HERO_SMS_SUPPORTED_COUNTRY_ID_SET
: new Set(['6', '52', '10']);
const parsed = Math.floor(Number(value)); const parsed = Math.floor(Number(value));
if (Number.isFinite(parsed) && supportedCountryIds.has(String(parsed))) { if (Number.isFinite(parsed) && parsed > 0) {
return parsed; return parsed;
} }
const fallbackParsed = Math.floor(Number(fallback)); const fallbackParsed = Math.floor(Number(fallback));
return supportedCountryIds.has(String(fallbackParsed)) ? fallbackParsed : DEFAULT_HERO_SMS_COUNTRY_ID; return Number.isFinite(fallbackParsed) && fallbackParsed > 0 ? fallbackParsed : DEFAULT_HERO_SMS_COUNTRY_ID;
} }
function normalizeHeroSmsCountryLabel(value = '') { function normalizeHeroSmsCountryLabel(value = '') {
@@ -4011,10 +4016,7 @@ function normalizeHeroSmsCountryFallbackList(value = []) {
} }
} }
const supportedCountryIds = typeof HERO_SMS_SUPPORTED_COUNTRY_ID_SET !== 'undefined' if (!id || seen.has(id)) {
? HERO_SMS_SUPPORTED_COUNTRY_ID_SET
: new Set(['6', '52', '10']);
if (!id || !supportedCountryIds.has(String(id)) || seen.has(id)) {
return; return;
} }
seen.add(id); seen.add(id);
@@ -5221,12 +5223,8 @@ async function loadHeroSmsCountries() {
].filter(Boolean).join(' '), ].filter(Boolean).join(' '),
}; };
}) })
.filter((entry) => entry.id && FIVE_SIM_SUPPORTED_COUNTRY_ID_SET.has(String(entry.id))) .filter((entry) => entry.id)
.sort((left, right) => { .sort((left, right) => String(left.label || '').localeCompare(String(right.label || '')));
const leftIndex = FIVE_SIM_SUPPORTED_COUNTRY_ITEMS.findIndex((item) => item.id === String(left.id));
const rightIndex = FIVE_SIM_SUPPORTED_COUNTRY_ITEMS.findIndex((item) => item.id === String(right.id));
return leftIndex - rightIndex;
});
if (!optionItems.length) { if (!optionItems.length) {
throw new Error('empty country list'); throw new Error('empty country list');
} }
@@ -5263,12 +5261,8 @@ async function loadHeroSmsCountries() {
throw new Error('empty country list'); throw new Error('empty country list');
} }
const optionItems = countries const optionItems = countries
.filter((item) => HERO_SMS_SUPPORTED_COUNTRY_ID_SET.has(String(Math.floor(Number(item?.id)))) && (String(item?.eng || '').trim() || String(item?.chn || '').trim())) .filter((item) => Number(item?.id) > 0 && (String(item?.eng || '').trim() || String(item?.chn || '').trim()))
.sort((left, right) => { .sort((left, right) => String(left.eng || '').localeCompare(String(right.eng || '')))
const leftIndex = HERO_SMS_SUPPORTED_COUNTRY_ITEMS.findIndex((item) => String(item.id) === String(Math.floor(Number(left?.id))));
const rightIndex = HERO_SMS_SUPPORTED_COUNTRY_ITEMS.findIndex((item) => String(item.id) === String(Math.floor(Number(right?.id))));
return leftIndex - rightIndex;
})
.map((item) => { .map((item) => {
const id = normalizeHeroSmsCountryId(item.id); const id = normalizeHeroSmsCountryId(item.id);
const label = buildHeroSmsCountryDisplayLabel(item); const label = buildHeroSmsCountryDisplayLabel(item);
@@ -5657,8 +5651,7 @@ async function loadFiveSimCountries() {
throw new Error(`HTTP ${response.status}`); throw new Error(`HTTP ${response.status}`);
} }
const payload = await response.json(); const payload = await response.json();
const items = parseFiveSimCountriesPayload(payload) const items = parseFiveSimCountriesPayload(payload);
.filter((entry) => FIVE_SIM_SUPPORTED_COUNTRY_ID_SET.has(entry.code || entry.id));
applyOptions(items.length ? items : fallbackItems); applyOptions(items.length ? items : fallbackItems);
} catch (error) { } catch (error) {
console.warn('Failed to load 5sim countries:', error); console.warn('Failed to load 5sim countries:', error);
@@ -176,7 +176,7 @@ return {
assert.deepStrictEqual(api.normalizePersistentSettingValue('phoneSmsProviderOrder', ['nexsms', '5sim', 'nexsms']), ['nexsms', '5sim']); assert.deepStrictEqual(api.normalizePersistentSettingValue('phoneSmsProviderOrder', ['nexsms', '5sim', 'nexsms']), ['nexsms', '5sim']);
assert.equal(api.normalizePersistentSettingValue('fiveSimApiKey', ' demo-five '), ' demo-five '); assert.equal(api.normalizePersistentSettingValue('fiveSimApiKey', ' demo-five '), ' demo-five ');
assert.equal(api.normalizePersistentSettingValue('fiveSimProduct', ' OpenAI! '), 'openai'); assert.equal(api.normalizePersistentSettingValue('fiveSimProduct', ' OpenAI! '), 'openai');
assert.equal(api.normalizePersistentSettingValue('fiveSimCountryId', ' England! '), 'vietnam'); assert.equal(api.normalizePersistentSettingValue('fiveSimCountryId', ' England! '), 'england');
assert.equal(api.normalizePersistentSettingValue('fiveSimCountryId', ''), 'vietnam'); assert.equal(api.normalizePersistentSettingValue('fiveSimCountryId', ''), 'vietnam');
assert.equal(api.normalizePersistentSettingValue('fiveSimCountryLabel', ''), '越南 (Vietnam)'); assert.equal(api.normalizePersistentSettingValue('fiveSimCountryLabel', ''), '越南 (Vietnam)');
assert.equal(api.normalizePersistentSettingValue('fiveSimMaxPrice', '9.87654'), '9.8765'); assert.equal(api.normalizePersistentSettingValue('fiveSimMaxPrice', '9.87654'), '9.8765');
@@ -184,11 +184,11 @@ return {
assert.equal(api.normalizePersistentSettingValue('fiveSimOperator', ''), 'any'); assert.equal(api.normalizePersistentSettingValue('fiveSimOperator', ''), 'any');
assert.deepStrictEqual( assert.deepStrictEqual(
api.normalizePersistentSettingValue('fiveSimCountryFallback', [{ id: 'usa', label: 'USA' }, 'thailand:Thailand']), api.normalizePersistentSettingValue('fiveSimCountryFallback', [{ id: 'usa', label: 'USA' }, 'thailand:Thailand']),
[{ id: 'thailand', label: 'Thailand' }] [{ id: 'usa', label: 'USA' }, { id: 'thailand', label: 'Thailand' }]
); );
assert.deepStrictEqual( assert.deepStrictEqual(
api.normalizePersistentSettingValue('heroSmsCountryFallback', [{ id: 16, label: 'United Kingdom' }, { id: 52 }]), api.normalizePersistentSettingValue('heroSmsCountryFallback', [{ id: 16, label: 'United Kingdom' }, { id: 52 }]),
[{ id: 52, label: 'Country #52' }] [{ id: 16, label: 'United Kingdom' }, { id: 52, label: 'Country #52' }]
); );
assert.equal( assert.equal(
api.normalizePersistentSettingValue('accountRunHistoryHelperBaseUrl', 'http://127.0.0.1:17373/append-account-log'), api.normalizePersistentSettingValue('accountRunHistoryHelperBaseUrl', 'http://127.0.0.1:17373/append-account-log'),
+11 -7
View File
@@ -55,12 +55,17 @@ test('5sim provider maps countries and prices', async () => {
const prices = await provider.fetchPrices({}, { id: 'vietnam', label: 'Vietnam' }); const prices = await provider.fetchPrices({}, { id: 'vietnam', label: 'Vietnam' });
const entries = provider.collectPriceEntries(prices, []); const entries = provider.collectPriceEntries(prices, []);
assert.deepStrictEqual(countries.map((country) => country.id), ['indonesia', 'thailand', 'vietnam']); assert.equal(countries.length, 4);
assert.deepStrictEqual(countries[2], { assert.equal(countries.some((country) => country.id === 'england'), true);
id: 'vietnam', assert.equal(countries.some((country) => country.id === 'indonesia'), true);
label: '越南 (Vietnam)', assert.deepStrictEqual(
searchText: 'vietnam 越南 (Vietnam) Vietnam VN +84', countries.find((country) => country.id === 'vietnam'),
}); {
id: 'vietnam',
label: '越南 (Vietnam)',
searchText: 'vietnam 越南 (Vietnam) Vietnam VN +84',
}
);
assert.equal(requests[1].url.searchParams.get('country'), 'vietnam'); assert.equal(requests[1].url.searchParams.get('country'), 'vietnam');
assert.equal(requests[1].url.searchParams.get('product'), 'openai'); assert.equal(requests[1].url.searchParams.get('product'), 'openai');
assert.deepStrictEqual(entries, [{ cost: 10, count: 2, inStock: true }]); assert.deepStrictEqual(entries, [{ cost: 10, count: 2, inStock: true }]);
@@ -283,4 +288,3 @@ test('5sim provider reports purchase rate limit separately from no-number countr
] ]
); );
}); });
@@ -144,6 +144,21 @@ return { btnHeroSmsCountryMenu, updateHeroSmsCountryMenuSummary };
assert.equal(api.btnHeroSmsCountryMenu.textContent, '\u672a\u9009\u62e9 (0/3)'); assert.equal(api.btnHeroSmsCountryMenu.textContent, '\u672a\u9009\u62e9 (0/3)');
}); });
test('live phone country sources are not hard-filtered down to the reduced country whitelist', () => {
assert.doesNotMatch(
sidepanelSource,
/\.filter\(\(entry\) => entry\.id && FIVE_SIM_SUPPORTED_COUNTRY_ID_SET\.has\(String\(entry\.id\)\)\)/
);
assert.doesNotMatch(
sidepanelSource,
/\.filter\(\(item\) => HERO_SMS_SUPPORTED_COUNTRY_ID_SET\.has\(String\(Math\.floor\(Number\(item\?\.id\)\)\)\)/
);
assert.doesNotMatch(
sidepanelSource,
/\.filter\(\(entry\) => FIVE_SIM_SUPPORTED_COUNTRY_ID_SET\.has\(entry\.code \|\| entry\.id\)\)/
);
});
test('removeHeroSmsCountryFromOrder clears the selected country and triggers a silent save', async () => { test('removeHeroSmsCountryFromOrder clears the selected country and triggers a silent save', async () => {
const api = new Function(` const api = new Function(`
let heroSmsCountrySelectionOrder = [52, 6]; let heroSmsCountrySelectionOrder = [52, 6];