完善手机接码逻辑
This commit is contained in:
+30
-5
@@ -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');
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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', () => {
|
||||
|
||||
@@ -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/);
|
||||
});
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user