feat: 增加步骤 7 登录超时处理逻辑,支持从步骤 6 重新发起验证码流程
This commit is contained in:
+62
-2
@@ -1009,6 +1009,35 @@ function isVerificationMailPollingError(error) {
|
|||||||
return /未在 .*邮箱中找到新的匹配邮件|邮箱轮询结束,但未获取到验证码|无法获取新的(?:注册|登录)验证码|页面未能重新就绪|页面通信异常|did not respond in \d+s/i.test(message);
|
return /未在 .*邮箱中找到新的匹配邮件|邮箱轮询结束,但未获取到验证码|无法获取新的(?:注册|登录)验证码|页面未能重新就绪|页面通信异常|did not respond in \d+s/i.test(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const STEP7_RESTART_FROM_STEP6_ERROR_CODE = 'STEP7_RESTART_FROM_STEP6';
|
||||||
|
|
||||||
|
function createStep7RestartFromStep6Error(details = {}) {
|
||||||
|
const { reason = 'unknown', url = '' } = details || {};
|
||||||
|
const reasonLabel = reason === 'login_timeout_error_page'
|
||||||
|
? '检测到登录页超时报错'
|
||||||
|
: '步骤 7 请求回到步骤 6';
|
||||||
|
const error = new Error(`步骤 7:${reasonLabel}。${url ? `URL: ${url}` : ''}`.trim());
|
||||||
|
error.code = STEP7_RESTART_FROM_STEP6_ERROR_CODE;
|
||||||
|
error.restartReason = reason;
|
||||||
|
error.restartUrl = url;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStep7RestartFromStep6Error(result) {
|
||||||
|
if (!result?.restartFromStep6) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return createStep7RestartFromStep6Error(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isStep7RestartFromStep6Error(error) {
|
||||||
|
return error?.code === STEP7_RESTART_FROM_STEP6_ERROR_CODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isStep7RecoverableError(error) {
|
||||||
|
return isVerificationMailPollingError(error) || isStep7RestartFromStep6Error(error);
|
||||||
|
}
|
||||||
|
|
||||||
function isRestartCurrentAttemptError(error) {
|
function isRestartCurrentAttemptError(error) {
|
||||||
const message = String(typeof error === 'string' ? error : error?.message || '');
|
const message = String(typeof error === 'string' ? error : error?.message || '');
|
||||||
return /当前邮箱已存在,需要重新开始新一轮/.test(message);
|
return /当前邮箱已存在,需要重新开始新一轮/.test(message);
|
||||||
@@ -2539,6 +2568,13 @@ async function requestVerificationCodeResend(step) {
|
|||||||
throw new Error(result.error);
|
throw new Error(result.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (step === 7) {
|
||||||
|
const restartError = getStep7RestartFromStep6Error(result);
|
||||||
|
if (restartError) {
|
||||||
|
throw restartError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Date.now();
|
return Date.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2625,6 +2661,13 @@ async function submitVerificationCode(step, code) {
|
|||||||
throw new Error(result.error);
|
throw new Error(result.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (step === 7) {
|
||||||
|
const restartError = getStep7RestartFromStep6Error(result);
|
||||||
|
if (restartError) {
|
||||||
|
throw restartError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result || {};
|
return result || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2644,6 +2687,9 @@ async function resolveVerificationStep(step, state, mail, options = {}) {
|
|||||||
await requestVerificationCodeResend(step);
|
await requestVerificationCodeResend(step);
|
||||||
await addLog(`步骤 ${step}:已先请求一封新的${getVerificationCodeLabel(step)}验证码,再开始轮询邮箱。`, 'warn');
|
await addLog(`步骤 ${step}:已先请求一封新的${getVerificationCodeLabel(step)}验证码,再开始轮询邮箱。`, 'warn');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
if (step === 7 && isStep7RestartFromStep6Error(err)) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
await addLog(`步骤 ${step}:首次重新获取验证码失败:${err.message},将继续使用当前时间窗口轮询。`, 'warn');
|
await addLog(`步骤 ${step}:首次重新获取验证码失败:${err.message},将继续使用当前时间窗口轮询。`, 'warn');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2840,6 +2886,11 @@ async function runStep7Attempt(state) {
|
|||||||
throw new Error(prepareResult.error);
|
throw new Error(prepareResult.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const restartError = getStep7RestartFromStep6Error(prepareResult);
|
||||||
|
if (restartError) {
|
||||||
|
throw restartError;
|
||||||
|
}
|
||||||
|
|
||||||
await addLog(`步骤 7:正在打开${mail.label}...`);
|
await addLog(`步骤 7:正在打开${mail.label}...`);
|
||||||
|
|
||||||
const alive = await isTabAlive(mail.source);
|
const alive = await isTabAlive(mail.source);
|
||||||
@@ -2890,7 +2941,7 @@ async function executeStep7(state) {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
lastError = err;
|
lastError = err;
|
||||||
|
|
||||||
if (!isVerificationMailPollingError(err)) {
|
if (!isStep7RecoverableError(err)) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2898,11 +2949,20 @@ async function executeStep7(state) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
await addLog(`步骤 7:检测到邮箱轮询类失败,准备从步骤 6 重新开始(${round + 1}/${STEP7_RESTART_MAX_ROUNDS})...`, 'warn');
|
await addLog(
|
||||||
|
isStep7RestartFromStep6Error(err)
|
||||||
|
? `步骤 7:检测到登录页超时报错,准备从步骤 6 重新开始(${round + 1}/${STEP7_RESTART_MAX_ROUNDS})...`
|
||||||
|
: `步骤 7:检测到邮箱轮询类失败,准备从步骤 6 重新开始(${round + 1}/${STEP7_RESTART_MAX_ROUNDS})...`,
|
||||||
|
'warn'
|
||||||
|
);
|
||||||
await rerunStep6ForStep7Recovery();
|
await rerunStep6ForStep7Recovery();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lastError && isStep7RecoverableError(lastError)) {
|
||||||
|
throw new Error(`步骤 7:登录验证码流程在 ${STEP7_RESTART_MAX_ROUNDS} 轮恢复后仍未成功。最后一次原因:${lastError.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
throw lastError || new Error(`步骤 7:登录验证码流程在 ${STEP7_RESTART_MAX_ROUNDS} 轮后仍未成功。`);
|
throw lastError || new Error(`步骤 7:登录验证码流程在 ${STEP7_RESTART_MAX_ROUNDS} 轮后仍未成功。`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+52
-14
@@ -188,6 +188,12 @@ async function prepareLoginCodeFlow(timeout = 15000) {
|
|||||||
return { ready: true, mode: 'verification_page' };
|
return { ready: true, mode: 'verification_page' };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const initialRestartSignal = getStep7RestartFromStep6Signal();
|
||||||
|
if (initialRestartSignal) {
|
||||||
|
log('步骤 7:检测到登录页超时报错,准备回到步骤 6 重新发起登录验证码流程...', 'warn');
|
||||||
|
return initialRestartSignal;
|
||||||
|
}
|
||||||
|
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
let switchClickCount = 0;
|
let switchClickCount = 0;
|
||||||
let lastSwitchAttemptAt = 0;
|
let lastSwitchAttemptAt = 0;
|
||||||
@@ -212,6 +218,12 @@ async function prepareLoginCodeFlow(timeout = 15000) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const restartSignal = getStep7RestartFromStep6Signal();
|
||||||
|
if (restartSignal) {
|
||||||
|
log('步骤 7:检测到登录页超时报错,准备回到步骤 6 重新发起登录验证码流程...', 'warn');
|
||||||
|
return restartSignal;
|
||||||
|
}
|
||||||
|
|
||||||
const passwordInput = document.querySelector('input[type="password"]');
|
const passwordInput = document.querySelector('input[type="password"]');
|
||||||
const switchTrigger = findOneTimeCodeLoginTrigger();
|
const switchTrigger = findOneTimeCodeLoginTrigger();
|
||||||
|
|
||||||
@@ -239,7 +251,10 @@ async function prepareLoginCodeFlow(timeout = 15000) {
|
|||||||
|
|
||||||
async function resendVerificationCode(step, timeout = 45000) {
|
async function resendVerificationCode(step, timeout = 45000) {
|
||||||
if (step === 7) {
|
if (step === 7) {
|
||||||
await prepareLoginCodeFlow();
|
const prepareResult = await prepareLoginCodeFlow();
|
||||||
|
if (prepareResult?.restartFromStep6) {
|
||||||
|
return prepareResult;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
@@ -382,8 +397,8 @@ const VERIFICATION_PAGE_PATTERN = /检查您的收件箱|输入我们刚刚向|
|
|||||||
const OAUTH_CONSENT_PAGE_PATTERN = /使用\s*ChatGPT\s*登录到\s*Codex|login\s+to\s+codex|log\s+in\s+to\s+codex|authorize|授权/i;
|
const OAUTH_CONSENT_PAGE_PATTERN = /使用\s*ChatGPT\s*登录到\s*Codex|login\s+to\s+codex|log\s+in\s+to\s+codex|authorize|授权/i;
|
||||||
const ADD_PHONE_PAGE_PATTERN = /add[\s-]*phone|添加手机号|手机号码|手机号|phone\s+number|telephone/i;
|
const ADD_PHONE_PAGE_PATTERN = /add[\s-]*phone|添加手机号|手机号码|手机号|phone\s+number|telephone/i;
|
||||||
const STEP5_SUBMIT_ERROR_PATTERN = /无法根据该信息创建帐户|请重试|unable\s+to\s+create\s+(?:your\s+)?account|couldn'?t\s+create\s+(?:your\s+)?account|something\s+went\s+wrong|invalid\s+(?:birthday|birth|date)|生日|出生日期/i;
|
const STEP5_SUBMIT_ERROR_PATTERN = /无法根据该信息创建帐户|请重试|unable\s+to\s+create\s+(?:your\s+)?account|couldn'?t\s+create\s+(?:your\s+)?account|something\s+went\s+wrong|invalid\s+(?:birthday|birth|date)|生日|出生日期/i;
|
||||||
const SIGNUP_PASSWORD_ERROR_TITLE_PATTERN = /糟糕,出错了|something\s+went\s+wrong|oops/i;
|
const AUTH_TIMEOUT_ERROR_TITLE_PATTERN = /糟糕,出错了|something\s+went\s+wrong|oops/i;
|
||||||
const SIGNUP_PASSWORD_ERROR_DETAIL_PATTERN = /operation\s+timed\s+out|timed\s+out|请求超时|操作超时/i;
|
const AUTH_TIMEOUT_ERROR_DETAIL_PATTERN = /operation\s+timed\s+out|timed\s+out|请求超时|操作超时/i;
|
||||||
const SIGNUP_EMAIL_EXISTS_PATTERN = /与此电子邮件地址相关联的帐户已存在|account\s+associated\s+with\s+this\s+email\s+address\s+already\s+exists|email\s+address.*already\s+exists/i;
|
const SIGNUP_EMAIL_EXISTS_PATTERN = /与此电子邮件地址相关联的帐户已存在|account\s+associated\s+with\s+this\s+email\s+address\s+already\s+exists|email\s+address.*already\s+exists/i;
|
||||||
|
|
||||||
function getVerificationErrorText() {
|
function getVerificationErrorText() {
|
||||||
@@ -466,6 +481,10 @@ function isAddPhonePageReady() {
|
|||||||
return ADD_PHONE_PAGE_PATTERN.test(getPageTextSnapshot());
|
return ADD_PHONE_PAGE_PATTERN.test(getPageTextSnapshot());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isLoginPage() {
|
||||||
|
return /\/log-in(?:[/?#]|$)/i.test(location.pathname || '');
|
||||||
|
}
|
||||||
|
|
||||||
function isStep8Ready() {
|
function isStep8Ready() {
|
||||||
const continueBtn = getPrimaryContinueButton();
|
const continueBtn = getPrimaryContinueButton();
|
||||||
if (!continueBtn) return false;
|
if (!continueBtn) return false;
|
||||||
@@ -610,31 +629,47 @@ function getSignupPasswordSubmitButton({ allowDisabled = false } = {}) {
|
|||||||
}) || null;
|
}) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSignupRetryButton() {
|
function getAuthRetryButton({ allowDisabled = false } = {}) {
|
||||||
const direct = document.querySelector('button[data-dd-action-name="Try again"]');
|
const direct = document.querySelector('button[data-dd-action-name="Try again"]');
|
||||||
if (direct && isVisibleElement(direct) && isActionEnabled(direct)) {
|
if (direct && isVisibleElement(direct) && (allowDisabled || isActionEnabled(direct))) {
|
||||||
return direct;
|
return direct;
|
||||||
}
|
}
|
||||||
|
|
||||||
const candidates = document.querySelectorAll('button, [role="button"]');
|
const candidates = document.querySelectorAll('button, [role="button"]');
|
||||||
return Array.from(candidates).find((el) => {
|
return Array.from(candidates).find((el) => {
|
||||||
if (!isVisibleElement(el) || !isActionEnabled(el)) return false;
|
if (!isVisibleElement(el) || (!allowDisabled && !isActionEnabled(el))) return false;
|
||||||
const text = getActionText(el);
|
const text = getActionText(el);
|
||||||
return /重试|try\s+again/i.test(text);
|
return /重试|try\s+again/i.test(text);
|
||||||
}) || null;
|
}) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isSignupPasswordErrorPage() {
|
function matchesAuthTimeoutErrorPage(pathPattern) {
|
||||||
if (!isSignupPasswordPage()) return false;
|
if (!pathPattern.test(location.pathname || '')) return false;
|
||||||
const text = getPageTextSnapshot();
|
const text = getPageTextSnapshot();
|
||||||
return Boolean(
|
return Boolean(
|
||||||
getSignupRetryButton()
|
getAuthRetryButton({ allowDisabled: true })
|
||||||
&& (SIGNUP_PASSWORD_ERROR_TITLE_PATTERN.test(text)
|
&& (AUTH_TIMEOUT_ERROR_TITLE_PATTERN.test(text)
|
||||||
|| SIGNUP_PASSWORD_ERROR_DETAIL_PATTERN.test(text)
|
|| AUTH_TIMEOUT_ERROR_DETAIL_PATTERN.test(text)
|
||||||
|| SIGNUP_PASSWORD_ERROR_TITLE_PATTERN.test(document.title || ''))
|
|| AUTH_TIMEOUT_ERROR_TITLE_PATTERN.test(document.title || ''))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isSignupPasswordErrorPage() {
|
||||||
|
return matchesAuthTimeoutErrorPage(/\/create-account\/password(?:[/?#]|$)/i);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStep7RestartFromStep6Signal() {
|
||||||
|
if (!isLoginPage() || !matchesAuthTimeoutErrorPage(/\/log-in(?:[/?#]|$)/i)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
restartFromStep6: true,
|
||||||
|
reason: 'login_timeout_error_page',
|
||||||
|
url: location.href,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function isSignupEmailAlreadyExistsPage() {
|
function isSignupEmailAlreadyExistsPage() {
|
||||||
return isSignupPasswordPage() && SIGNUP_EMAIL_EXISTS_PATTERN.test(getPageTextSnapshot());
|
return isSignupPasswordPage() && SIGNUP_EMAIL_EXISTS_PATTERN.test(getPageTextSnapshot());
|
||||||
}
|
}
|
||||||
@@ -651,7 +686,7 @@ function inspectSignupVerificationState() {
|
|||||||
if (isSignupPasswordErrorPage()) {
|
if (isSignupPasswordErrorPage()) {
|
||||||
return {
|
return {
|
||||||
state: 'error',
|
state: 'error',
|
||||||
retryButton: getSignupRetryButton(),
|
retryButton: getAuthRetryButton({ allowDisabled: true }),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -804,7 +839,10 @@ async function fillVerificationCode(step, payload) {
|
|||||||
log(`步骤 ${step}:正在填写验证码:${code}`);
|
log(`步骤 ${step}:正在填写验证码:${code}`);
|
||||||
|
|
||||||
if (step === 7) {
|
if (step === 7) {
|
||||||
await prepareLoginCodeFlow();
|
const prepareResult = await prepareLoginCodeFlow();
|
||||||
|
if (prepareResult?.restartFromStep6) {
|
||||||
|
return prepareResult;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find code input — could be a single input or multiple separate inputs
|
// Find code input — could be a single input or multiple separate inputs
|
||||||
|
|||||||
Reference in New Issue
Block a user