diff --git a/background.js b/background.js index cceb207..04fcd5a 100644 --- a/background.js +++ b/background.js @@ -23,6 +23,7 @@ importScripts( 'phone-sms/providers/five-sim.js', 'phone-sms/providers/nexsms.js', 'phone-sms/providers/madao.js', + 'phone-sms/providers/sms-bower.js', 'phone-sms/providers/registry.js', 'background/phone-verification-flow.js', 'background/account-run-history.js', @@ -702,12 +703,14 @@ const PHONE_SMS_PROVIDER_HERO_SMS = PHONE_SMS_PROVIDER_HERO; const PHONE_SMS_PROVIDER_FIVE_SIM = PHONE_SMS_PROVIDER_5SIM; const PHONE_SMS_PROVIDER_NEXSMS = 'nexsms'; const PHONE_SMS_PROVIDER_MADAO = 'madao'; +const PHONE_SMS_PROVIDER_SMS_BOWER = 'sms-bower'; const DEFAULT_PHONE_SMS_PROVIDER = PHONE_SMS_PROVIDER_HERO; const DEFAULT_PHONE_SMS_PROVIDER_ORDER = Object.freeze([ PHONE_SMS_PROVIDER_HERO, PHONE_SMS_PROVIDER_5SIM, PHONE_SMS_PROVIDER_NEXSMS, PHONE_SMS_PROVIDER_MADAO, + PHONE_SMS_PROVIDER_SMS_BOWER, ]); const DEFAULT_FIVE_SIM_BASE_URL = 'https://5sim.net/v1'; const DEFAULT_FIVE_SIM_PRODUCT = 'openai'; @@ -716,6 +719,8 @@ const DEFAULT_FIVE_SIM_COUNTRY_ORDER = Object.freeze(['thailand']); const DEFAULT_NEX_SMS_BASE_URL = 'https://api.nexsms.net'; const DEFAULT_NEX_SMS_SERVICE_CODE = 'ot'; const DEFAULT_NEX_SMS_COUNTRY_ORDER = Object.freeze([1]); +const DEFAULT_SMS_BOWER_SERVICE_CODE = 'ot'; +const DEFAULT_SMS_BOWER_COUNTRY_ORDER = Object.freeze([52]); const DEFAULT_MADAO_BASE_URL = 'http://127.0.0.1:7822'; const DEFAULT_MADAO_MODE = 'routing_plan'; const DEFAULT_HERO_SMS_REUSE_ENABLED = true; @@ -1447,6 +1452,7 @@ const PERSISTED_SETTING_DEFAULTS = { nexSmsApiKey: '', nexSmsCountryOrder: [...DEFAULT_NEX_SMS_COUNTRY_ORDER], nexSmsServiceCode: DEFAULT_NEX_SMS_SERVICE_CODE, + smsBowerApiKey: '', madaoBaseUrl: DEFAULT_MADAO_BASE_URL, madaoHttpSecret: '', madaoMode: DEFAULT_MADAO_MODE, @@ -1848,6 +1854,9 @@ function normalizePhoneSmsProvider(value = '') { if (normalized === PHONE_SMS_PROVIDER_MADAO) { return PHONE_SMS_PROVIDER_MADAO; } + if (normalized === PHONE_SMS_PROVIDER_SMS_BOWER) { + return PHONE_SMS_PROVIDER_SMS_BOWER; + } return PHONE_SMS_PROVIDER_HERO_SMS; } function normalizePhoneSmsProviderOrder(value = [], fallbackOrder = []) { diff --git a/phone-sms/providers/registry.js b/phone-sms/providers/registry.js index d46cd28..a5d7f07 100644 --- a/phone-sms/providers/registry.js +++ b/phone-sms/providers/registry.js @@ -6,12 +6,14 @@ const PROVIDER_FIVE_SIM = '5sim'; const PROVIDER_NEXSMS = 'nexsms'; const PROVIDER_MADAO = 'madao'; + const PROVIDER_SMS_BOWER = 'sms-bower'; const DEFAULT_PROVIDER = PROVIDER_HERO_SMS; const DEFAULT_PROVIDER_ORDER = Object.freeze([ PROVIDER_HERO_SMS, PROVIDER_FIVE_SIM, PROVIDER_NEXSMS, PROVIDER_MADAO, + PROVIDER_SMS_BOWER, ]); const PROVIDER_DEFINITIONS = Object.freeze({ [PROVIDER_HERO_SMS]: Object.freeze({ @@ -34,6 +36,11 @@ label: 'MaDao', moduleKey: 'PhoneSmsMaDaoProvider', }), + [PROVIDER_SMS_BOWER]: Object.freeze({ + id: PROVIDER_SMS_BOWER, + label: 'SMS Bower', + moduleKey: 'PhoneSmsBowerProvider', + }), }); function resolveProviderKey(value = '') { @@ -133,6 +140,7 @@ PROVIDER_FIVE_SIM, PROVIDER_NEXSMS, PROVIDER_MADAO, + PROVIDER_SMS_BOWER, DEFAULT_PROVIDER, DEFAULT_PROVIDER_ORDER, PROVIDER_DEFINITIONS, diff --git a/phone-sms/providers/sms-bower.js b/phone-sms/providers/sms-bower.js new file mode 100644 index 0000000..1e8dbd7 --- /dev/null +++ b/phone-sms/providers/sms-bower.js @@ -0,0 +1,471 @@ +// phone-sms/providers/sms-bower.js — SMS Bower 接码平台适配层 +(function attachSmsBowerProvider(root, factory) { + root.PhoneSmsBowerProvider = factory(); +})(typeof self !== 'undefined' ? self : globalThis, function createSmsBowerProviderModule() { + const PROVIDER_ID = 'sms-bower'; + const DEFAULT_BASE_URL = 'https://smsbower.page/stubs/handler_api.php'; + const DEFAULT_SERVICE_CODE = 'ot'; + const DEFAULT_SERVICE_LABEL = 'OpenAI'; + const DEFAULT_COUNTRY_ID = 52; + const DEFAULT_COUNTRY_LABEL = 'Thailand'; + const DEFAULT_REQUEST_TIMEOUT_MS = 20000; + const DEFAULT_POLL_TIMEOUT_MS = 180000; + const DEFAULT_POLL_INTERVAL_MS = 5000; + const PHONE_CODE_TIMEOUT_ERROR_PREFIX = 'PHONE_CODE_TIMEOUT::'; + const MAX_PRICE_CANDIDATES = 8; + + function normalizeText(value = '', fallback = '') { + return String(value || '').trim() || fallback; + } + + function normalizeSmsBowerCountryId(value, fallback = DEFAULT_COUNTRY_ID) { + const parsed = Math.floor(Number(value)); + if (Number.isFinite(parsed) && parsed >= 0) return parsed; + const fallbackParsed = Math.floor(Number(fallback)); + return Number.isFinite(fallbackParsed) && fallbackParsed >= 0 ? fallbackParsed : DEFAULT_COUNTRY_ID; + } + + function normalizeSmsBowerCountryLabel(value = '', fallback = DEFAULT_COUNTRY_LABEL) { + return normalizeText(value, fallback); + } + + function normalizeCountryKey(value) { + const countryId = normalizeSmsBowerCountryId(value, -1); + return countryId >= 0 ? String(countryId) : ''; + } + + function normalizeSmsBowerServiceCode(value = '', fallback = DEFAULT_SERVICE_CODE) { + const normalized = String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, ''); + if (normalized) return normalized; + const fallbackNormalized = String(fallback || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, ''); + return fallbackNormalized || DEFAULT_SERVICE_CODE; + } + + function normalizePriceLimit(value) { + if (value === undefined || value === null || String(value).trim() === '') return null; + const parsed = Number(value); + if (!Number.isFinite(parsed) || parsed <= 0) return null; + return Math.round(parsed * 10000) / 10000; + } + + function normalizeSmsBowerMaxPrice(value = '') { + const normalized = normalizePriceLimit(value); + return normalized === null ? '' : String(normalized); + } + + function normalizeCsvIds(value = '') { + const source = Array.isArray(value) ? value : String(value || '').split(/[\r\n,,;;]+/); + const seen = new Set(); + return source + .map((entry) => String(entry || '').trim().replace(/[^0-9a-zA-Z_-]+/g, '')) + .filter((entry) => { + if (!entry || seen.has(entry)) return false; + seen.add(entry); + return true; + }) + .join(','); + } + + function normalizeCountryOrder(value = []) { + const source = Array.isArray(value) + ? value + : String(value || '').split(/[\r\n,,;;]+/).map((entry) => String(entry || '').trim()).filter(Boolean); + const seen = new Set(); + const normalized = []; + source.forEach((entry) => { + let id = -1; + let label = ''; + if (entry && typeof entry === 'object' && !Array.isArray(entry)) { + id = normalizeSmsBowerCountryId(entry.id ?? entry.countryId, -1); + label = normalizeText(entry.label ?? entry.countryLabel, ''); + } else { + const text = String(entry || '').trim(); + const structured = text.match(/^(\d+)\s*(?:[:|/-]\s*(.+))?$/); + id = normalizeSmsBowerCountryId(structured?.[1] || text, -1); + label = normalizeText(structured?.[2], ''); + } + if (id < 0 || seen.has(id)) return; + seen.add(id); + normalized.push({ id, label: label || `Country #${id}` }); + }); + return normalized.slice(0, 20); + } + + function normalizeBaseUrl(value = '') { + const trimmed = String(value || '').trim() || DEFAULT_BASE_URL; + try { + const parsed = new URL(trimmed); + return parsed.toString(); + } catch { + return DEFAULT_BASE_URL; + } + } + + function parsePayload(text) { + const trimmed = String(text || '').trim(); + if (!trimmed) return ''; + if ((trimmed.startsWith('{') && trimmed.endsWith('}')) || (trimmed.startsWith('[') && trimmed.endsWith(']'))) { + try { return JSON.parse(trimmed); } catch { return trimmed; } + } + return trimmed; + } + + function describePayload(raw) { + if (typeof raw === 'string') return raw.trim(); + if (raw && typeof raw === 'object') { + const direct = normalizeText(raw.message || raw.msg || raw.error || raw.title || raw.status, ''); + if (direct) return direct; + try { return JSON.stringify(raw); } catch { return String(raw); } + } + return String(raw || '').trim(); + } + + function resolveConfig(state = {}, deps = {}) { + return { + apiKey: normalizeText(state.smsBowerApiKey), + baseUrl: normalizeBaseUrl(state.smsBowerBaseUrl || DEFAULT_BASE_URL), + serviceCode: normalizeSmsBowerServiceCode(state.smsBowerServiceCode, DEFAULT_SERVICE_CODE), + fetchImpl: deps.fetchImpl || (typeof fetch === 'function' ? fetch.bind(globalThis) : null), + requestTimeoutMs: deps.requestTimeoutMs || DEFAULT_REQUEST_TIMEOUT_MS, + }; + } + + function buildUrl(config = {}, query = {}) { + const url = new URL(config.baseUrl || DEFAULT_BASE_URL); + url.searchParams.set('api_key', config.apiKey); + Object.entries(query || {}).forEach(([key, value]) => { + if (value === undefined || value === null || value === '') return; + url.searchParams.set(key, String(value)); + }); + return url.toString(); + } + + async function fetchPayload(config = {}, query = {}, actionLabel = 'SMS Bower 请求') { + if (!config.fetchImpl) throw new Error('SMS Bower 网络请求实现不可用。'); + if (!config.apiKey) throw new Error('SMS Bower API Key 缺失,请先在侧边栏保存接码 API Key。'); + const controller = typeof AbortController === 'function' ? new AbortController() : null; + const timeoutId = controller ? setTimeout(() => controller.abort(), Number(config.requestTimeoutMs) || DEFAULT_REQUEST_TIMEOUT_MS) : null; + try { + const response = await config.fetchImpl(buildUrl(config, query), { + method: 'GET', + signal: controller?.signal, + headers: { Accept: 'application/json, text/plain, */*' }, + }); + const text = await response.text(); + const payload = parsePayload(text); + if (!response.ok) { + const error = new Error(`${actionLabel}失败:${describePayload(payload) || response.status}`); + error.payload = payload; + error.status = response.status; + throw error; + } + const message = describePayload(payload); + if (/^(BAD_KEY|BAD_ACTION|BAD_SERVICE|BAD_STATUS|NO_ACTIVATION|EARLY_CANCEL_DENIED|BAD_COUNTRY)\b/i.test(message)) { + const error = new Error(`${actionLabel}失败:${message}`); + error.payload = payload; + throw error; + } + return payload; + } catch (error) { + if (error?.name === 'AbortError') throw new Error(`${actionLabel}超时。`); + throw error; + } finally { + if (timeoutId) clearTimeout(timeoutId); + } + } + + function resolveCountryCandidates(state = {}) { + const candidates = normalizeCountryOrder(state.smsBowerCountryOrder); + if (candidates.length) return candidates; + return [{ + id: normalizeSmsBowerCountryId(state.smsBowerCountryId, DEFAULT_COUNTRY_ID), + label: normalizeSmsBowerCountryLabel(state.smsBowerCountryLabel, DEFAULT_COUNTRY_LABEL), + }]; + } + + function resolveCountryLabel(state = {}, countryId = DEFAULT_COUNTRY_ID) { + const key = normalizeCountryKey(countryId); + const matched = resolveCountryCandidates(state).find((entry) => normalizeCountryKey(entry.id) === key); + return matched?.label || (key ? `Country #${key}` : DEFAULT_COUNTRY_LABEL); + } + + function resolvePriceRange(state = {}) { + const minPriceLimit = normalizePriceLimit(state.smsBowerMinPrice); + const maxPriceLimit = normalizePriceLimit(state.smsBowerMaxPrice); + return { + minPriceLimit, + maxPriceLimit, + hasMinPriceLimit: minPriceLimit !== null, + hasMaxPriceLimit: maxPriceLimit !== null, + invalidRange: minPriceLimit !== null && maxPriceLimit !== null && minPriceLimit > maxPriceLimit, + }; + } + + function formatPriceRangeText(minPriceLimit = null, maxPriceLimit = null) { + const minPrice = normalizePriceLimit(minPriceLimit); + const maxPrice = normalizePriceLimit(maxPriceLimit); + if (minPrice !== null && maxPrice !== null) return `${minPrice}~${maxPrice}`; + if (minPrice !== null) return `${minPrice}~`; + if (maxPrice !== null) return `~${maxPrice}`; + return 'unbounded'; + } + + async function fetchBalance(state = {}, deps = {}) { + const config = resolveConfig(state, deps); + const payload = await fetchPayload(config, { action: 'getBalance' }, 'SMS Bower 查询余额'); + const matched = describePayload(payload).match(/ACCESS_BALANCE\s*:\s*([\d.]+)/i); + return { balance: matched ? Number(matched[1]) : null, raw: payload }; + } + + async function fetchPrices(state = {}, countryConfig = {}, deps = {}) { + const config = resolveConfig(state, deps); + return fetchPayload(config, { + action: 'getPrices', + service: config.serviceCode, + country: normalizeSmsBowerCountryId(countryConfig?.id ?? state.smsBowerCountryId, DEFAULT_COUNTRY_ID), + }, 'SMS Bower 查询价格'); + } + + function collectPriceEntries(payload, entries = []) { + if (Array.isArray(payload)) { + payload.forEach((entry) => collectPriceEntries(entry, entries)); + return entries; + } + if (!payload || typeof payload !== 'object') return entries; + const cost = Number(payload.cost ?? payload.price ?? payload.Price); + if (Number.isFinite(cost) && cost >= 0) { + const count = Number(payload.count ?? payload.qty ?? payload.Qty); + entries.push({ cost, count: Number.isFinite(count) ? count : 0, inStock: !Number.isFinite(count) || count > 0 }); + } + Object.values(payload).forEach((entry) => collectPriceEntries(entry, entries)); + return entries; + } + + function normalizeActivation(payload, fallback = {}) { + if (typeof payload === 'string') { + const matched = payload.match(/^ACCESS_NUMBER\s*:\s*([^:]+)\s*:\s*(.+)$/i); + if (matched) { + payload = { activationId: matched[1], phoneNumber: matched[2] }; + } + } + const source = payload && typeof payload === 'object' && !Array.isArray(payload) ? payload : {}; + const activationId = normalizeText(source.activationId ?? source.id ?? fallback.activationId); + const phoneNumber = normalizeText(source.phoneNumber ?? source.phone ?? fallback.phoneNumber); + if (!activationId || !phoneNumber) return null; + const countryId = normalizeSmsBowerCountryId(source.countryId ?? source.country ?? fallback.countryId, DEFAULT_COUNTRY_ID); + return { + activationId, + phoneNumber, + provider: PROVIDER_ID, + serviceCode: normalizeSmsBowerServiceCode(source.serviceCode || fallback.serviceCode || DEFAULT_SERVICE_CODE), + countryId, + countryCode: countryId, + countryLabel: normalizeSmsBowerCountryLabel(source.countryLabel || fallback.countryLabel, `Country #${countryId}`), + successfulUses: Math.max(0, Math.floor(Number(source.successfulUses ?? fallback.successfulUses) || 0)), + maxUses: 1, + }; + } + + function resolveActivationCountry(activation = {}, state = {}) { + const normalized = normalizeActivation(activation) || (activation && typeof activation === 'object' ? activation : {}); + const id = normalizeSmsBowerCountryId(normalized.countryId ?? normalized.countryCode ?? normalized.country, DEFAULT_COUNTRY_ID); + return { id, label: resolveCountryLabel(state, id) }; + } + + function getActivationCountryKey(activation = {}) { + return normalizeCountryKey(activation?.countryCode ?? activation?.countryId ?? activation?.country); + } + + function getActivationPrice(activation = {}) { + const price = Number(activation?.selectedPrice ?? activation?.price ?? activation?.maxPrice); + return Number.isFinite(price) && price >= 0 ? price : null; + } + + function buildAcquireQuery(state = {}, config = resolveConfig(state), countryConfig = {}) { + const range = resolvePriceRange(state); + if (range.invalidRange) { + throw new Error(`SMS Bower 价格区间无效:最低购买价 ${range.minPriceLimit} 高于价格上限 ${range.maxPriceLimit}。`); + } + return { + action: 'getNumber', + service: config.serviceCode, + country: normalizeSmsBowerCountryId(countryConfig.id, DEFAULT_COUNTRY_ID), + maxPrice: range.maxPriceLimit, + minPrice: range.minPriceLimit, + providerIds: normalizeCsvIds(state.smsBowerProviderIds), + exceptProviderIds: normalizeCsvIds(state.smsBowerExceptProviderIds), + phoneException: normalizeCsvIds(state.smsBowerPhoneException), + }; + } + + async function requestActivation(state = {}, options = {}, deps = {}) { + const config = resolveConfig(state, deps); + const blocked = new Set((Array.isArray(options.blockedCountryIds) ? options.blockedCountryIds : []).map((v) => normalizeCountryKey(v)).filter(Boolean)); + const candidates = resolveCountryCandidates(state).filter((entry) => !blocked.has(normalizeCountryKey(entry.id))); + const countryList = candidates.length ? candidates : resolveCountryCandidates(state); + const errors = []; + for (const countryConfig of countryList) { + try { + const payload = await fetchPayload(config, buildAcquireQuery(state, config, countryConfig), `SMS Bower 购买手机号(${countryConfig.label || countryConfig.id})`); + const activation = normalizeActivation(payload, { + countryId: countryConfig.id, + countryLabel: countryConfig.label, + serviceCode: config.serviceCode, + }); + if (activation) return activation; + throw new Error(`SMS Bower 购买手机号返回不可用响应:${describePayload(payload) || '空响应'}`); + } catch (error) { + errors.push(error.message || String(error)); + } + } + throw new Error(`SMS Bower 购买手机号失败:${errors.join(' | ')}`); + } + + async function setActivationStatus(state = {}, activation, status, actionLabel, deps = {}) { + const normalized = normalizeActivation(activation); + if (!normalized) return ''; + const config = resolveConfig(state, deps); + const payload = await fetchPayload(config, { + action: 'setStatus', + status, + id: normalized.activationId, + }, actionLabel); + return describePayload(payload); + } + + async function finishActivation(state = {}, activation, deps = {}) { + return setActivationStatus(state, activation, 6, 'SMS Bower 完成订单', deps); + } + + async function cancelActivation(state = {}, activation, deps = {}) { + return setActivationStatus(state, activation, 8, 'SMS Bower 取消订单', deps); + } + + async function banActivation(state = {}, activation, deps = {}) { + return cancelActivation(state, activation, deps); + } + + async function requestAdditionalSms(state = {}, activation, deps = {}) { + return setActivationStatus(state, activation, 3, 'SMS Bower 请求下一条短信', deps); + } + + async function reuseActivation(state = {}, activation, deps = {}) { + await requestAdditionalSms(state, activation, deps); + return normalizeActivation(activation); + } + + async function rotateActivation(state = {}, activation, options = {}, deps = {}) { + await (String(options?.releaseAction || '').trim().toLowerCase() === 'ban' + ? banActivation(state, activation, deps) + : cancelActivation(state, activation, deps)); + return { currentTicketId: String(activation?.activationId || activation?.id || ''), nextActivation: null }; + } + + function extractVerificationCode(raw = '') { + const trimmed = String(raw || '').trim(); + if (!trimmed) return ''; + const matched = trimmed.match(/\b(\d{4,8})\b/); + return matched?.[1] || trimmed.replace(/^STATUS_OK\s*:\s*/i, '').trim(); + } + + async function pollActivationCode(state = {}, activation, options = {}, deps = {}) { + const normalized = normalizeActivation(activation); + if (!normalized) throw new Error('缺少手机号接码订单。'); + const config = resolveConfig(state, deps); + const timeoutMs = Math.max(1000, Number(options.timeoutMs) || DEFAULT_POLL_TIMEOUT_MS); + const intervalMs = Math.max(1000, Number(options.intervalMs) || DEFAULT_POLL_INTERVAL_MS); + const maxRoundsRaw = Math.floor(Number(options.maxRounds)); + const maxRounds = Number.isFinite(maxRoundsRaw) && maxRoundsRaw > 0 ? maxRoundsRaw : 0; + const start = Date.now(); + let pollCount = 0; + let lastResponse = ''; + while (Date.now() - start < timeoutMs) { + if (maxRounds > 0 && pollCount >= maxRounds) break; + deps.throwIfStopped?.(); + const payload = await fetchPayload(config, { action: 'getStatus', id: normalized.activationId }, 'SMS Bower 查询验证码'); + pollCount += 1; + lastResponse = describePayload(payload); + if (typeof options.onStatus === 'function') { + await options.onStatus({ activation: normalized, elapsedMs: Date.now() - start, pollCount, statusText: lastResponse, timeoutMs }); + } + if (/^STATUS_OK\s*:/i.test(lastResponse)) return extractVerificationCode(lastResponse); + if (/^STATUS_CANCEL\b/i.test(lastResponse)) throw new Error('SMS Bower 查询验证码失败:订单已取消'); + if (typeof options.onWaitingForCode === 'function') { + await options.onWaitingForCode({ activation: normalized, elapsedMs: Date.now() - start, pollCount, statusText: lastResponse, timeoutMs }); + } + await deps.sleepWithStop?.(intervalMs); + } + throw new Error(`${PHONE_CODE_TIMEOUT_ERROR_PREFIX}等待手机验证码超时。${lastResponse ? ` SMS Bower 最后状态:${lastResponse}` : ''}`); + } + + function createProvider(deps = {}) { + const providerDeps = { + fetchImpl: deps.fetchImpl, + sleepWithStop: deps.sleepWithStop, + throwIfStopped: deps.throwIfStopped, + requestTimeoutMs: deps.requestTimeoutMs || DEFAULT_REQUEST_TIMEOUT_MS, + }; + const capabilities = Object.freeze({ + supportsReusableActivation: true, + supportsAutomaticFreeReuse: true, + supportsFreeReusePreservation: false, + supportsPageResend: false, + supportsPageResendProbe: true, + requiresCountrySelection: true, + }); + return { + id: PROVIDER_ID, + label: 'SMS Bower', + capabilities, + defaultCountryId: DEFAULT_COUNTRY_ID, + defaultCountryLabel: DEFAULT_COUNTRY_LABEL, + defaultProduct: DEFAULT_SERVICE_LABEL, + defaultServiceCode: DEFAULT_SERVICE_CODE, + normalizeCountryId: normalizeSmsBowerCountryId, + normalizeCountryLabel: normalizeSmsBowerCountryLabel, + normalizeCountryKey, + normalizeServiceCode: normalizeSmsBowerServiceCode, + normalizeMaxPrice: normalizeSmsBowerMaxPrice, + normalizeCountryFallback: normalizeCountryOrder, + normalizeActivation, + resolveCountryCandidates, + resolveCountryLabel, + resolveActivationCountry, + getActivationCountryKey, + getActivationPrice, + requestActivation: (state, options) => requestActivation(state, options, providerDeps), + reuseActivation: (state, activation) => reuseActivation(state, activation, providerDeps), + finishActivation: (state, activation) => finishActivation(state, activation, providerDeps), + cancelActivation: (state, activation) => cancelActivation(state, activation, providerDeps), + banActivation: (state, activation) => banActivation(state, activation, providerDeps), + requestAdditionalSms: (state, activation) => requestAdditionalSms(state, activation, providerDeps), + rotateActivation: (state, activation, options) => rotateActivation(state, activation, options, providerDeps), + pollActivationCode: (state, activation, options) => pollActivationCode(state, activation, options, providerDeps), + prepareActivationForReuse: async (_state, activation) => ({ activation: normalizeActivation(activation), ignoredPhoneCodeKeys: [] }), + canPersistReusableActivation: () => false, + canPreserveActivationForFreeReuse: () => false, + shouldUsePageResend: () => false, + shouldProbePageResend: () => true, + fetchBalance: (state) => fetchBalance(state, providerDeps), + fetchPrices: (state, countryConfig) => fetchPrices(state, countryConfig, providerDeps), + resolvePriceRange, + formatPriceRangeText, + collectPriceEntries, + describePayload, + }; + } + + return { + PROVIDER_ID, + DEFAULT_COUNTRY_ID, + DEFAULT_COUNTRY_LABEL, + DEFAULT_SERVICE_CODE, + createProvider, + normalizeSmsBowerCountryId, + normalizeSmsBowerCountryLabel, + normalizeCountryKey, + normalizeSmsBowerServiceCode, + normalizeSmsBowerMaxPrice, + normalizeActivation, + }; +}); diff --git a/sidepanel/sidepanel.html b/sidepanel/sidepanel.html index 2876411..21a6475 100644 --- a/sidepanel/sidepanel.html +++ b/sidepanel/sidepanel.html @@ -1367,6 +1367,7 @@ +