feat: add account run history module and logging functionality
- Implemented `background/account-run-history.js` to persist account run results (success, failure, stop) in `chrome.storage.local` and log to a text file when the local Hotmail helper is enabled. - Enhanced `background/auto-run-controller.js` and `background/message-router.js` to utilize the new account run record functionality. - Updated steps in `background/steps/fetch-login-code.js`, `background/steps/fetch-signup-code.js`, and `background/steps/oauth-login.js` to handle retries and logging appropriately. - Introduced tests for account run history and step retry limits to ensure functionality and reliability. - Modified documentation to reflect new features and changes in behavior for specific providers (e.g., 2925).
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
(function attachBackgroundAccountRunHistory(root, factory) {
|
||||
root.MultiPageBackgroundAccountRunHistory = factory();
|
||||
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundAccountRunHistoryModule() {
|
||||
function createAccountRunHistoryHelpers(deps = {}) {
|
||||
const {
|
||||
ACCOUNT_RUN_HISTORY_STORAGE_KEY = 'accountRunHistory',
|
||||
addLog,
|
||||
buildHotmailLocalEndpoint,
|
||||
chrome,
|
||||
getErrorMessage,
|
||||
getState,
|
||||
HOTMAIL_SERVICE_MODE_LOCAL = 'local',
|
||||
normalizeHotmailLocalBaseUrl,
|
||||
} = deps;
|
||||
|
||||
function normalizeAccountRunHistory(records) {
|
||||
if (!Array.isArray(records)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return records
|
||||
.filter((item) => item && typeof item === 'object')
|
||||
.map((item) => ({
|
||||
email: String(item.email || '').trim(),
|
||||
password: String(item.password || '').trim(),
|
||||
status: String(item.status || '').trim().toLowerCase(),
|
||||
recordedAt: String(item.recordedAt || '').trim(),
|
||||
reason: String(item.reason || '').trim(),
|
||||
}))
|
||||
.filter((item) => item.email && item.password && item.status);
|
||||
}
|
||||
|
||||
async function getPersistedAccountRunHistory() {
|
||||
try {
|
||||
const stored = await chrome.storage.local.get(ACCOUNT_RUN_HISTORY_STORAGE_KEY);
|
||||
return normalizeAccountRunHistory(stored[ACCOUNT_RUN_HISTORY_STORAGE_KEY]);
|
||||
} catch (err) {
|
||||
console.warn('[MultiPage:account-run-history] Failed to read account run history:', err?.message || err);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function buildAccountRunHistoryRecord(state = {}, status = '', reason = '') {
|
||||
const email = String(state.email || '').trim();
|
||||
const password = String(state.password || state.customPassword || '').trim();
|
||||
const normalizedStatus = String(status || '').trim().toLowerCase();
|
||||
const normalizedReason = String(reason || '').trim();
|
||||
|
||||
if (!email || !password || !normalizedStatus) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
email,
|
||||
password,
|
||||
status: normalizedStatus,
|
||||
recordedAt: new Date().toISOString(),
|
||||
reason: normalizedReason,
|
||||
};
|
||||
}
|
||||
|
||||
async function appendAccountRunHistoryRecord(status, stateOverride = null, reason = '') {
|
||||
const state = stateOverride || await getState();
|
||||
const record = buildAccountRunHistoryRecord(state, status, reason);
|
||||
if (!record) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const history = await getPersistedAccountRunHistory();
|
||||
history.push(record);
|
||||
await chrome.storage.local.set({
|
||||
[ACCOUNT_RUN_HISTORY_STORAGE_KEY]: history,
|
||||
});
|
||||
return record;
|
||||
}
|
||||
|
||||
function shouldAppendAccountRunTextFile(state = {}) {
|
||||
const serviceMode = String(state.hotmailServiceMode || '').trim().toLowerCase();
|
||||
if (serviceMode !== HOTMAIL_SERVICE_MODE_LOCAL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const helperBaseUrl = normalizeHotmailLocalBaseUrl(state.hotmailLocalBaseUrl);
|
||||
return Boolean(helperBaseUrl);
|
||||
}
|
||||
|
||||
async function appendAccountRunHistoryTextFile(record, stateOverride = null) {
|
||||
const normalizedRecord = record && typeof record === 'object'
|
||||
? record
|
||||
: buildAccountRunHistoryRecord(stateOverride || await getState(), '');
|
||||
if (!normalizedRecord?.email || !normalizedRecord?.password || !normalizedRecord?.status) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const state = stateOverride || await getState();
|
||||
if (!shouldAppendAccountRunTextFile(state)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const helperBaseUrl = normalizeHotmailLocalBaseUrl(state.hotmailLocalBaseUrl);
|
||||
let response;
|
||||
try {
|
||||
response = await fetch(buildHotmailLocalEndpoint(helperBaseUrl, '/append-account-log'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: normalizedRecord.email,
|
||||
password: normalizedRecord.password,
|
||||
status: normalizedRecord.status,
|
||||
recordedAt: normalizedRecord.recordedAt,
|
||||
reason: normalizedRecord.reason || '',
|
||||
}),
|
||||
});
|
||||
} catch (err) {
|
||||
throw new Error(`账号文本记录写入失败:无法连接本地 helper(${getErrorMessage(err)})`);
|
||||
}
|
||||
|
||||
let payload = null;
|
||||
try {
|
||||
payload = await response.json();
|
||||
} catch (err) {
|
||||
throw new Error(`账号文本记录写入失败:本地 helper 返回了无法解析的响应(${getErrorMessage(err)})`);
|
||||
}
|
||||
|
||||
if (!response.ok || payload?.ok === false) {
|
||||
throw new Error(`账号文本记录写入失败:${payload?.error || `HTTP ${response.status}`}`);
|
||||
}
|
||||
|
||||
return payload?.filePath || '';
|
||||
}
|
||||
|
||||
async function appendAccountRunRecord(status, stateOverride = null, reason = '') {
|
||||
const state = stateOverride || await getState();
|
||||
const record = await appendAccountRunHistoryRecord(status, state, reason);
|
||||
if (!record) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const filePath = await appendAccountRunHistoryTextFile(record, state);
|
||||
if (filePath) {
|
||||
await addLog(`账号记录已追加到本地文本:${filePath}`, 'info');
|
||||
}
|
||||
} catch (err) {
|
||||
await addLog(getErrorMessage(err), 'warn');
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
return {
|
||||
appendAccountRunRecord,
|
||||
appendAccountRunHistoryRecord,
|
||||
appendAccountRunHistoryTextFile,
|
||||
buildAccountRunHistoryRecord,
|
||||
getPersistedAccountRunHistory,
|
||||
normalizeAccountRunHistory,
|
||||
shouldAppendAccountRunTextFile,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
createAccountRunHistoryHelpers,
|
||||
};
|
||||
});
|
||||
@@ -4,6 +4,7 @@
|
||||
function createAutoRunController(deps = {}) {
|
||||
const {
|
||||
addLog,
|
||||
appendAccountRunRecord,
|
||||
AUTO_RUN_MAX_RETRIES_PER_ROUND,
|
||||
AUTO_RUN_RETRY_DELAY_MS,
|
||||
AUTO_RUN_TIMER_KIND_BEFORE_RETRY,
|
||||
@@ -303,6 +304,7 @@
|
||||
|
||||
for (let targetRun = resumeCurrentRun; targetRun <= totalRuns; targetRun += 1) {
|
||||
const roundSummary = roundSummaries[targetRun - 1];
|
||||
let roundRecordAppended = false;
|
||||
const resumingCurrentRound = continueCurrentOnFirstAttempt && targetRun === resumeCurrentRun;
|
||||
let attemptRun = resumingCurrentRound ? resumeAttemptRun : 1;
|
||||
let reuseExistingProgress = resumingCurrentRound;
|
||||
@@ -377,6 +379,21 @@
|
||||
forceFreshTabsNextRun = false;
|
||||
}
|
||||
|
||||
const appendRoundRecordIfNeeded = async (status, reason = '') => {
|
||||
if (roundRecordAppended) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof appendAccountRunRecord !== 'function') {
|
||||
return;
|
||||
}
|
||||
|
||||
const record = await appendAccountRunRecord(status, null, reason);
|
||||
if (record) {
|
||||
roundRecordAppended = true;
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
deps.throwIfStopped();
|
||||
await broadcastAutoRunStatus('running', {
|
||||
@@ -403,6 +420,7 @@
|
||||
} catch (err) {
|
||||
if (isStopError(err)) {
|
||||
stoppedEarly = true;
|
||||
await appendRoundRecordIfNeeded('stopped', getErrorMessage(err));
|
||||
await addLog(`第 ${targetRun}/${totalRuns} 轮已被用户停止`, 'warn');
|
||||
await broadcastAutoRunStatus('stopped', {
|
||||
currentRun: targetRun,
|
||||
@@ -444,6 +462,7 @@
|
||||
} catch (sleepError) {
|
||||
if (isStopError(sleepError)) {
|
||||
stoppedEarly = true;
|
||||
await appendRoundRecordIfNeeded('stopped', getErrorMessage(sleepError));
|
||||
await addLog(`第 ${targetRun}/${totalRuns} 轮已被用户停止`, 'warn');
|
||||
await broadcastAutoRunStatus('stopped', {
|
||||
currentRun: targetRun,
|
||||
@@ -466,6 +485,7 @@
|
||||
} catch (sleepError) {
|
||||
if (isStopError(sleepError)) {
|
||||
stoppedEarly = true;
|
||||
await appendRoundRecordIfNeeded('stopped', getErrorMessage(sleepError));
|
||||
await addLog(`第 ${targetRun}/${totalRuns} 轮已被用户停止`, 'warn');
|
||||
await broadcastAutoRunStatus('stopped', {
|
||||
currentRun: targetRun,
|
||||
@@ -486,6 +506,7 @@
|
||||
await setState({
|
||||
autoRunRoundSummaries: serializeAutoRunRoundSummaries(totalRuns, roundSummaries),
|
||||
});
|
||||
await appendRoundRecordIfNeeded('failed', reason);
|
||||
if (!autoRunSkipFailures) {
|
||||
cancelPendingCommands('当前轮执行失败。');
|
||||
await broadcastStopToContentScripts();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
function createMessageRouter(deps = {}) {
|
||||
const {
|
||||
addLog,
|
||||
appendAccountRunRecord,
|
||||
batchUpdateLuckmailPurchases,
|
||||
buildLocalhostCleanupPrefix,
|
||||
buildLuckmailSessionSettingsPayload,
|
||||
@@ -78,6 +79,19 @@
|
||||
verifyHotmailAccount,
|
||||
} = deps;
|
||||
|
||||
async function appendManualAccountRunRecordIfNeeded(status, stateOverride = null, reason = '') {
|
||||
if (typeof appendAccountRunRecord !== 'function') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const state = stateOverride || await getState();
|
||||
if (isAutoRunLockedState(state)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return appendAccountRunRecord(status, state, reason);
|
||||
}
|
||||
|
||||
async function handleStepData(step, payload) {
|
||||
switch (step) {
|
||||
case 1: {
|
||||
@@ -187,12 +201,17 @@
|
||||
case 'STEP_COMPLETE': {
|
||||
if (getStopRequested()) {
|
||||
await setStepStatus(message.step, 'stopped');
|
||||
await appendManualAccountRunRecordIfNeeded(`step${message.step}_stopped`, null, '流程已被用户停止。');
|
||||
notifyStepError(message.step, '流程已被用户停止。');
|
||||
return { ok: true };
|
||||
}
|
||||
const completionState = message.step === 9 ? await getState() : null;
|
||||
await setStepStatus(message.step, 'completed');
|
||||
await addLog(`步骤 ${message.step} 已完成`, 'ok');
|
||||
await handleStepData(message.step, message.payload);
|
||||
if (message.step === 9 && typeof appendAccountRunRecord === 'function') {
|
||||
await appendAccountRunRecord('success', completionState);
|
||||
}
|
||||
notifyStepComplete(message.step, message.payload);
|
||||
return { ok: true };
|
||||
}
|
||||
@@ -201,10 +220,12 @@
|
||||
if (isStopError(message.error)) {
|
||||
await setStepStatus(message.step, 'stopped');
|
||||
await addLog(`步骤 ${message.step} 已被用户停止`, 'warn');
|
||||
await appendManualAccountRunRecordIfNeeded(`step${message.step}_stopped`, null, message.error);
|
||||
notifyStepError(message.step, message.error);
|
||||
} else {
|
||||
await setStepStatus(message.step, 'failed');
|
||||
await addLog(`步骤 ${message.step} 失败:${message.error}`, 'error');
|
||||
await appendManualAccountRunRecordIfNeeded(`step${message.step}_failed`, null, message.error);
|
||||
notifyStepError(message.step, message.error);
|
||||
}
|
||||
return { ok: true };
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
confirmCustomVerificationStepBypass,
|
||||
ensureStep7VerificationPageReady,
|
||||
executeStep6,
|
||||
getPanelMode,
|
||||
getMailConfig,
|
||||
getState,
|
||||
getTabId,
|
||||
@@ -77,18 +78,45 @@
|
||||
}
|
||||
}
|
||||
|
||||
const shouldRefreshOAuthBeforeSubmit = getPanelMode(state) === 'cpa';
|
||||
let step6ReplayCompleted = false;
|
||||
|
||||
await resolveVerificationStep(7, state, mail, {
|
||||
filterAfterTimestamp: mail.provider === HOTMAIL_PROVIDER ? undefined : Math.max(0, stepStartedAt - 60000),
|
||||
requestFreshCodeFirst: false,
|
||||
resendIntervalMs: mail.provider === HOTMAIL_PROVIDER ? 0 : STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS,
|
||||
resendIntervalMs: (mail.provider === HOTMAIL_PROVIDER || mail.provider === '2925')
|
||||
? 0
|
||||
: STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS,
|
||||
beforeSubmit: shouldRefreshOAuthBeforeSubmit ? async (result) => {
|
||||
if (step6ReplayCompleted) {
|
||||
return;
|
||||
}
|
||||
|
||||
step6ReplayCompleted = true;
|
||||
await addLog(`步骤 7:已拿到登录验证码 ${result.code},先刷新 CPA OAuth 链接并重走步骤 6,再回填验证码。`, 'warn');
|
||||
await rerunStep6ForStep7Recovery({
|
||||
logMessage: '步骤 7:正在重新获取最新 CPA OAuth 链接,并快速重走步骤 6...',
|
||||
skipPreLoginCleanup: true,
|
||||
postStepDelayMs: 1200,
|
||||
});
|
||||
await ensureStep7VerificationPageReady();
|
||||
await addLog('步骤 7:登录验证码页面已重新就绪,开始回填刚才获取到的验证码。', 'info');
|
||||
} : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
async function rerunStep6ForStep7Recovery() {
|
||||
async function rerunStep6ForStep7Recovery(options = {}) {
|
||||
const {
|
||||
logMessage = '步骤 7:正在回到步骤 6,重新发起登录验证码流程...',
|
||||
skipPreLoginCleanup = false,
|
||||
postStepDelayMs = 3000,
|
||||
} = options;
|
||||
const currentState = await getState();
|
||||
await addLog('步骤 7:正在回到步骤 6,重新发起登录验证码流程...', 'warn');
|
||||
await executeStep6(currentState);
|
||||
await sleepWithStop(3000);
|
||||
await addLog(logMessage, 'warn');
|
||||
await executeStep6(currentState, { skipPreLoginCleanup });
|
||||
if (postStepDelayMs > 0) {
|
||||
await sleepWithStop(postStepDelayMs);
|
||||
}
|
||||
}
|
||||
|
||||
async function executeStep7(state) {
|
||||
|
||||
@@ -89,7 +89,9 @@
|
||||
await resolveVerificationStep(4, state, mail, {
|
||||
filterAfterTimestamp: mail.provider === HOTMAIL_PROVIDER ? undefined : stepStartedAt,
|
||||
requestFreshCodeFirst: mail.provider === HOTMAIL_PROVIDER ? false : true,
|
||||
resendIntervalMs: mail.provider === HOTMAIL_PROVIDER ? 0 : STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS,
|
||||
resendIntervalMs: (mail.provider === HOTMAIL_PROVIDER || mail.provider === '2925')
|
||||
? 0
|
||||
: STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
const {
|
||||
addLog,
|
||||
completeStepFromBackground,
|
||||
getErrorMessage,
|
||||
getLoginAuthStateLabel,
|
||||
getState,
|
||||
isStep6RecoverableResult,
|
||||
@@ -15,10 +16,12 @@
|
||||
sendToContentScriptResilient,
|
||||
shouldSkipLoginVerificationForCpaCallback,
|
||||
skipLoginVerificationStepsForCpaCallback,
|
||||
STEP6_MAX_ATTEMPTS,
|
||||
throwIfStopped,
|
||||
} = deps;
|
||||
|
||||
async function executeStep6(state) {
|
||||
async function executeStep6(state, options = {}) {
|
||||
const { skipPreLoginCleanup = false } = options;
|
||||
if (shouldSkipLoginVerificationForCpaCallback(state)) {
|
||||
await skipLoginVerificationStepsForCpaCallback();
|
||||
return;
|
||||
@@ -27,63 +30,77 @@
|
||||
throw new Error('缺少邮箱地址,请先完成步骤 3。');
|
||||
}
|
||||
|
||||
await runPreStep6CookieCleanup();
|
||||
if (!skipPreLoginCleanup) {
|
||||
await runPreStep6CookieCleanup();
|
||||
}
|
||||
|
||||
let attempt = 0;
|
||||
let lastError = null;
|
||||
|
||||
while (true) {
|
||||
while (attempt < STEP6_MAX_ATTEMPTS) {
|
||||
throwIfStopped();
|
||||
attempt += 1;
|
||||
const currentState = attempt === 1 ? state : await getState();
|
||||
const password = currentState.password || currentState.customPassword || '';
|
||||
const oauthUrl = await refreshOAuthUrlBeforeStep6(currentState);
|
||||
try {
|
||||
const currentState = attempt === 1 ? state : await getState();
|
||||
const password = currentState.password || currentState.customPassword || '';
|
||||
const oauthUrl = await refreshOAuthUrlBeforeStep6(currentState);
|
||||
|
||||
if (attempt === 1) {
|
||||
await addLog('步骤 6:正在打开最新 OAuth 链接并登录...');
|
||||
} else {
|
||||
await addLog(`步骤 6:上一轮登录未进入验证码页,正在重新发起第 ${attempt} 轮登录尝试...`, 'warn');
|
||||
}
|
||||
|
||||
await reuseOrCreateTab('signup-page', oauthUrl);
|
||||
|
||||
const result = await sendToContentScriptResilient(
|
||||
'signup-page',
|
||||
{
|
||||
type: 'EXECUTE_STEP',
|
||||
step: 6,
|
||||
source: 'background',
|
||||
payload: {
|
||||
email: currentState.email,
|
||||
password,
|
||||
},
|
||||
},
|
||||
{
|
||||
timeoutMs: 180000,
|
||||
retryDelayMs: 700,
|
||||
logMessage: '步骤 6:认证页正在切换,等待页面重新就绪后继续登录...',
|
||||
if (attempt === 1) {
|
||||
await addLog('步骤 6:正在打开最新 OAuth 链接并登录...');
|
||||
} else {
|
||||
await addLog(`步骤 6:上一轮失败后,正在进行第 ${attempt} 次尝试(最多 ${STEP6_MAX_ATTEMPTS} 次)...`, 'warn');
|
||||
}
|
||||
);
|
||||
|
||||
if (result?.error) {
|
||||
throw new Error(result.error);
|
||||
await reuseOrCreateTab('signup-page', oauthUrl);
|
||||
|
||||
const result = await sendToContentScriptResilient(
|
||||
'signup-page',
|
||||
{
|
||||
type: 'EXECUTE_STEP',
|
||||
step: 6,
|
||||
source: 'background',
|
||||
payload: {
|
||||
email: currentState.email,
|
||||
password,
|
||||
},
|
||||
},
|
||||
{
|
||||
timeoutMs: 180000,
|
||||
retryDelayMs: 700,
|
||||
logMessage: '步骤 6:认证页正在切换,等待页面重新就绪后继续登录...',
|
||||
}
|
||||
);
|
||||
|
||||
if (result?.error) {
|
||||
throw new Error(result.error);
|
||||
}
|
||||
|
||||
if (isStep6SuccessResult(result)) {
|
||||
await completeStepFromBackground(6, {
|
||||
loginVerificationRequestedAt: result.loginVerificationRequestedAt || null,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (isStep6RecoverableResult(result)) {
|
||||
const reasonMessage = result.message
|
||||
|| `当前停留在${getLoginAuthStateLabel(result.state)},准备重新执行步骤 6。`;
|
||||
throw new Error(reasonMessage);
|
||||
}
|
||||
|
||||
throw new Error('步骤 6:认证页未返回可识别的登录结果。');
|
||||
} catch (err) {
|
||||
throwIfStopped(err);
|
||||
lastError = err;
|
||||
if (attempt >= STEP6_MAX_ATTEMPTS) {
|
||||
break;
|
||||
}
|
||||
|
||||
await addLog(`步骤 6:第 ${attempt} 次尝试失败,原因:${getErrorMessage(err)};准备重试...`, 'warn');
|
||||
}
|
||||
|
||||
if (isStep6SuccessResult(result)) {
|
||||
await completeStepFromBackground(6, {
|
||||
loginVerificationRequestedAt: result.loginVerificationRequestedAt || null,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (isStep6RecoverableResult(result)) {
|
||||
const reasonMessage = result.message
|
||||
|| `当前停留在${getLoginAuthStateLabel(result.state)},准备重新执行步骤 6。`;
|
||||
await addLog(`步骤 6:${reasonMessage}`, 'warn');
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new Error('步骤 6:认证页未返回可识别的登录结果。');
|
||||
}
|
||||
|
||||
throw new Error(`步骤 6:判断失败后已重试 ${STEP6_MAX_ATTEMPTS - 1} 次,仍未成功。最后原因:${getErrorMessage(lastError)}`);
|
||||
}
|
||||
|
||||
return { executeStep6 };
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
HOTMAIL_PROVIDER,
|
||||
isStopError,
|
||||
LUCKMAIL_PROVIDER,
|
||||
MAIL_2925_VERIFICATION_INTERVAL_MS,
|
||||
MAIL_2925_VERIFICATION_MAX_ATTEMPTS,
|
||||
pollCloudflareTempEmailVerificationCode,
|
||||
pollHotmailVerificationCode,
|
||||
pollLuckmailVerificationCode,
|
||||
@@ -62,14 +64,15 @@
|
||||
}
|
||||
|
||||
function getVerificationPollPayload(step, state, overrides = {}) {
|
||||
const is2925Provider = state?.mailProvider === '2925';
|
||||
if (step === 4) {
|
||||
return {
|
||||
filterAfterTimestamp: getHotmailVerificationRequestTimestamp(4, state),
|
||||
senderFilters: ['openai', 'noreply', 'verify', 'auth', 'duckduckgo', 'forward'],
|
||||
subjectFilters: ['verify', 'verification', 'code', '验证码', 'confirm'],
|
||||
targetEmail: state.email,
|
||||
maxAttempts: 5,
|
||||
intervalMs: 3000,
|
||||
maxAttempts: is2925Provider ? MAIL_2925_VERIFICATION_MAX_ATTEMPTS : 5,
|
||||
intervalMs: is2925Provider ? MAIL_2925_VERIFICATION_INTERVAL_MS : 3000,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
@@ -79,8 +82,8 @@
|
||||
senderFilters: ['openai', 'noreply', 'verify', 'auth', 'chatgpt', 'duckduckgo', 'forward'],
|
||||
subjectFilters: ['verify', 'verification', 'code', '验证码', 'confirm', 'login'],
|
||||
targetEmail: state.email,
|
||||
maxAttempts: 5,
|
||||
intervalMs: 3000,
|
||||
maxAttempts: is2925Provider ? MAIL_2925_VERIFICATION_MAX_ATTEMPTS : 5,
|
||||
intervalMs: is2925Provider ? MAIL_2925_VERIFICATION_INTERVAL_MS : 3000,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
@@ -368,6 +371,9 @@
|
||||
const hotmailPollConfig = mail.provider === HOTMAIL_PROVIDER
|
||||
? getHotmailVerificationPollConfig(step)
|
||||
: null;
|
||||
const beforeSubmit = typeof options.beforeSubmit === 'function'
|
||||
? options.beforeSubmit
|
||||
: null;
|
||||
const ignorePersistedLastCode = Boolean(hotmailPollConfig?.ignorePersistedLastCode);
|
||||
if (state[stateKey] && !ignorePersistedLastCode) {
|
||||
rejectedCodes.add(state[stateKey]);
|
||||
@@ -431,6 +437,14 @@
|
||||
|
||||
throwIfStopped();
|
||||
await addLog(`步骤 ${step}:已获取${getVerificationCodeLabel(step)}验证码:${result.code}`);
|
||||
if (beforeSubmit) {
|
||||
await beforeSubmit(result, {
|
||||
attempt,
|
||||
rejectedCodes: new Set(rejectedCodes),
|
||||
filterAfterTimestamp: nextFilterAfterTimestamp ?? undefined,
|
||||
lastResendAt,
|
||||
});
|
||||
}
|
||||
throwIfStopped();
|
||||
const submitResult = await submitVerificationCode(step, result.code);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user