@@ -317,6 +317,36 @@
|
|||||||
return Math.max(0, Math.floor(Number(value) || 0));
|
return Math.max(0, Math.floor(Number(value) || 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeStringList(value = []) {
|
||||||
|
const source = Array.isArray(value) ? value : [];
|
||||||
|
const seen = new Set();
|
||||||
|
const normalized = [];
|
||||||
|
source.forEach((entry) => {
|
||||||
|
const text = String(entry || '').trim();
|
||||||
|
if (!text || seen.has(text)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
seen.add(text);
|
||||||
|
normalized.push(text);
|
||||||
|
});
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPhoneSmsCodeKey(message = {}) {
|
||||||
|
return [
|
||||||
|
message.id ?? message.ID ?? '',
|
||||||
|
message.created_at ?? message.date ?? '',
|
||||||
|
message.code ?? '',
|
||||||
|
message.text ?? '',
|
||||||
|
message.message ?? '',
|
||||||
|
].map((part) => String(part || '').trim()).filter(Boolean).join('::');
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectPhoneSmsCodeKeys(payload) {
|
||||||
|
const smsList = Array.isArray(payload?.sms) ? payload.sms : [];
|
||||||
|
return normalizeStringList(smsList.map((message) => buildPhoneSmsCodeKey(message)).filter(Boolean));
|
||||||
|
}
|
||||||
|
|
||||||
function normalizePhoneDigits(value) {
|
function normalizePhoneDigits(value) {
|
||||||
return String(value || '').replace(/\D+/g, '');
|
return String(value || '').replace(/\D+/g, '');
|
||||||
}
|
}
|
||||||
@@ -1322,6 +1352,7 @@
|
|||||||
? normalizeNexSmsCountryId(rawCountryId, 0)
|
? normalizeNexSmsCountryId(rawCountryId, 0)
|
||||||
: normalizeCountryId(rawCountryId, fallbackCountryId)
|
: normalizeCountryId(rawCountryId, fallbackCountryId)
|
||||||
);
|
);
|
||||||
|
const ignoredPhoneCodeKeys = normalizeStringList(record.ignoredPhoneCodeKeys);
|
||||||
return {
|
return {
|
||||||
activationId,
|
activationId,
|
||||||
phoneNumber,
|
phoneNumber,
|
||||||
@@ -1337,6 +1368,7 @@
|
|||||||
...(record.source ? { source: String(record.source || '').trim() } : {}),
|
...(record.source ? { source: String(record.source || '').trim() } : {}),
|
||||||
...(record.phoneCodeReceived ? { phoneCodeReceived: true } : {}),
|
...(record.phoneCodeReceived ? { phoneCodeReceived: true } : {}),
|
||||||
...(record.phoneCodeReceivedAt ? { phoneCodeReceivedAt: Math.max(0, Number(record.phoneCodeReceivedAt) || 0) } : {}),
|
...(record.phoneCodeReceivedAt ? { phoneCodeReceivedAt: Math.max(0, Number(record.phoneCodeReceivedAt) || 0) } : {}),
|
||||||
|
...(ignoredPhoneCodeKeys.length ? { ignoredPhoneCodeKeys } : {}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1382,12 +1414,16 @@
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const recordedAt = Math.max(0, Number(record?.recordedAt) || 0);
|
const recordedAt = Math.max(0, Number(record?.recordedAt) || 0);
|
||||||
return {
|
const reusableActivation = {
|
||||||
...normalized,
|
...normalized,
|
||||||
provider: PHONE_SMS_PROVIDER_HERO,
|
provider: normalized.provider,
|
||||||
source: 'free-manual-reuse',
|
source: 'free-manual-reuse',
|
||||||
...(recordedAt ? { recordedAt } : {}),
|
...(recordedAt ? { recordedAt } : {}),
|
||||||
};
|
};
|
||||||
|
delete reusableActivation.phoneCodeReceived;
|
||||||
|
delete reusableActivation.phoneCodeReceivedAt;
|
||||||
|
delete reusableActivation.ignoredPhoneCodeKeys;
|
||||||
|
return reusableActivation;
|
||||||
}
|
}
|
||||||
|
|
||||||
function markActivationPhoneCodeReceived(activation) {
|
function markActivationPhoneCodeReceived(activation) {
|
||||||
@@ -3901,25 +3937,16 @@
|
|||||||
|
|
||||||
const config = resolvePhoneConfig(state);
|
const config = resolvePhoneConfig(state);
|
||||||
if (config.provider === PHONE_SMS_PROVIDER_5SIM) {
|
if (config.provider === PHONE_SMS_PROVIDER_5SIM) {
|
||||||
const reuseProduct = normalizeFiveSimCountryCode(
|
|
||||||
normalizedActivation.serviceCode || config.product || DEFAULT_FIVE_SIM_PRODUCT,
|
|
||||||
DEFAULT_FIVE_SIM_PRODUCT
|
|
||||||
);
|
|
||||||
const reuseNumber = String(normalizedActivation.phoneNumber || '').replace(/[^\d]/g, '');
|
|
||||||
if (!reuseNumber) {
|
|
||||||
throw new Error('5sim 复用手机号失败:手机号缺失。');
|
|
||||||
}
|
|
||||||
const payload = await fetchFiveSimPayload(
|
const payload = await fetchFiveSimPayload(
|
||||||
config,
|
config,
|
||||||
`/user/reuse/${reuseProduct}/${reuseNumber}`,
|
`/user/check/${encodeURIComponent(normalizedActivation.activationId)}`,
|
||||||
'5sim reuse activation'
|
'5sim reuse activation baseline'
|
||||||
);
|
);
|
||||||
const nextActivation = parseFiveSimActivationPayload(payload, normalizedActivation);
|
return {
|
||||||
if (!nextActivation) {
|
...normalizedActivation,
|
||||||
const text = describeFiveSimPayload(payload);
|
source: '5sim-retained-reuse',
|
||||||
throw createPhoneSmsActionFailureError('5sim reuse activation', text || 'empty response');
|
ignoredPhoneCodeKeys: collectPhoneSmsCodeKeys(payload),
|
||||||
}
|
};
|
||||||
return nextActivation;
|
|
||||||
}
|
}
|
||||||
if (config.provider === PHONE_SMS_PROVIDER_NEXSMS) {
|
if (config.provider === PHONE_SMS_PROVIDER_NEXSMS) {
|
||||||
throw new Error('NexSMS 当前流程不支持复用手机号订单。');
|
throw new Error('NexSMS 当前流程不支持复用手机号订单。');
|
||||||
@@ -4078,6 +4105,38 @@
|
|||||||
return normalizeActivation(activation)?.source === 'free-auto-reuse';
|
return normalizeActivation(activation)?.source === 'free-auto-reuse';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isFiveSimRetainedReuseActivation(activation) {
|
||||||
|
const normalizedActivation = normalizeActivation(activation);
|
||||||
|
return Boolean(
|
||||||
|
normalizedActivation
|
||||||
|
&& normalizedActivation.provider === PHONE_SMS_PROVIDER_5SIM
|
||||||
|
&& normalizedActivation.source === '5sim-retained-reuse'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldRetireFreeReusableActivationOnFailure(state, activation) {
|
||||||
|
const normalizedActivation = normalizeActivation(activation);
|
||||||
|
if (!normalizedActivation) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (normalizedActivation.phoneCodeReceived) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (isFreeAutoReuseActivation(normalizedActivation)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const savedFreeActivation = normalizeFreeReusablePhoneActivation(
|
||||||
|
state?.[FREE_REUSABLE_PHONE_ACTIVATION_STATE_KEY]
|
||||||
|
);
|
||||||
|
return Boolean(
|
||||||
|
savedFreeActivation
|
||||||
|
&& (
|
||||||
|
isSameActivation(savedFreeActivation, normalizedActivation)
|
||||||
|
|| phoneNumbersMatch(savedFreeActivation.phoneNumber, normalizedActivation.phoneNumber)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async function banPhoneActivation(state = {}, activation) {
|
async function banPhoneActivation(state = {}, activation) {
|
||||||
try {
|
try {
|
||||||
if (shouldSkipTerminalStatusForFreeReuse(state, activation)) {
|
if (shouldSkipTerminalStatusForFreeReuse(state, activation)) {
|
||||||
@@ -4139,6 +4198,35 @@
|
|||||||
message: '免费复用手机号激活记录缺失。',
|
message: '免费复用手机号激活记录缺失。',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (normalizedActivation.provider === PHONE_SMS_PROVIDER_5SIM) {
|
||||||
|
const provider = getFiveSimProviderForState(state);
|
||||||
|
if (provider) {
|
||||||
|
let retainedActivation = null;
|
||||||
|
try {
|
||||||
|
retainedActivation = await provider.reuseActivation(state, normalizedActivation);
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
reason: 'five_sim_reuse_check_failed',
|
||||||
|
message: error.message || '5sim 复用手机号基线检查失败。',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
activation: {
|
||||||
|
...retainedActivation,
|
||||||
|
source: 'free-auto-reuse',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
activation: {
|
||||||
|
...normalizedActivation,
|
||||||
|
source: 'free-auto-reuse',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
if (!String(normalizedActivation.activationId || '').trim()) {
|
if (!String(normalizedActivation.activationId || '').trim()) {
|
||||||
return {
|
return {
|
||||||
ok: false,
|
ok: false,
|
||||||
@@ -4311,10 +4399,20 @@
|
|||||||
pollCount += 1;
|
pollCount += 1;
|
||||||
|
|
||||||
const smsList = Array.isArray(payload?.sms) ? payload.sms : [];
|
const smsList = Array.isArray(payload?.sms) ? payload.sms : [];
|
||||||
|
const ignoredPhoneCodeKeys = new Set(normalizeStringList(normalizedActivation.ignoredPhoneCodeKeys));
|
||||||
const directCode = extractVerificationCode(payload?.code || payload?.sms_code);
|
const directCode = extractVerificationCode(payload?.code || payload?.sms_code);
|
||||||
const smsCode = directCode || smsList
|
let smsCode = '';
|
||||||
.map((smsItem) => extractVerificationCode(smsItem?.code || smsItem?.text || smsItem?.message || ''))
|
for (let index = smsList.length - 1; index >= 0; index -= 1) {
|
||||||
.find(Boolean);
|
const smsItem = smsList[index] || {};
|
||||||
|
if (ignoredPhoneCodeKeys.has(buildPhoneSmsCodeKey(smsItem))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
smsCode = extractVerificationCode(smsItem?.code || smsItem?.text || smsItem?.message || '');
|
||||||
|
if (smsCode) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
smsCode = directCode || smsCode;
|
||||||
if (smsCode) {
|
if (smsCode) {
|
||||||
return smsCode;
|
return smsCode;
|
||||||
}
|
}
|
||||||
@@ -5269,6 +5367,7 @@
|
|||||||
};
|
};
|
||||||
delete nextReusableActivation.phoneCodeReceived;
|
delete nextReusableActivation.phoneCodeReceived;
|
||||||
delete nextReusableActivation.phoneCodeReceivedAt;
|
delete nextReusableActivation.phoneCodeReceivedAt;
|
||||||
|
delete nextReusableActivation.ignoredPhoneCodeKeys;
|
||||||
await upsertReusableActivationPool(nextReusableActivation, { state });
|
await upsertReusableActivationPool(nextReusableActivation, { state });
|
||||||
if (!normalizePhoneSmsReuseEnabled(state)) {
|
if (!normalizePhoneSmsReuseEnabled(state)) {
|
||||||
await clearReusableActivation();
|
await clearReusableActivation();
|
||||||
@@ -5293,8 +5392,13 @@
|
|||||||
const normalizedActivation = normalizeActivation(activation);
|
const normalizedActivation = normalizeActivation(activation);
|
||||||
return Boolean(
|
return Boolean(
|
||||||
normalizedActivation
|
normalizedActivation
|
||||||
&& normalizedActivation.provider === PHONE_SMS_PROVIDER_HERO
|
&& (
|
||||||
|
(
|
||||||
|
normalizedActivation.provider === PHONE_SMS_PROVIDER_HERO
|
||||||
&& normalizedActivation.source === 'hero-sms-new'
|
&& normalizedActivation.source === 'hero-sms-new'
|
||||||
|
)
|
||||||
|
|| normalizedActivation.provider === PHONE_SMS_PROVIDER_5SIM
|
||||||
|
)
|
||||||
&& normalizedActivation.phoneCodeReceived
|
&& normalizedActivation.phoneCodeReceived
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -5304,12 +5408,21 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const normalizedActivation = normalizeActivation(activation);
|
const normalizedActivation = normalizeActivation(activation);
|
||||||
if (!normalizedActivation || normalizedActivation.provider !== PHONE_SMS_PROVIDER_HERO) {
|
if (
|
||||||
|
!normalizedActivation
|
||||||
|
|| (
|
||||||
|
normalizedActivation.provider !== PHONE_SMS_PROVIDER_HERO
|
||||||
|
&& normalizedActivation.provider !== PHONE_SMS_PROVIDER_5SIM
|
||||||
|
)
|
||||||
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (isFreeAutoReuseActivation(normalizedActivation)) {
|
if (isFreeAutoReuseActivation(normalizedActivation)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (isFiveSimRetainedReuseActivation(normalizedActivation)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (normalizedActivation.source === 'free-manual-reuse') {
|
if (normalizedActivation.source === 'free-manual-reuse') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -5345,7 +5458,10 @@
|
|||||||
const normalizedActivation = normalizeActivation(activation);
|
const normalizedActivation = normalizeActivation(activation);
|
||||||
if (
|
if (
|
||||||
!normalizedActivation
|
!normalizedActivation
|
||||||
|| normalizedActivation.provider !== PHONE_SMS_PROVIDER_HERO
|
|| (
|
||||||
|
normalizedActivation.provider !== PHONE_SMS_PROVIDER_HERO
|
||||||
|
&& normalizedActivation.provider !== PHONE_SMS_PROVIDER_5SIM
|
||||||
|
)
|
||||||
|| !normalizedActivation.phoneCodeReceived
|
|| !normalizedActivation.phoneCodeReceived
|
||||||
|| isFreeAutoReuseActivation(normalizedActivation)
|
|| isFreeAutoReuseActivation(normalizedActivation)
|
||||||
) {
|
) {
|
||||||
@@ -5412,7 +5528,10 @@
|
|||||||
const normalizedActivation = normalizeActivation(activation);
|
const normalizedActivation = normalizeActivation(activation);
|
||||||
if (
|
if (
|
||||||
!normalizedActivation
|
!normalizedActivation
|
||||||
|| normalizedActivation.provider !== PHONE_SMS_PROVIDER_HERO
|
|| (
|
||||||
|
normalizedActivation.provider !== PHONE_SMS_PROVIDER_HERO
|
||||||
|
&& normalizedActivation.provider !== PHONE_SMS_PROVIDER_5SIM
|
||||||
|
)
|
||||||
|| isFreeAutoReuseActivation(normalizedActivation)
|
|| isFreeAutoReuseActivation(normalizedActivation)
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
@@ -5439,7 +5558,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const maxUses = Math.max(1, Math.floor(Number(savedActivation.maxUses) || DEFAULT_PHONE_NUMBER_MAX_USES));
|
const maxUses = Math.max(1, Math.floor(Number(savedActivation.maxUses) || DEFAULT_PHONE_NUMBER_MAX_USES));
|
||||||
const successfulUses = Math.min(maxUses, Math.max(1, normalizeUseCount(savedActivation.successfulUses)));
|
const successfulUses = Math.min(maxUses, normalizeUseCount(savedActivation.successfulUses) + 1);
|
||||||
if (successfulUses >= maxUses) {
|
if (successfulUses >= maxUses) {
|
||||||
await clearFreeReusableActivation();
|
await clearFreeReusableActivation();
|
||||||
await addLog(
|
await addLog(
|
||||||
@@ -6643,7 +6762,7 @@
|
|||||||
`步骤 9:当前号码 ${activation.phoneNumber} 反复返回添加手机号页,正在更换号码(${usedNumberReplacementAttempts}/${maxNumberReplacementAttempts})。`,
|
`步骤 9:当前号码 ${activation.phoneNumber} 反复返回添加手机号页,正在更换号码(${usedNumberReplacementAttempts}/${maxNumberReplacementAttempts})。`,
|
||||||
'warn'
|
'warn'
|
||||||
);
|
);
|
||||||
if (isFreeAutoReuseActivation(activation)) {
|
if (shouldRetireFreeReusableActivationOnFailure(await getState(), activation)) {
|
||||||
await retireFreeReusableActivation(
|
await retireFreeReusableActivation(
|
||||||
`自动白嫖复用号码 ${activation.phoneNumber} 反复返回添加手机号页。`
|
`自动白嫖复用号码 ${activation.phoneNumber} 反复返回添加手机号页。`
|
||||||
);
|
);
|
||||||
@@ -6703,7 +6822,7 @@
|
|||||||
activation,
|
activation,
|
||||||
await getState()
|
await getState()
|
||||||
);
|
);
|
||||||
if (isFreeAutoReuseActivation(activation)) {
|
if (shouldRetireFreeReusableActivationOnFailure(await getState(), activation)) {
|
||||||
await retireFreeReusableActivation(
|
await retireFreeReusableActivation(
|
||||||
`自动白嫖复用号码 ${activation.phoneNumber} 被目标站拒绝。`
|
`自动白嫖复用号码 ${activation.phoneNumber} 被目标站拒绝。`
|
||||||
);
|
);
|
||||||
@@ -6844,7 +6963,7 @@
|
|||||||
activation,
|
activation,
|
||||||
await getState()
|
await getState()
|
||||||
);
|
);
|
||||||
if (isFreeAutoReuseActivation(activation)) {
|
if (shouldRetireFreeReusableActivationOnFailure(await getState(), activation)) {
|
||||||
await retireFreeReusableActivation(
|
await retireFreeReusableActivation(
|
||||||
`自动白嫖复用号码 ${activation.phoneNumber} 被目标站拒绝。`
|
`自动白嫖复用号码 ${activation.phoneNumber} 被目标站拒绝。`
|
||||||
);
|
);
|
||||||
@@ -6922,8 +7041,9 @@
|
|||||||
|
|
||||||
const latestSuccessState = await getState();
|
const latestSuccessState = await getState();
|
||||||
if (shouldSkipTerminalStatusForFreeReuse(latestSuccessState, activation)) {
|
if (shouldSkipTerminalStatusForFreeReuse(latestSuccessState, activation)) {
|
||||||
|
const terminalProviderLabel = getPhoneSmsProviderLabel(activation.provider);
|
||||||
await addLog(
|
await addLog(
|
||||||
`步骤 9:已跳过 HeroSMS 完成状态,保留 ${activation.phoneNumber} 供白嫖复用。`,
|
`步骤 9:已跳过 ${terminalProviderLabel} 完成状态,保留 ${activation.phoneNumber} 供白嫖复用。`,
|
||||||
'info'
|
'info'
|
||||||
);
|
);
|
||||||
await markFreeReusableActivationAfterInitialSuccess(latestSuccessState, activation);
|
await markFreeReusableActivationAfterInitialSuccess(latestSuccessState, activation);
|
||||||
@@ -6973,7 +7093,7 @@
|
|||||||
if (shouldCancelActivation && activation) {
|
if (shouldCancelActivation && activation) {
|
||||||
await cancelPhoneActivation(state, activation);
|
await cancelPhoneActivation(state, activation);
|
||||||
}
|
}
|
||||||
if (isFreeAutoReuseActivation(activation)) {
|
if (shouldRetireFreeReusableActivationOnFailure(await getState(), activation)) {
|
||||||
await retireFreeReusableActivation(
|
await retireFreeReusableActivation(
|
||||||
`自动白嫖复用号码 ${activation.phoneNumber} 在失败后被更换。`
|
`自动白嫖复用号码 ${activation.phoneNumber} 在失败后被更换。`
|
||||||
);
|
);
|
||||||
@@ -7042,7 +7162,7 @@
|
|||||||
) {
|
) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
if (isFreeAutoReuseActivation(activation)) {
|
if (shouldRetireFreeReusableActivationOnFailure(await getState(), activation)) {
|
||||||
await retireFreeReusableActivation(
|
await retireFreeReusableActivation(
|
||||||
`自动白嫖复用号码 ${activation.phoneNumber} 执行失败:${errorMessage || 'unknown error'}。`
|
`自动白嫖复用号码 ${activation.phoneNumber} 执行失败:${errorMessage || 'unknown error'}。`
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -229,6 +229,21 @@
|
|||||||
return price;
|
return price;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeStringList(value = []) {
|
||||||
|
const source = Array.isArray(value) ? value : [];
|
||||||
|
const seen = new Set();
|
||||||
|
const normalized = [];
|
||||||
|
source.forEach((entry) => {
|
||||||
|
const text = String(entry || '').trim();
|
||||||
|
if (!text || seen.has(text)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
seen.add(text);
|
||||||
|
normalized.push(text);
|
||||||
|
});
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
function buildSortedUniquePriceCandidates(values = []) {
|
function buildSortedUniquePriceCandidates(values = []) {
|
||||||
return Array.from(
|
return Array.from(
|
||||||
new Set(
|
new Set(
|
||||||
@@ -549,6 +564,9 @@
|
|||||||
operator: normalizeFiveSimOperator(record.operator || fallback.operator),
|
operator: normalizeFiveSimOperator(record.operator || fallback.operator),
|
||||||
...(record.price !== undefined ? { price: Number(record.price) } : {}),
|
...(record.price !== undefined ? { price: Number(record.price) } : {}),
|
||||||
...(record.status ? { status: String(record.status) } : {}),
|
...(record.status ? { status: String(record.status) } : {}),
|
||||||
|
...(normalizeStringList(record.ignoredPhoneCodeKeys || fallback.ignoredPhoneCodeKeys).length
|
||||||
|
? { ignoredPhoneCodeKeys: normalizeStringList(record.ignoredPhoneCodeKeys || fallback.ignoredPhoneCodeKeys) }
|
||||||
|
: {}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -746,14 +764,17 @@
|
|||||||
if (!phoneDigits) {
|
if (!phoneDigits) {
|
||||||
throw new Error('可复用的 5sim 手机号无效。');
|
throw new Error('可复用的 5sim 手机号无效。');
|
||||||
}
|
}
|
||||||
|
// 5sim 的 /user/reuse 会按手机号重新创建付费订单,并不是保留原订单继续收短信。
|
||||||
|
// 真正的复用应继续轮询原 activationId,避免产生新的订单。
|
||||||
const config = resolveConfig(state, deps);
|
const config = resolveConfig(state, deps);
|
||||||
const numberWithoutPlus = String(normalizedActivation.phoneNumber || '')
|
const payload = await fetchJson(config, `/v1/user/check/${encodeURIComponent(normalizedActivation.activationId)}`, {
|
||||||
.replace(/^\+/, '')
|
actionLabel: '5sim 复用手机号基线检查',
|
||||||
.replace(/[^0-9]+/g, '');
|
|
||||||
const payload = await fetchJson(config, `/v1/user/reuse/${DEFAULT_PRODUCT}/${numberWithoutPlus || phoneDigits}`, {
|
|
||||||
actionLabel: '5sim 复用手机号',
|
|
||||||
});
|
});
|
||||||
return normalizeActivation(payload, normalizedActivation);
|
return {
|
||||||
|
...normalizedActivation,
|
||||||
|
source: '5sim-retained-reuse',
|
||||||
|
ignoredPhoneCodeKeys: collectSmsCodeKeys(payload),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function finishActivation(state = {}, activation, deps = {}) {
|
async function finishActivation(state = {}, activation, deps = {}) {
|
||||||
@@ -795,10 +816,30 @@
|
|||||||
return digitMatch?.[1] || trimmed;
|
return digitMatch?.[1] || trimmed;
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractCodeFromOrder(payload) {
|
function buildSmsCodeKey(message = {}) {
|
||||||
|
const text = [
|
||||||
|
message.id ?? message.ID ?? '',
|
||||||
|
message.created_at ?? message.date ?? '',
|
||||||
|
message.code ?? '',
|
||||||
|
message.text ?? '',
|
||||||
|
message.message ?? '',
|
||||||
|
].map((part) => String(part || '').trim()).filter(Boolean).join('::');
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectSmsCodeKeys(payload) {
|
||||||
const smsList = Array.isArray(payload?.sms) ? payload.sms : [];
|
const smsList = Array.isArray(payload?.sms) ? payload.sms : [];
|
||||||
|
return normalizeStringList(smsList.map((message) => buildSmsCodeKey(message)).filter(Boolean));
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractCodeFromOrder(payload, ignoredPhoneCodeKeys = []) {
|
||||||
|
const smsList = Array.isArray(payload?.sms) ? payload.sms : [];
|
||||||
|
const ignoredKeys = new Set(normalizeStringList(ignoredPhoneCodeKeys));
|
||||||
for (let index = smsList.length - 1; index >= 0; index -= 1) {
|
for (let index = smsList.length - 1; index >= 0; index -= 1) {
|
||||||
const message = smsList[index] || {};
|
const message = smsList[index] || {};
|
||||||
|
if (ignoredKeys.has(buildSmsCodeKey(message))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const code = extractVerificationCode(message.code) || extractVerificationCode(message.text);
|
const code = extractVerificationCode(message.code) || extractVerificationCode(message.text);
|
||||||
if (code) {
|
if (code) {
|
||||||
return code;
|
return code;
|
||||||
@@ -840,7 +881,7 @@
|
|||||||
timeoutMs,
|
timeoutMs,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const code = extractCodeFromOrder(payload);
|
const code = extractCodeFromOrder(payload, normalizedActivation.ignoredPhoneCodeKeys);
|
||||||
if (code) {
|
if (code) {
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ test('5sim provider maps countries and prices', async () => {
|
|||||||
assert.deepStrictEqual(entries, [{ cost: 10, count: 2, inStock: true }]);
|
assert.deepStrictEqual(entries, [{ cost: 10, count: 2, inStock: true }]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('5sim provider buys, checks, finishes, cancels, bans, and reuses activation', async () => {
|
test('5sim provider buys, checks, finishes, cancels, bans, and keeps original activation on reuse', async () => {
|
||||||
const requests = [];
|
const requests = [];
|
||||||
const provider = api.createProvider({
|
const provider = api.createProvider({
|
||||||
fetchImpl: async (url, options = {}) => {
|
fetchImpl: async (url, options = {}) => {
|
||||||
@@ -92,9 +92,7 @@ test('5sim provider buys, checks, finishes, cancels, bans, and reuses activation
|
|||||||
if (parsed.pathname === '/v1/user/finish/1001') return createTextResponse({ status: 'FINISHED' });
|
if (parsed.pathname === '/v1/user/finish/1001') return createTextResponse({ status: 'FINISHED' });
|
||||||
if (parsed.pathname === '/v1/user/cancel/1001') return createTextResponse({ status: 'CANCELED' });
|
if (parsed.pathname === '/v1/user/cancel/1001') return createTextResponse({ status: 'CANCELED' });
|
||||||
if (parsed.pathname === '/v1/user/ban/1001') return createTextResponse({ status: 'BANNED' });
|
if (parsed.pathname === '/v1/user/ban/1001') return createTextResponse({ status: 'BANNED' });
|
||||||
if (parsed.pathname === '/v1/user/reuse/openai/84901123456') {
|
if (parsed.pathname.includes('/reuse/')) throw new Error(`5sim free reuse should not create a new order: ${parsed.pathname}`);
|
||||||
return createTextResponse({ id: 1002, phone: '+84901123456', country: 'vietnam', status: 'PENDING' });
|
|
||||||
}
|
|
||||||
throw new Error(`unexpected ${parsed.pathname}`);
|
throw new Error(`unexpected ${parsed.pathname}`);
|
||||||
},
|
},
|
||||||
sleepWithStop: async () => {},
|
sleepWithStop: async () => {},
|
||||||
@@ -113,7 +111,8 @@ test('5sim provider buys, checks, finishes, cancels, bans, and reuses activation
|
|||||||
assert.equal(activation.activationId, '1001');
|
assert.equal(activation.activationId, '1001');
|
||||||
assert.equal(activation.countryId, 'vietnam');
|
assert.equal(activation.countryId, 'vietnam');
|
||||||
assert.equal(code, '112233');
|
assert.equal(code, '112233');
|
||||||
assert.equal(reused.activationId, '1002');
|
assert.equal(reused.activationId, '1001');
|
||||||
|
assert.deepStrictEqual(reused.ignoredPhoneCodeKeys, ['code 112233']);
|
||||||
const buy = requests.find((entry) => entry.url.pathname.includes('/buy/activation'));
|
const buy = requests.find((entry) => entry.url.pathname.includes('/buy/activation'));
|
||||||
assert.equal(buy.url.searchParams.get('maxPrice'), '12');
|
assert.equal(buy.url.searchParams.get('maxPrice'), '12');
|
||||||
assert.equal(buy.url.searchParams.get('reuse'), '1');
|
assert.equal(buy.url.searchParams.get('reuse'), '1');
|
||||||
@@ -127,7 +126,7 @@ test('5sim provider buys, checks, finishes, cancels, bans, and reuses activation
|
|||||||
'/v1/user/finish/1001',
|
'/v1/user/finish/1001',
|
||||||
'/v1/user/cancel/1001',
|
'/v1/user/cancel/1001',
|
||||||
'/v1/user/ban/1001',
|
'/v1/user/ban/1001',
|
||||||
'/v1/user/reuse/openai/84901123456',
|
'/v1/user/check/1001',
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2872,7 +2872,7 @@ test('phone verification helper treats HeroSMS STATUS_WAIT_RETRY payload status
|
|||||||
assert.equal(statusUpdates[0], 'STATUS_WAIT_RETRY:846171');
|
assert.equal(statusUpdates[0], 'STATUS_WAIT_RETRY:846171');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('phone verification helper reuses 5sim number via product-plus-number endpoint', async () => {
|
test('phone verification helper reuses 5sim by keeping the original activation', async () => {
|
||||||
const requests = [];
|
const requests = [];
|
||||||
const helpers = api.createPhoneVerificationHelpers({
|
const helpers = api.createPhoneVerificationHelpers({
|
||||||
addLog: async () => {},
|
addLog: async () => {},
|
||||||
@@ -2880,20 +2880,19 @@ test('phone verification helper reuses 5sim number via product-plus-number endpo
|
|||||||
fetchImpl: async (url) => {
|
fetchImpl: async (url) => {
|
||||||
const parsedUrl = new URL(url);
|
const parsedUrl = new URL(url);
|
||||||
requests.push(parsedUrl.pathname);
|
requests.push(parsedUrl.pathname);
|
||||||
if (parsedUrl.pathname !== '/v1/user/reuse/openai/447911123456') {
|
if (parsedUrl.pathname === '/v1/user/check/600001') {
|
||||||
throw new Error(`Unexpected 5sim request: ${parsedUrl.pathname}`);
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
ok: true,
|
ok: true,
|
||||||
status: 200,
|
status: 200,
|
||||||
text: async () => JSON.stringify({
|
text: async () => JSON.stringify({
|
||||||
id: 700002,
|
id: '600001',
|
||||||
phone: '+447911123456',
|
phone: '+44 7911-123-456',
|
||||||
country: 'england',
|
status: 'RECEIVED',
|
||||||
country_name: 'England',
|
sms: [{ id: 'old', code: '111111' }],
|
||||||
product: 'openai',
|
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
throw new Error(`Unexpected 5sim request: ${parsedUrl.pathname}`);
|
||||||
},
|
},
|
||||||
getState: async () => ({
|
getState: async () => ({
|
||||||
phoneSmsProvider: '5sim',
|
phoneSmsProvider: '5sim',
|
||||||
@@ -2929,17 +2928,18 @@ test('phone verification helper reuses 5sim number via product-plus-number endpo
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert.deepStrictEqual(nextActivation, {
|
assert.deepStrictEqual(nextActivation, {
|
||||||
activationId: '700002',
|
activationId: '600001',
|
||||||
phoneNumber: '+447911123456',
|
phoneNumber: '+44 7911-123-456',
|
||||||
provider: '5sim',
|
provider: '5sim',
|
||||||
serviceCode: 'openai',
|
serviceCode: 'openai',
|
||||||
countryId: 'england',
|
countryId: 'england',
|
||||||
countryCode: 'england',
|
countryCode: 'england',
|
||||||
countryLabel: 'England',
|
|
||||||
successfulUses: 0,
|
successfulUses: 0,
|
||||||
maxUses: 1,
|
maxUses: 1,
|
||||||
|
source: '5sim-retained-reuse',
|
||||||
|
ignoredPhoneCodeKeys: ['old::111111'],
|
||||||
});
|
});
|
||||||
assert.deepStrictEqual(requests, ['/v1/user/reuse/openai/447911123456']);
|
assert.deepStrictEqual(requests, ['/v1/user/check/600001']);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('phone verification helper acquires a number from NexSMS with ordered fallback countries', async () => {
|
test('phone verification helper acquires a number from NexSMS with ordered fallback countries', async () => {
|
||||||
@@ -6101,6 +6101,189 @@ test('phone verification helper preserves newly saved free-reuse activation afte
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('phone verification helper auto free-reuses 5sim by polling the retained order without reuse or finish', async () => {
|
||||||
|
const requests = [];
|
||||||
|
let checkCount = 0;
|
||||||
|
let currentState = {
|
||||||
|
phoneSmsProvider: '5sim',
|
||||||
|
fiveSimApiKey: 'demo-key',
|
||||||
|
fiveSimCountryOrder: ['vietnam'],
|
||||||
|
fiveSimOperator: 'any',
|
||||||
|
freePhoneReuseEnabled: true,
|
||||||
|
freePhoneReuseAutoEnabled: true,
|
||||||
|
verificationResendCount: 0,
|
||||||
|
phoneCodeWaitSeconds: 60,
|
||||||
|
phoneCodeTimeoutWindows: 1,
|
||||||
|
phoneCodePollIntervalSeconds: 1,
|
||||||
|
phoneCodePollMaxRounds: 2,
|
||||||
|
currentPhoneActivation: null,
|
||||||
|
reusablePhoneActivation: null,
|
||||||
|
freeReusablePhoneActivation: {
|
||||||
|
activationId: 'five-free-1',
|
||||||
|
phoneNumber: '+84901122334',
|
||||||
|
provider: '5sim',
|
||||||
|
serviceCode: 'openai',
|
||||||
|
countryId: 'vietnam',
|
||||||
|
countryLabel: '越南 (Vietnam)',
|
||||||
|
successfulUses: 1,
|
||||||
|
maxUses: 3,
|
||||||
|
source: 'free-manual-reuse',
|
||||||
|
phoneCodeReceived: true,
|
||||||
|
phoneCodeReceivedAt: 1716000000000,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const fiveSimSource = fs.readFileSync('phone-sms/providers/five-sim.js', 'utf8');
|
||||||
|
const fiveSimModule = new Function('self', `${fiveSimSource}; return self.PhoneSmsFiveSimProvider;`)({});
|
||||||
|
const helpers = api.createPhoneVerificationHelpers({
|
||||||
|
addLog: async () => {},
|
||||||
|
ensureStep8SignupPageReady: async () => {},
|
||||||
|
createFiveSimProvider: fiveSimModule.createProvider,
|
||||||
|
fetchImpl: async (url) => {
|
||||||
|
const parsedUrl = new URL(url);
|
||||||
|
requests.push(parsedUrl);
|
||||||
|
if (parsedUrl.pathname === '/v1/user/check/five-free-1') {
|
||||||
|
checkCount += 1;
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
status: 200,
|
||||||
|
text: async () => JSON.stringify({
|
||||||
|
id: 'five-free-1',
|
||||||
|
phone: '+84901122334',
|
||||||
|
status: 'RECEIVED',
|
||||||
|
sms: checkCount === 1
|
||||||
|
? [{ id: 'old', code: '111111' }]
|
||||||
|
: [{ id: 'old', code: '111111' }, { id: 'new', code: '334455' }],
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (parsedUrl.pathname.includes('/reuse/') || parsedUrl.pathname.includes('/finish/')) {
|
||||||
|
throw new Error(`5sim free reuse should not call terminal/reuse endpoint: ${parsedUrl.pathname}`);
|
||||||
|
}
|
||||||
|
throw new Error(`Unexpected 5sim path: ${parsedUrl.pathname}`);
|
||||||
|
},
|
||||||
|
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
|
||||||
|
getState: async () => ({ ...currentState }),
|
||||||
|
sendToContentScriptResilient: async (_source, message) => {
|
||||||
|
if (message.type === 'SUBMIT_PHONE_NUMBER') {
|
||||||
|
assert.equal(message.payload.phoneNumber, '+84901122334');
|
||||||
|
return { phoneVerificationPage: true, url: 'https://auth.openai.com/phone-verification' };
|
||||||
|
}
|
||||||
|
if (message.type === 'SUBMIT_PHONE_VERIFICATION_CODE') {
|
||||||
|
assert.equal(message.payload.code, '334455');
|
||||||
|
return { success: true, consentReady: true, url: 'https://auth.openai.com/authorize' };
|
||||||
|
}
|
||||||
|
throw new Error(`Unexpected content-script message: ${message.type}`);
|
||||||
|
},
|
||||||
|
setState: async (updates) => {
|
||||||
|
currentState = { ...currentState, ...updates };
|
||||||
|
},
|
||||||
|
sleepWithStop: async () => {},
|
||||||
|
throwIfStopped: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await helpers.completePhoneVerificationFlow(1, {
|
||||||
|
addPhonePage: true,
|
||||||
|
phoneVerificationPage: false,
|
||||||
|
url: 'https://auth.openai.com/add-phone',
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepStrictEqual(result, {
|
||||||
|
success: true,
|
||||||
|
consentReady: true,
|
||||||
|
url: 'https://auth.openai.com/authorize',
|
||||||
|
});
|
||||||
|
assert.equal(currentState.freeReusablePhoneActivation.provider, '5sim');
|
||||||
|
assert.equal(currentState.freeReusablePhoneActivation.activationId, 'five-free-1');
|
||||||
|
assert.equal(currentState.freeReusablePhoneActivation.successfulUses, 2);
|
||||||
|
assert.equal(Object.prototype.hasOwnProperty.call(currentState.freeReusablePhoneActivation, 'phoneCodeReceived'), false);
|
||||||
|
assert.equal(currentState.reusablePhoneActivation, null);
|
||||||
|
assert.deepStrictEqual(requests.map((url) => url.pathname), ['/v1/user/check/five-free-1', '/v1/user/check/five-free-1']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('phone verification helper retires failed 5sim free-reuse record instead of retrying stale order', async () => {
|
||||||
|
const requests = [];
|
||||||
|
let currentState = {
|
||||||
|
phoneSmsProvider: '5sim',
|
||||||
|
fiveSimApiKey: 'demo-key',
|
||||||
|
fiveSimCountryOrder: ['vietnam'],
|
||||||
|
fiveSimOperator: 'any',
|
||||||
|
freePhoneReuseEnabled: true,
|
||||||
|
freePhoneReuseAutoEnabled: true,
|
||||||
|
verificationResendCount: 0,
|
||||||
|
phoneCodeWaitSeconds: 60,
|
||||||
|
phoneCodeTimeoutWindows: 1,
|
||||||
|
phoneCodePollIntervalSeconds: 1,
|
||||||
|
phoneCodePollMaxRounds: 1,
|
||||||
|
currentPhoneActivation: null,
|
||||||
|
reusablePhoneActivation: null,
|
||||||
|
freeReusablePhoneActivation: {
|
||||||
|
activationId: 'five-free-stale',
|
||||||
|
phoneNumber: '+84901122999',
|
||||||
|
provider: '5sim',
|
||||||
|
serviceCode: 'openai',
|
||||||
|
countryId: 'vietnam',
|
||||||
|
countryLabel: '越南 (Vietnam)',
|
||||||
|
successfulUses: 1,
|
||||||
|
maxUses: 3,
|
||||||
|
source: 'free-manual-reuse',
|
||||||
|
phoneCodeReceived: true,
|
||||||
|
phoneCodeReceivedAt: 1716000000000,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const fiveSimSource = fs.readFileSync('phone-sms/providers/five-sim.js', 'utf8');
|
||||||
|
const fiveSimModule = new Function('self', `${fiveSimSource}; return self.PhoneSmsFiveSimProvider;`)({});
|
||||||
|
const helpers = api.createPhoneVerificationHelpers({
|
||||||
|
addLog: async () => {},
|
||||||
|
ensureStep8SignupPageReady: async () => {},
|
||||||
|
createFiveSimProvider: fiveSimModule.createProvider,
|
||||||
|
fetchImpl: async (url) => {
|
||||||
|
const parsedUrl = new URL(url);
|
||||||
|
requests.push(parsedUrl);
|
||||||
|
if (parsedUrl.pathname === '/v1/user/check/five-free-stale') {
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
status: 200,
|
||||||
|
text: async () => JSON.stringify({
|
||||||
|
id: 'five-free-stale',
|
||||||
|
phone: '+84901122999',
|
||||||
|
status: 'FINISHED',
|
||||||
|
sms: [{ id: 'old', code: '111111' }],
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw new Error(`Unexpected 5sim path: ${parsedUrl.pathname}`);
|
||||||
|
},
|
||||||
|
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
|
||||||
|
getState: async () => ({ ...currentState }),
|
||||||
|
sendToContentScriptResilient: async (_source, message) => {
|
||||||
|
if (message.type === 'SUBMIT_PHONE_NUMBER') {
|
||||||
|
assert.equal(message.payload.phoneNumber, '+84901122999');
|
||||||
|
return { phoneVerificationPage: true, url: 'https://auth.openai.com/phone-verification' };
|
||||||
|
}
|
||||||
|
throw new Error(`Unexpected content-script message: ${message.type}`);
|
||||||
|
},
|
||||||
|
setState: async (updates) => {
|
||||||
|
currentState = { ...currentState, ...updates };
|
||||||
|
},
|
||||||
|
sleepWithStop: async () => {},
|
||||||
|
throwIfStopped: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
await assert.rejects(
|
||||||
|
helpers.completePhoneVerificationFlow(1, {
|
||||||
|
addPhonePage: true,
|
||||||
|
phoneVerificationPage: false,
|
||||||
|
url: 'https://auth.openai.com/add-phone',
|
||||||
|
}),
|
||||||
|
/5sim 查询验证码失败:订单状态 FINISHED|5sim 订单在收到短信前已结束:FINISHED/
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(currentState.freeReusablePhoneActivation, null);
|
||||||
|
assert.deepStrictEqual(requests.map((url) => url.pathname), ['/v1/user/check/five-free-stale', '/v1/user/check/five-free-stale']);
|
||||||
|
});
|
||||||
|
|
||||||
test('phone verification helper replaces number immediately when resend is throttled and does not spam resend clicks', async () => {
|
test('phone verification helper replaces number immediately when resend is throttled and does not spam resend clicks', async () => {
|
||||||
const requests = [];
|
const requests = [];
|
||||||
const messages = [];
|
const messages = [];
|
||||||
@@ -8321,8 +8504,9 @@ test('phone verification helper routes 5sim buy, check, and finish by current ac
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('phone verification helper routes 5sim reusable activation through reuse endpoint', async () => {
|
test('phone verification helper keeps 5sim reusable activation on the original order', async () => {
|
||||||
const requests = [];
|
const requests = [];
|
||||||
|
let checkCount = 0;
|
||||||
let currentState = {
|
let currentState = {
|
||||||
phoneSmsProvider: '5sim',
|
phoneSmsProvider: '5sim',
|
||||||
fiveSimApiKey: 'demo-key',
|
fiveSimApiKey: 'demo-key',
|
||||||
@@ -8334,7 +8518,7 @@ test('phone verification helper routes 5sim reusable activation through reuse en
|
|||||||
phoneCodeWaitSeconds: 60,
|
phoneCodeWaitSeconds: 60,
|
||||||
phoneCodeTimeoutWindows: 1,
|
phoneCodeTimeoutWindows: 1,
|
||||||
phoneCodePollIntervalSeconds: 1,
|
phoneCodePollIntervalSeconds: 1,
|
||||||
phoneCodePollMaxRounds: 1,
|
phoneCodePollMaxRounds: 2,
|
||||||
currentPhoneActivation: null,
|
currentPhoneActivation: null,
|
||||||
reusablePhoneActivation: {
|
reusablePhoneActivation: {
|
||||||
activationId: '4001',
|
activationId: '4001',
|
||||||
@@ -8357,14 +8541,23 @@ test('phone verification helper routes 5sim reusable activation through reuse en
|
|||||||
fetchImpl: async (url) => {
|
fetchImpl: async (url) => {
|
||||||
const parsedUrl = new URL(url);
|
const parsedUrl = new URL(url);
|
||||||
requests.push(parsedUrl);
|
requests.push(parsedUrl);
|
||||||
if (parsedUrl.pathname === '/v1/user/reuse/openai/84901122334') {
|
if (parsedUrl.pathname === '/v1/user/check/4001') {
|
||||||
return { ok: true, status: 200, text: async () => JSON.stringify({ id: 4002, phone: '+84901122334', country: 'vietnam', status: 'PENDING' }) };
|
checkCount += 1;
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
status: 200,
|
||||||
|
text: async () => JSON.stringify({
|
||||||
|
id: 4001,
|
||||||
|
phone: '+84901122334',
|
||||||
|
status: 'RECEIVED',
|
||||||
|
sms: checkCount === 1
|
||||||
|
? [{ id: 'old', code: '111111' }]
|
||||||
|
: [{ id: 'old', code: '111111' }, { id: 'new', code: '654321' }],
|
||||||
|
}),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
if (parsedUrl.pathname === '/v1/user/check/4002') {
|
if (parsedUrl.pathname.includes('/reuse/') || parsedUrl.pathname.includes('/finish/')) {
|
||||||
return { ok: true, status: 200, text: async () => JSON.stringify({ id: 4002, phone: '+447911223344', status: 'RECEIVED', sms: [{ code: '654321' }] }) };
|
throw new Error(`5sim free reuse should not call terminal/reuse endpoint: ${parsedUrl.pathname}`);
|
||||||
}
|
|
||||||
if (parsedUrl.pathname === '/v1/user/finish/4002') {
|
|
||||||
return { ok: true, status: 200, text: async () => JSON.stringify({ status: 'FINISHED' }) };
|
|
||||||
}
|
}
|
||||||
throw new Error(`Unexpected 5sim path: ${parsedUrl.pathname}`);
|
throw new Error(`Unexpected 5sim path: ${parsedUrl.pathname}`);
|
||||||
},
|
},
|
||||||
@@ -8375,6 +8568,7 @@ test('phone verification helper routes 5sim reusable activation through reuse en
|
|||||||
return { phoneVerificationPage: true, url: 'https://auth.openai.com/phone-verification' };
|
return { phoneVerificationPage: true, url: 'https://auth.openai.com/phone-verification' };
|
||||||
}
|
}
|
||||||
if (message.type === 'SUBMIT_PHONE_VERIFICATION_CODE') {
|
if (message.type === 'SUBMIT_PHONE_VERIFICATION_CODE') {
|
||||||
|
assert.equal(message.payload.code, '654321');
|
||||||
return { success: true, consentReady: true, url: 'https://auth.openai.com/authorize' };
|
return { success: true, consentReady: true, url: 'https://auth.openai.com/authorize' };
|
||||||
}
|
}
|
||||||
throw new Error(`Unexpected content-script message: ${message.type}`);
|
throw new Error(`Unexpected content-script message: ${message.type}`);
|
||||||
@@ -8397,14 +8591,13 @@ test('phone verification helper routes 5sim reusable activation through reuse en
|
|||||||
consentReady: true,
|
consentReady: true,
|
||||||
url: 'https://auth.openai.com/authorize',
|
url: 'https://auth.openai.com/authorize',
|
||||||
});
|
});
|
||||||
assert.equal(currentState.reusablePhoneActivation.activationId, '4002');
|
assert.equal(currentState.reusablePhoneActivation.activationId, '4001');
|
||||||
assert.equal(currentState.reusablePhoneActivation.successfulUses, 1);
|
assert.equal(currentState.reusablePhoneActivation.successfulUses, 2);
|
||||||
assert.deepStrictEqual(
|
assert.deepStrictEqual(
|
||||||
requests.map((url) => url.pathname),
|
requests.map((url) => url.pathname),
|
||||||
[
|
[
|
||||||
'/v1/user/reuse/openai/84901122334',
|
'/v1/user/check/4001',
|
||||||
'/v1/user/check/4002',
|
'/v1/user/check/4001',
|
||||||
'/v1/user/finish/4002',
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user