完善手机接码逻辑

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
+30 -5
View File
@@ -809,6 +809,7 @@ const PERSISTED_SETTING_DEFAULTS = {
autoStepDelaySeconds: null,
step6CookieCleanupEnabled: false,
phoneVerificationEnabled: false,
phoneSmsReuseEnabled: DEFAULT_HERO_SMS_REUSE_ENABLED,
freePhoneReuseEnabled: true,
freePhoneReuseAutoEnabled: true,
signupMethod: DEFAULT_SIGNUP_METHOD,
@@ -1329,12 +1330,15 @@ function normalizePhoneSmsProvider(value = '') {
}
return PHONE_SMS_PROVIDER_HERO_SMS;
}
function normalizePhoneSmsProviderOrder(value = [], fallbackOrder = []) {
const rootScope = typeof self !== 'undefined' ? self : globalThis;
if (rootScope.PhoneSmsProviderRegistry?.normalizeProviderOrder) {
return rootScope.PhoneSmsProviderRegistry.normalizeProviderOrder(value, fallbackOrder);
}
const source = Array.isArray(value)
? value
: String(value || '')
.split(/[\r\n,;]+/)
.split(/[\r\n,]+/)
.map((entry) => String(entry || '').trim())
.filter(Boolean);
const normalized = [];
@@ -1373,7 +1377,6 @@ function normalizePhoneSmsProviderOrder(value = [], fallbackOrder = []) {
return normalized.slice(0, DEFAULT_PHONE_SMS_PROVIDER_ORDER.length);
}
function normalizeSignupMethod(value = '') {
return String(value || '').trim().toLowerCase() === 'phone'
? 'phone'
@@ -2668,6 +2671,7 @@ function normalizePersistentSettingValue(key, value) {
return typeof value === 'boolean' ? value : true;
case 'step6CookieCleanupEnabled':
case 'phoneVerificationEnabled':
case 'phoneSmsReuseEnabled':
case 'freePhoneReuseEnabled':
case 'freePhoneReuseAutoEnabled':
case 'plusModeEnabled':
@@ -2842,6 +2846,13 @@ function buildPersistentSettingsPayload(input = {}, options = {}) {
throw new Error('\u914d\u7f6e\u5185\u5bb9\u683c\u5f0f\u65e0\u6548\u3002');
}
const persistedSettingDefaults = typeof PERSISTED_SETTING_DEFAULTS !== 'undefined' && PERSISTED_SETTING_DEFAULTS
? PERSISTED_SETTING_DEFAULTS
: {};
const persistedSettingKeys = Array.isArray(typeof PERSISTED_SETTING_KEYS !== 'undefined' ? PERSISTED_SETTING_KEYS : null)
? PERSISTED_SETTING_KEYS
: Object.keys(persistedSettingDefaults);
const normalizedInput = { ...input };
if (normalizedInput.autoStepDelaySeconds === undefined) {
const legacyAutoStepDelaySeconds = resolveLegacyAutoStepDelaySeconds(normalizedInput);
@@ -2860,15 +2871,29 @@ function buildPersistentSettingsPayload(input = {}, options = {}) {
const payload = {};
let matchedKeyCount = 0;
for (const key of PERSISTED_SETTING_KEYS) {
for (const key of persistedSettingKeys) {
if (normalizedInput[key] !== undefined) {
payload[key] = normalizePersistentSettingValue(key, normalizedInput[key]);
matchedKeyCount += 1;
} else if (fillDefaults) {
payload[key] = normalizePersistentSettingValue(key, PERSISTED_SETTING_DEFAULTS[key]);
payload[key] = normalizePersistentSettingValue(key, persistedSettingDefaults[key]);
}
}
const hasPhoneSmsReuseEnabled = Object.prototype.hasOwnProperty.call(normalizedInput, 'phoneSmsReuseEnabled');
const hasHeroSmsReuseEnabled = Object.prototype.hasOwnProperty.call(normalizedInput, 'heroSmsReuseEnabled');
const hasFiveSimReuseEnabled = Object.prototype.hasOwnProperty.call(normalizedInput, 'fiveSimReuseEnabled');
if (hasPhoneSmsReuseEnabled || hasHeroSmsReuseEnabled || hasFiveSimReuseEnabled) {
const reuseSource = hasPhoneSmsReuseEnabled
? normalizedInput.phoneSmsReuseEnabled
: (hasHeroSmsReuseEnabled
? normalizedInput.heroSmsReuseEnabled
: normalizedInput.fiveSimReuseEnabled);
const normalizedReuseEnabled = normalizePersistentSettingValue('phoneSmsReuseEnabled', reuseSource);
payload.phoneSmsReuseEnabled = normalizedReuseEnabled;
payload.heroSmsReuseEnabled = normalizedReuseEnabled;
}
if (requireKnownKeys && matchedKeyCount === 0) {
throw new Error('\u914d\u7f6e\u6587\u4ef6\u4e2d\u6ca1\u6709\u53ef\u8bc6\u522b\u7684\u914d\u7f6e\u5185\u5bb9\u3002');
}
+19 -9
View File
@@ -180,6 +180,10 @@
}
function normalizePhoneSmsProvider(value = '') {
const rootScope = typeof self !== 'undefined' ? self : globalThis;
if (rootScope.PhoneSmsProviderRegistry?.normalizeProviderId) {
return rootScope.PhoneSmsProviderRegistry.normalizeProviderId(value);
}
const normalized = String(value || '').trim().toLowerCase();
if (normalized === PHONE_SMS_PROVIDER_5SIM) {
return PHONE_SMS_PROVIDER_5SIM;
@@ -189,7 +193,6 @@
}
return PHONE_SMS_PROVIDER_HERO;
}
function isFiveSimProvider(state = {}) {
return normalizePhoneSmsProvider(state?.phoneSmsProvider || DEFAULT_PHONE_SMS_PROVIDER) === PHONE_SMS_PROVIDER_5SIM;
}
@@ -515,6 +518,13 @@
return Boolean(value);
}
function normalizePhoneSmsReuseEnabled(state = {}) {
if (Object.prototype.hasOwnProperty.call(state, 'phoneSmsReuseEnabled')) {
return Boolean(state.phoneSmsReuseEnabled);
}
return normalizeHeroSmsReuseEnabled(state?.heroSmsReuseEnabled);
}
function normalizeFreePhoneReuseEnabled(value) {
return Boolean(value);
}
@@ -536,10 +546,14 @@
}
function normalizePhoneSmsProviderOrder(value = [], fallbackOrder = []) {
const rootScope = typeof self !== 'undefined' ? self : globalThis;
if (rootScope.PhoneSmsProviderRegistry?.normalizeProviderOrder) {
return rootScope.PhoneSmsProviderRegistry.normalizeProviderOrder(value, fallbackOrder);
}
const source = Array.isArray(value)
? value
: String(value || '')
.split(/[\r\n,;|/]+/)
.split(/[\r\n,]+/)
.map((entry) => String(entry || '').trim())
.filter(Boolean);
const normalized = [];
@@ -573,7 +587,6 @@
return fallbackNormalized.slice(0, 3);
}
function resolvePhoneProviderOrder(state = {}, preferredProvider = '') {
const currentProvider = normalizePhoneSmsProvider(
preferredProvider || state?.phoneSmsProvider || DEFAULT_PHONE_SMS_PROVIDER
@@ -856,10 +869,7 @@
}
function isPhoneSmsReuseEnabled(state = {}) {
if (normalizePhoneSmsProvider(state?.phoneSmsProvider) === PHONE_SMS_PROVIDER_FIVE_SIM) {
return state?.fiveSimReuseEnabled !== false;
}
return normalizeHeroSmsReuseEnabled(state?.heroSmsReuseEnabled);
return normalizePhoneSmsReuseEnabled(state);
}
function createResolvedFiveSimProvider() {
@@ -2658,7 +2668,7 @@
{
query: {
...(candidatePrice !== null && candidatePrice !== undefined ? { maxPrice: candidatePrice } : {}),
...(normalizeHeroSmsReuseEnabled(state.heroSmsReuseEnabled) ? { reuse: 1 } : {}),
...(normalizePhoneSmsReuseEnabled(state) ? { reuse: 1 } : {}),
},
}
);
@@ -4850,7 +4860,7 @@
delete nextReusableActivation.phoneCodeReceived;
delete nextReusableActivation.phoneCodeReceivedAt;
await upsertReusableActivationPool(nextReusableActivation, { state });
if (!normalizeHeroSmsReuseEnabled(state?.heroSmsReuseEnabled)) {
if (!normalizePhoneSmsReuseEnabled(state)) {
await clearReusableActivation();
return;
}
+22 -12
View File
@@ -574,22 +574,32 @@
return /not\s+enough\s+(?:user\s+)?balance|not\s+enough\s+rating|unauthorized|invalid\s+token|banned|bad\s+(?:country|operator)|no\s+product|server\s+offline/i.test(text);
}
function assertMaxPriceCompatibleWithOperator(state = {}) {
const maxPrice = normalizeFiveSimMaxPrice(state.fiveSimMaxPrice);
const operator = normalizeFiveSimOperator(state.fiveSimOperator);
if (maxPrice && operator !== DEFAULT_OPERATOR) {
throw new Error('5sim maxPrice only works when operator is "any"; clear the price limit or switch operator to any before buying a number.');
}
}
async function buyActivationWithPrice(state = {}, countryConfig, maxPrice, deps = {}) {
const config = resolveConfig(state, deps);
const operator = normalizeFiveSimOperator(state.fiveSimOperator);
function assertMaxPriceCompatibleWithOperator(state = {}) {
const maxPrice = normalizeFiveSimMaxPrice(state.fiveSimMaxPrice);
const operator = normalizeFiveSimOperator(state.fiveSimOperator);
if (maxPrice && operator !== DEFAULT_OPERATOR) {
throw new Error('5sim maxPrice only works when operator is "any"; clear the price limit or switch operator to any before buying a number.');
}
}
function normalizeReuseEnabled(state = {}) {
if (Object.prototype.hasOwnProperty.call(state, 'phoneSmsReuseEnabled')) {
return state.phoneSmsReuseEnabled !== false;
}
if (Object.prototype.hasOwnProperty.call(state, 'heroSmsReuseEnabled')) {
return state.heroSmsReuseEnabled !== false;
}
return true;
}
async function buyActivationWithPrice(state = {}, countryConfig, maxPrice, deps = {}) {
const config = resolveConfig(state, deps);
const operator = normalizeFiveSimOperator(state.fiveSimOperator);
const query = {};
if (maxPrice !== null && maxPrice !== undefined) {
query.maxPrice = maxPrice;
}
if (state.fiveSimReuseEnabled !== false) {
if (normalizeReuseEnabled(state)) {
query.reuse = 1;
}
const payload = await fetchJson(
+106 -10
View File
@@ -1,41 +1,137 @@
// phone-sms/providers/registry.js — 接码平台注册表
// phone-sms/providers/registry.js - phone SMS provider registry
(function attachPhoneSmsProviderRegistry(root, factory) {
root.PhoneSmsProviderRegistry = factory(root);
})(typeof self !== 'undefined' ? self : globalThis, function createPhoneSmsProviderRegistry(root) {
const PROVIDER_HERO_SMS = 'hero-sms';
const PROVIDER_FIVE_SIM = '5sim';
const PROVIDER_NEXSMS = 'nexsms';
const DEFAULT_PROVIDER = PROVIDER_HERO_SMS;
const DEFAULT_PROVIDER_ORDER = Object.freeze([
PROVIDER_HERO_SMS,
PROVIDER_FIVE_SIM,
PROVIDER_NEXSMS,
]);
const PROVIDER_DEFINITIONS = Object.freeze({
[PROVIDER_HERO_SMS]: Object.freeze({
id: PROVIDER_HERO_SMS,
label: 'HeroSMS',
moduleKey: 'PhoneSmsHeroSmsProvider',
}),
[PROVIDER_FIVE_SIM]: Object.freeze({
id: PROVIDER_FIVE_SIM,
label: '5sim',
moduleKey: 'PhoneSmsFiveSimProvider',
}),
[PROVIDER_NEXSMS]: Object.freeze({
id: PROVIDER_NEXSMS,
label: 'NexSMS',
moduleKey: 'PhoneSmsNexSmsProvider',
}),
});
function normalizeProviderId(value = '') {
const normalized = String(value || '').trim().toLowerCase();
return normalized === PROVIDER_FIVE_SIM ? PROVIDER_FIVE_SIM : PROVIDER_HERO_SMS;
function resolveProviderKey(value = '') {
if (value && typeof value === 'object' && !Array.isArray(value)) {
return String(value.provider || value.id || value.value || '').trim().toLowerCase();
}
return String(value || '').trim().toLowerCase();
}
function isKnownProviderId(providerId = '') {
return Boolean(PROVIDER_DEFINITIONS[providerId]);
}
function normalizeProviderId(value = '', fallback = DEFAULT_PROVIDER) {
const normalized = resolveProviderKey(value);
if (isKnownProviderId(normalized)) {
return normalized;
}
const fallbackNormalized = resolveProviderKey(fallback);
if (isKnownProviderId(fallbackNormalized)) {
return fallbackNormalized;
}
return DEFAULT_PROVIDER;
}
function normalizeProviderOrder(value = [], fallbackOrder = DEFAULT_PROVIDER_ORDER) {
const source = Array.isArray(value)
? value
: String(value || '')
.split(/[\r\n,;]+/)
.map((entry) => String(entry || '').trim())
.filter(Boolean);
const normalized = [];
const seen = new Set();
const pushProvider = (entry) => {
const provider = normalizeProviderId(entry, DEFAULT_PROVIDER);
if (!provider || seen.has(provider)) {
return;
}
seen.add(provider);
normalized.push(provider);
};
source.forEach((entry) => {
pushProvider(entry && typeof entry === 'object' && !Array.isArray(entry)
? (entry.provider || entry.id || entry.value || '')
: entry);
});
if (normalized.length) {
return normalized.slice(0, DEFAULT_PROVIDER_ORDER.length);
}
const fallback = Array.isArray(fallbackOrder) ? fallbackOrder : [];
fallback.forEach((entry) => {
pushProvider(entry && typeof entry === 'object' && !Array.isArray(entry)
? (entry.provider || entry.id || entry.value || '')
: entry);
});
return normalized.slice(0, DEFAULT_PROVIDER_ORDER.length);
}
function getProviderIds() {
return DEFAULT_PROVIDER_ORDER.slice();
}
function getProviderDefinition(providerId = DEFAULT_PROVIDER) {
return PROVIDER_DEFINITIONS[normalizeProviderId(providerId)] || PROVIDER_DEFINITIONS[DEFAULT_PROVIDER];
}
function getProviderModule(providerId = DEFAULT_PROVIDER) {
const normalized = normalizeProviderId(providerId);
if (normalized === PROVIDER_FIVE_SIM) {
return root.PhoneSmsFiveSimProvider || null;
const definition = getProviderDefinition(providerId);
const moduleKey = definition?.moduleKey;
if (!moduleKey) {
return null;
}
return root.PhoneSmsHeroSmsProvider || null;
return root?.[moduleKey] || null;
}
function createProvider(providerId = DEFAULT_PROVIDER, deps = {}) {
const definition = getProviderDefinition(providerId);
const module = getProviderModule(providerId);
if (!module || typeof module.createProvider !== 'function') {
throw new Error(`Phone SMS provider is not loaded: ${normalizeProviderId(providerId)}`);
throw new Error(`Phone SMS provider module is not loaded: ${definition.id}`);
}
return module.createProvider(deps);
}
function getProviderLabel(providerId = DEFAULT_PROVIDER) {
return normalizeProviderId(providerId) === PROVIDER_FIVE_SIM ? '5sim' : 'HeroSMS';
return getProviderDefinition(providerId)?.label || 'HeroSMS';
}
return {
PROVIDER_HERO_SMS,
PROVIDER_FIVE_SIM,
PROVIDER_NEXSMS,
DEFAULT_PROVIDER,
DEFAULT_PROVIDER_ORDER,
PROVIDER_DEFINITIONS,
normalizeProviderId,
normalizeProviderOrder,
getProviderIds,
getProviderDefinition,
getProviderModule,
createProvider,
getProviderLabel,
+46 -22
View File
@@ -3333,9 +3333,13 @@ function collectSettingsPayload() {
const defaultPhoneCodePollMaxRounds = typeof DEFAULT_PHONE_CODE_POLL_MAX_ROUNDS !== 'undefined'
? DEFAULT_PHONE_CODE_POLL_MAX_ROUNDS
: 12;
const heroSmsReuseEnabledValue = typeof inputHeroSmsReuseEnabled !== 'undefined' && inputHeroSmsReuseEnabled
const phoneSmsReuseEnabledValue = typeof inputHeroSmsReuseEnabled !== 'undefined' && inputHeroSmsReuseEnabled
? normalizeHeroSmsReuseEnabledValue(inputHeroSmsReuseEnabled.checked)
: defaultHeroSmsReuseEnabled;
: normalizeHeroSmsReuseEnabledValue(
latestState?.phoneSmsReuseEnabled,
latestState?.heroSmsReuseEnabled
);
const heroSmsReuseEnabledValue = phoneSmsReuseEnabledValue;
const freePhoneReuseEnabledValue = typeof inputFreePhoneReuseEnabled !== 'undefined' && inputFreePhoneReuseEnabled
? Boolean(inputFreePhoneReuseEnabled.checked)
: Boolean(latestState?.freePhoneReuseEnabled);
@@ -3782,6 +3786,7 @@ function collectSettingsPayload() {
nexSmsApiKey: nexSmsApiKeyValue,
nexSmsCountryOrder: nexSmsCountryOrderValue,
nexSmsServiceCode: nexSmsServiceCodeValue,
phoneSmsReuseEnabled: phoneSmsReuseEnabledValue,
heroSmsReuseEnabled: heroSmsReuseEnabledValue,
freePhoneReuseEnabled: freePhoneReuseEnabledValue,
freePhoneReuseAutoEnabled: freePhoneReuseAutoEnabledValue,
@@ -3851,15 +3856,22 @@ function normalizeAccountRunHistoryHelperBaseUrlValue(value = '') {
function normalizePhoneSmsProvider(value = '') {
if (typeof window !== 'undefined' && window.PhoneSmsProviderRegistry?.normalizeProviderId) {
return window.PhoneSmsProviderRegistry.normalizeProviderId(value);
const rootScope = typeof window !== 'undefined' ? window : globalThis;
if (rootScope.PhoneSmsProviderRegistry?.normalizeProviderId) {
return rootScope.PhoneSmsProviderRegistry.normalizeProviderId(value);
}
const nexSmsProvider = typeof PHONE_SMS_PROVIDER_NEXSMS !== 'undefined'
? PHONE_SMS_PROVIDER_NEXSMS
: 'nexsms';
const normalized = String(value || '').trim().toLowerCase();
return normalized === PHONE_SMS_PROVIDER_FIVE_SIM
? PHONE_SMS_PROVIDER_FIVE_SIM
: PHONE_SMS_PROVIDER_HERO_SMS;
if (normalized === PHONE_SMS_PROVIDER_FIVE_SIM) {
return PHONE_SMS_PROVIDER_FIVE_SIM;
}
if (normalized === nexSmsProvider) {
return nexSmsProvider;
}
return PHONE_SMS_PROVIDER_HERO_SMS;
}
function setPhoneSmsProviderSelectValue(provider) {
const normalizedProvider = normalizePhoneSmsProvider(provider);
if (selectPhoneSmsProvider) {
@@ -4042,21 +4054,21 @@ function normalizeHeroSmsMaxPriceValue(value = '') {
}
function normalizePhoneSmsProviderValue(value = '') {
const normalized = String(value || '').trim().toLowerCase();
if (normalized === PHONE_SMS_PROVIDER_FIVE_SIM) {
return PHONE_SMS_PROVIDER_FIVE_SIM;
const rootScope = typeof window !== 'undefined' ? window : globalThis;
if (rootScope.PhoneSmsProviderRegistry?.normalizeProviderId) {
return rootScope.PhoneSmsProviderRegistry.normalizeProviderId(value);
}
if (normalized === PHONE_SMS_PROVIDER_NEXSMS) {
return PHONE_SMS_PROVIDER_NEXSMS;
}
return PHONE_SMS_PROVIDER_HERO;
return normalizePhoneSmsProvider(value);
}
function normalizePhoneSmsProviderOrderValue(value = [], fallbackOrder = []) {
const rootScope = typeof window !== 'undefined' ? window : globalThis;
if (rootScope.PhoneSmsProviderRegistry?.normalizeProviderOrder) {
return rootScope.PhoneSmsProviderRegistry.normalizeProviderOrder(value, fallbackOrder);
}
const source = Array.isArray(value)
? value
: String(value || '')
.split(/[\r\n,;|/]+/)
.split(/[\r\n,]+/)
.map((entry) => String(entry || '').trim())
.filter(Boolean);
const normalized = [];
@@ -4089,7 +4101,6 @@ function normalizePhoneSmsProviderOrderValue(value = [], fallbackOrder = []) {
});
return fallbackNormalized.slice(0, 3);
}
function formatPhoneSmsProviderOrderSummary(order = []) {
const normalized = normalizePhoneSmsProviderOrderValue(order, []);
if (!normalized.length) {
@@ -4593,8 +4604,11 @@ function normalizePhoneCodePollMaxRoundsValue(value, fallback = DEFAULT_PHONE_CO
return Math.max(PHONE_CODE_POLL_MAX_ROUNDS_MIN, Math.min(PHONE_CODE_POLL_MAX_ROUNDS_MAX, parsed));
}
function normalizeHeroSmsReuseEnabledValue(value) {
function normalizeHeroSmsReuseEnabledValue(value, fallbackValue = undefined) {
if (value === undefined || value === null) {
if (fallbackValue !== undefined && fallbackValue !== null) {
return Boolean(fallbackValue);
}
return DEFAULT_HERO_SMS_REUSE_ENABLED;
}
return Boolean(value);
@@ -8983,7 +8997,10 @@ function applySettingsState(state) {
: String(state?.nexSmsServiceCode || defaultNexSmsServiceCode).trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '') || defaultNexSmsServiceCode;
}
if (typeof inputHeroSmsReuseEnabled !== 'undefined' && inputHeroSmsReuseEnabled) {
inputHeroSmsReuseEnabled.checked = normalizeHeroSmsReuseEnabledValue(state?.heroSmsReuseEnabled);
inputHeroSmsReuseEnabled.checked = normalizeHeroSmsReuseEnabledValue(
state?.phoneSmsReuseEnabled,
state?.heroSmsReuseEnabled
);
}
if (typeof inputFreePhoneReuseEnabled !== 'undefined' && inputFreePhoneReuseEnabled) {
inputFreePhoneReuseEnabled.checked = Boolean(state?.freePhoneReuseEnabled);
@@ -14582,8 +14599,15 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
if (message.payload.nexSmsServiceCode !== undefined && inputNexSmsServiceCode) {
inputNexSmsServiceCode.value = normalizeNexSmsServiceCodeValue(message.payload.nexSmsServiceCode);
}
if (message.payload.heroSmsReuseEnabled !== undefined && inputHeroSmsReuseEnabled) {
inputHeroSmsReuseEnabled.checked = normalizeHeroSmsReuseEnabledValue(message.payload.heroSmsReuseEnabled);
if (
(message.payload.phoneSmsReuseEnabled !== undefined
|| message.payload.heroSmsReuseEnabled !== undefined)
&& inputHeroSmsReuseEnabled
) {
inputHeroSmsReuseEnabled.checked = normalizeHeroSmsReuseEnabledValue(
message.payload.phoneSmsReuseEnabled,
message.payload.heroSmsReuseEnabled
);
}
if (message.payload.freePhoneReuseEnabled !== undefined && inputFreePhoneReuseEnabled) {
inputFreePhoneReuseEnabled.checked = Boolean(message.payload.freePhoneReuseEnabled);
@@ -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);