完善手机接码逻辑

This commit is contained in:
QLHazyCoder
2026-05-15 05:07:34 +08:00
parent a7ea261093
commit 22fc865f19
10 changed files with 475 additions and 61 deletions
@@ -81,6 +81,7 @@ test('background account history settings are normalized independently from hotm
extractFunction('normalizeSub2ApiGroupNames'),
extractFunction('normalizeBoundedIntegerSetting'),
extractFunction('normalizeLocalHttpBaseUrl'),
extractFunction('buildPersistentSettingsPayload'),
extractFunction('normalizePersistentSettingValue'),
].join('\n');
@@ -181,10 +182,16 @@ function normalizeCloudflareTempEmailReceiveMailbox(value) { return String(value
function normalizeCloudflareTempEmailDomain(value) { return String(value || '').trim(); }
function normalizeCloudflareTempEmailDomains(value) { return Array.isArray(value) ? value : []; }
function normalizeHotmailAccounts(value) { return Array.isArray(value) ? value : []; }
function resolveLegacyAutoStepDelaySeconds(value) {
return value && Object.prototype.hasOwnProperty.call(value, 'autoStepDelaySeconds')
? value.autoStepDelaySeconds
: undefined;
}
${bundle}
return {
normalizeAccountRunHistoryHelperBaseUrl,
normalizePersistentSettingValue,
buildPersistentSettingsPayload,
};
`)();
@@ -243,6 +250,8 @@ return {
assert.equal(api.normalizePersistentSettingValue('phoneSmsProvider', 'NEXSMS'), 'nexsms');
assert.equal(api.normalizePersistentSettingValue('phoneSmsProvider', 'unknown'), 'hero-sms');
assert.deepStrictEqual(api.normalizePersistentSettingValue('phoneSmsProviderOrder', ['nexsms', '5sim', 'nexsms']), ['nexsms', '5sim']);
assert.equal(api.normalizePersistentSettingValue('phoneSmsReuseEnabled', false), false);
assert.equal(api.normalizePersistentSettingValue('phoneSmsReuseEnabled', true), true);
assert.equal(api.normalizePersistentSettingValue('fiveSimApiKey', ' demo-five '), ' demo-five ');
assert.equal(api.normalizePersistentSettingValue('fiveSimProduct', ' OpenAI! '), 'openai');
assert.equal(api.normalizePersistentSettingValue('fiveSimCountryId', ' England! '), 'england');
@@ -335,4 +344,26 @@ return {
countryLabel: '',
}
);
const legacyReusePayload = api.buildPersistentSettingsPayload({
heroSmsReuseEnabled: false,
});
assert.equal(legacyReusePayload.phoneSmsReuseEnabled, false);
assert.equal(legacyReusePayload.heroSmsReuseEnabled, false);
const fiveSimReusePayload = api.buildPersistentSettingsPayload({
fiveSimReuseEnabled: false,
});
assert.equal(fiveSimReusePayload.phoneSmsReuseEnabled, false);
assert.equal(fiveSimReusePayload.heroSmsReuseEnabled, false);
const conflictingReusePayload = api.buildPersistentSettingsPayload({
heroSmsReuseEnabled: true,
fiveSimReuseEnabled: false,
});
assert.equal(conflictingReusePayload.phoneSmsReuseEnabled, true);
assert.equal(conflictingReusePayload.heroSmsReuseEnabled, true);
const newReusePayload = api.buildPersistentSettingsPayload({
phoneSmsReuseEnabled: false,
heroSmsReuseEnabled: true,
});
assert.equal(newReusePayload.phoneSmsReuseEnabled, false);
assert.equal(newReusePayload.heroSmsReuseEnabled, false);
});
@@ -15,6 +15,7 @@ test('background defaults enable free phone reuse switches', () => {
assert.match(defaultsBlock, /freePhoneReuseEnabled:\s*true/);
assert.match(defaultsBlock, /freePhoneReuseAutoEnabled:\s*true/);
assert.match(defaultsBlock, /phoneSmsReuseEnabled:\s*DEFAULT_HERO_SMS_REUSE_ENABLED/);
});
test('background free reusable phone setter does not depend on module-scoped phone flow constants', () => {
+44
View File
@@ -0,0 +1,44 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const source = fs.readFileSync('phone-sms/providers/registry.js', 'utf8');
function loadRegistry(root = {}) {
return new Function('self', `${source}; return self.PhoneSmsProviderRegistry;`)(root);
}
test('phone sms provider registry normalizes ids, order and labels consistently', () => {
const registry = loadRegistry({
PhoneSmsHeroSmsProvider: {
createProvider: (deps = {}) => ({ provider: 'hero-sms', deps }),
},
PhoneSmsFiveSimProvider: {
createProvider: (deps = {}) => ({ provider: '5sim', deps }),
},
});
assert.deepStrictEqual(registry.getProviderIds(), ['hero-sms', '5sim', 'nexsms']);
assert.equal(registry.normalizeProviderId(' NEXSMS '), 'nexsms');
assert.equal(registry.normalizeProviderId('unknown-provider'), 'hero-sms');
assert.equal(registry.getProviderLabel('nexsms'), 'NexSMS');
assert.equal(registry.getProviderDefinition('nexsms').moduleKey, 'PhoneSmsNexSmsProvider');
assert.deepStrictEqual(
registry.normalizeProviderOrder([
{ provider: 'nexsms' },
{ id: '5sim' },
{ value: 'hero-sms' },
'NEXSMS',
]),
['nexsms', '5sim', 'hero-sms']
);
assert.deepStrictEqual(
registry.normalizeProviderOrder([], ['nexsms', '5sim', 'nexsms']),
['nexsms', '5sim']
);
assert.deepStrictEqual(
registry.createProvider('5sim', { foo: 1 }),
{ provider: '5sim', deps: { foo: 1 } }
);
assert.throws(() => registry.createProvider('nexsms'), /Phone SMS provider module is not loaded: nexsms/);
});
+175 -3
View File
@@ -1948,9 +1948,181 @@ test('phone verification helper acquires a number from 5sim with fallback countr
assert.equal(requests[3].pathname, '/v1/user/buy/activation/england/any/openai');
});
test('phone verification helper rejects 5sim maxPrice with custom operator before buying', async () => {
const requests = [];
const helpers = api.createPhoneVerificationHelpers({
test('phone verification helper prefers phoneSmsReuseEnabled over legacy heroSmsReuseEnabled for 5sim acquisition', async () => {
const requests = [];
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url, options = {}) => {
const parsedUrl = new URL(url);
requests.push({
pathname: parsedUrl.pathname,
search: parsedUrl.searchParams,
headers: options?.headers || {},
});
if (parsedUrl.pathname === '/v1/guest/prices') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
openai: {
thailand: {
any: {
cost: 0.08,
count: 12,
},
},
},
}),
};
}
if (parsedUrl.pathname === '/v1/user/buy/activation/thailand/any/openai') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
id: 1234567,
phone: '+66880000000',
country: 'thailand',
country_name: 'Thailand',
product: 'openai',
}),
};
}
throw new Error(`Unexpected 5sim request: ${parsedUrl.pathname}`);
},
getState: async () => ({
phoneSmsProvider: '5sim',
fiveSimApiKey: 'five-token',
fiveSimCountryOrder: ['thailand'],
fiveSimOperator: 'any',
fiveSimProduct: 'openai',
phoneSmsReuseEnabled: false,
heroSmsReuseEnabled: true,
heroSmsActivationRetryRounds: 1,
}),
sendToContentScriptResilient: async () => ({}),
setState: async () => {},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
const activation = await helpers.requestPhoneActivation({
phoneSmsProvider: '5sim',
fiveSimApiKey: 'five-token',
fiveSimCountryOrder: ['thailand'],
fiveSimOperator: 'any',
fiveSimProduct: 'openai',
phoneSmsReuseEnabled: false,
heroSmsReuseEnabled: true,
heroSmsActivationRetryRounds: 1,
});
assert.deepStrictEqual(activation, {
activationId: '1234567',
phoneNumber: '+66880000000',
provider: '5sim',
serviceCode: 'openai',
countryId: 'thailand',
countryCode: 'thailand',
countryLabel: 'Thailand',
successfulUses: 0,
maxUses: 3,
});
assert.equal(requests[0].pathname, '/v1/guest/prices');
assert.equal(requests[1].pathname, '/v1/user/buy/activation/thailand/any/openai');
assert.equal(requests[1].search.get('reuse'), null);
});
test('phone verification helper treats fiveSimReuseEnabled as legacy-only when phoneSmsReuseEnabled is absent', async () => {
const requests = [];
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url, options = {}) => {
const parsedUrl = new URL(url);
requests.push({
pathname: parsedUrl.pathname,
search: parsedUrl.searchParams,
headers: options?.headers || {},
});
if (parsedUrl.pathname === '/v1/guest/prices') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
openai: {
thailand: {
any: {
cost: 0.08,
count: 12,
},
},
},
}),
};
}
if (parsedUrl.pathname === '/v1/user/buy/activation/thailand/any/openai') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
id: 1234568,
phone: '+66880000001',
country: 'thailand',
country_name: 'Thailand',
product: 'openai',
}),
};
}
throw new Error(`Unexpected 5sim request: ${parsedUrl.pathname}`);
},
getState: async () => ({
phoneSmsProvider: '5sim',
fiveSimApiKey: 'five-token',
fiveSimCountryOrder: ['thailand'],
fiveSimOperator: 'any',
fiveSimProduct: 'openai',
heroSmsReuseEnabled: true,
fiveSimReuseEnabled: false,
heroSmsActivationRetryRounds: 1,
}),
sendToContentScriptResilient: async () => ({}),
setState: async () => {},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
const activation = await helpers.requestPhoneActivation({
phoneSmsProvider: '5sim',
fiveSimApiKey: 'five-token',
fiveSimCountryOrder: ['thailand'],
fiveSimOperator: 'any',
fiveSimProduct: 'openai',
heroSmsReuseEnabled: true,
fiveSimReuseEnabled: false,
heroSmsActivationRetryRounds: 1,
});
assert.deepStrictEqual(activation, {
activationId: '1234568',
phoneNumber: '+66880000001',
provider: '5sim',
serviceCode: 'openai',
countryId: 'thailand',
countryCode: 'thailand',
countryLabel: 'Thailand',
successfulUses: 0,
maxUses: 3,
});
assert.equal(requests[0].pathname, '/v1/guest/prices');
assert.equal(requests[1].pathname, '/v1/user/buy/activation/thailand/any/openai');
assert.equal(requests[1].search.get('reuse'), '1');
});
test('phone verification helper rejects 5sim maxPrice with custom operator before buying', async () => {
const requests = [];
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
@@ -943,6 +943,7 @@ return { collectSettingsPayload };
assert.equal(payload.nexSmsApiKey, 'nex-key');
assert.deepStrictEqual(payload.nexSmsCountryOrder, [1]);
assert.equal(payload.nexSmsServiceCode, 'ot');
assert.equal(payload.phoneSmsReuseEnabled, true);
assert.equal(payload.heroSmsReuseEnabled, true);
assert.equal(payload.freePhoneReuseEnabled, true);
assert.equal(payload.freePhoneReuseAutoEnabled, true);