fix: handle signup phone entry mode before email submission

- 同步最新 dev 到 PR #113,并保留 dev 上更完整的 163 邮箱实现\n- 补充 Step 2 的手机号入口切邮箱逻辑与本地化邮箱输入识别\n- 避免把 Step 2 的 phone entry 提示误判成 auth add-phone 致命错误
This commit is contained in:
QLHazyCoder
2026-04-25 14:53:13 +08:00
105 changed files with 18673 additions and 1530 deletions
+2 -8
View File
@@ -41,11 +41,11 @@
await addLog('步骤 9:正在监听 localhost 回调地址...');
const callbackTimeoutMs = typeof getOAuthFlowStepTimeoutMs === 'function'
? await getOAuthFlowStepTimeoutMs(120000, {
? await getOAuthFlowStepTimeoutMs(240000, {
step: 9,
actionLabel: 'OAuth localhost 回调',
})
: 120000;
: 240000;
return new Promise((resolve, reject) => {
let resolved = false;
@@ -200,12 +200,6 @@
break;
}
if (effect.restartCurrentStep) {
await addLog(`步骤 9${getStep8EffectLabel(effect)},准备重新定位“继续”按钮并重试...`, 'warn');
await sleepWithStop(STEP8_CLICK_RETRY_DELAY_MS);
continue;
}
if (round >= STEP8_MAX_ROUNDS) {
throw new Error(`步骤 9:连续 ${STEP8_MAX_ROUNDS} 轮点击“继续”后页面仍无反应。`);
}
+65 -17
View File
@@ -1,12 +1,16 @@
(function attachBackgroundStep8(root, factory) {
root.MultiPageBackgroundStep8 = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep8Module() {
const MAIL_2925_FILTER_LOOKBACK_MS = 10 * 60 * 1000;
function createStep8Executor(deps = {}) {
const {
addLog,
chrome,
CLOUDFLARE_TEMP_EMAIL_PROVIDER,
confirmCustomVerificationStepBypass,
ensureMail2925MailboxSession,
ensureIcloudMailSession,
ensureStep8VerificationPageReady,
getOAuthFlowRemainingMs,
getOAuthFlowStepTimeoutMs,
@@ -55,11 +59,50 @@
return String(value || '').trim().toLowerCase();
}
function getExpectedMail2925MailboxEmail(state = {}) {
if (Boolean(state?.mail2925UseAccountPool)) {
const currentAccountId = String(state?.currentMail2925AccountId || '').trim();
const accounts = Array.isArray(state?.mail2925Accounts) ? state.mail2925Accounts : [];
const currentAccount = accounts.find((account) => String(account?.id || '') === currentAccountId) || null;
const accountEmail = String(currentAccount?.email || '').trim().toLowerCase();
if (accountEmail) {
return accountEmail;
}
}
return String(state?.mail2925BaseEmail || '').trim().toLowerCase();
}
async function focusOrOpenMailTab(mail) {
const alive = await isTabAlive(mail.source);
if (alive) {
if (mail.navigateOnReuse) {
await reuseOrCreateTab(mail.source, mail.url, {
inject: mail.inject,
injectSource: mail.injectSource,
});
return;
}
const tabId = await getTabId(mail.source);
await chrome.tabs.update(tabId, { active: true });
return;
}
await reuseOrCreateTab(mail.source, mail.url, {
inject: mail.inject,
injectSource: mail.injectSource,
});
}
async function runStep8Attempt(state) {
const mail = getMailConfig(state);
if (mail.error) throw new Error(mail.error);
const stepStartedAt = Date.now();
const verificationFilterAfterTimestamp = mail.provider === '2925'
? Math.max(0, stepStartedAt - MAIL_2925_FILTER_LOOKBACK_MS)
: stepStartedAt;
const verificationSessionKey = `8:${stepStartedAt}`;
const authTabId = await getTabId('signup-page');
@@ -98,6 +141,15 @@
return;
}
if (mail.source === 'icloud-mail' && typeof ensureIcloudMailSession === 'function') {
await addLog('步骤 8:正在确认 iCloud 邮箱登录态...', 'info');
await ensureIcloudMailSession({
state,
step: 8,
actionLabel: '步骤 8:确认 iCloud 邮箱登录态',
});
}
throwIfStopped();
if (
mail.provider === HOTMAIL_PROVIDER
@@ -107,23 +159,19 @@
await addLog(`步骤 8:正在通过 ${mail.label} 轮询验证码...`);
} else {
await addLog(`步骤 8:正在打开${mail.label}...`);
const alive = await isTabAlive(mail.source);
if (alive) {
if (mail.navigateOnReuse) {
await reuseOrCreateTab(mail.source, mail.url, {
inject: mail.inject,
injectSource: mail.injectSource,
});
} else {
const tabId = await getTabId(mail.source);
await chrome.tabs.update(tabId, { active: true });
}
} else {
await reuseOrCreateTab(mail.source, mail.url, {
inject: mail.inject,
injectSource: mail.injectSource,
if (mail.provider === '2925' && typeof ensureMail2925MailboxSession === 'function') {
await ensureMail2925MailboxSession({
accountId: state.currentMail2925AccountId || null,
forceRelogin: false,
allowLoginWhenOnLoginPage: Boolean(state?.mail2925UseAccountPool),
expectedMailboxEmail: getExpectedMail2925MailboxEmail(state),
actionLabel: 'Step 8: ensure 2925 mailbox session',
});
} else {
await focusOrOpenMailTab(mail);
}
if (mail.provider === '2925') {
await addLog(`步骤 8:将直接使用当前已登录的 ${mail.label} 轮询验证码。`, 'info');
}
}
@@ -131,7 +179,7 @@
...state,
step8VerificationTargetEmail: displayedVerificationEmail || '',
}, mail, {
filterAfterTimestamp: mail.provider === '2925' ? 0 : stepStartedAt,
filterAfterTimestamp: verificationFilterAfterTimestamp,
sessionKey: verificationSessionKey,
disableTimeBudgetCap: mail.provider === '2925',
getRemainingTimeMs: getStep8RemainingTimeResolver(state?.oauthUrl || ''),
+87 -26
View File
@@ -1,12 +1,16 @@
(function attachBackgroundStep4(root, factory) {
root.MultiPageBackgroundStep4 = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep4Module() {
const MAIL_2925_FILTER_LOOKBACK_MS = 10 * 60 * 1000;
function createStep4Executor(deps = {}) {
const {
addLog,
chrome,
completeStepFromBackground,
confirmCustomVerificationStepBypass,
ensureMail2925MailboxSession,
ensureIcloudMailSession,
getMailConfig,
getTabId,
HOTMAIL_PROVIDER,
@@ -21,19 +25,61 @@
throwIfStopped,
} = deps;
function getExpectedMail2925MailboxEmail(state = {}) {
if (Boolean(state?.mail2925UseAccountPool)) {
const currentAccountId = String(state?.currentMail2925AccountId || '').trim();
const accounts = Array.isArray(state?.mail2925Accounts) ? state.mail2925Accounts : [];
const currentAccount = accounts.find((account) => String(account?.id || '') === currentAccountId) || null;
const accountEmail = String(currentAccount?.email || '').trim().toLowerCase();
if (accountEmail) {
return accountEmail;
}
}
return String(state?.mail2925BaseEmail || '').trim().toLowerCase();
}
async function focusOrOpenMailTab(mail) {
const alive = await isTabAlive(mail.source);
if (alive) {
if (mail.navigateOnReuse) {
await reuseOrCreateTab(mail.source, mail.url, {
inject: mail.inject,
injectSource: mail.injectSource,
});
return;
}
const tabId = await getTabId(mail.source);
await chrome.tabs.update(tabId, { active: true });
return;
}
await reuseOrCreateTab(mail.source, mail.url, {
inject: mail.inject,
injectSource: mail.injectSource,
});
}
async function executeStep4(state) {
const mail = getMailConfig(state);
if (mail.error) throw new Error(mail.error);
const stepStartedAt = Date.now();
const verificationFilterAfterTimestamp = mail.provider === '2925'
? Math.max(0, stepStartedAt - MAIL_2925_FILTER_LOOKBACK_MS)
: stepStartedAt;
const verificationSessionKey = `4:${stepStartedAt}`;
const signupTabId = await getTabId('signup-page');
if (!signupTabId) {
throw new Error('认证页面标签页已关闭,无法继续步骤 4。');
throw new Error('认证页面标签页已关闭,无法继续步骤 4。请先执行步骤 1 或步骤 2,重新打开认证页后再试。');
}
await chrome.tabs.update(signupTabId, { active: true });
throwIfStopped();
await addLog('步骤 4:正在确认注册验证码页面是否就绪,必要时自动恢复密码页超时报错...');
const prepareResult = await sendToContentScriptResilient(
'signup-page',
{
@@ -66,36 +112,51 @@
return;
}
throwIfStopped();
if (mail.provider === HOTMAIL_PROVIDER || mail.provider === LUCKMAIL_PROVIDER || mail.provider === CLOUDFLARE_TEMP_EMAIL_PROVIDER) {
await addLog(`步骤 4:正在通过 ${mail.label} 轮询验证码...`);
} else {
await addLog(`步骤 4:正在打开${mail.label}...`);
const alive = await isTabAlive(mail.source);
if (alive) {
if (mail.navigateOnReuse) {
await reuseOrCreateTab(mail.source, mail.url, {
inject: mail.inject,
injectSource: mail.injectSource,
});
} else {
const tabId = await getTabId(mail.source);
await chrome.tabs.update(tabId, { active: true });
}
} else {
await reuseOrCreateTab(mail.source, mail.url, {
inject: mail.inject,
injectSource: mail.injectSource,
});
}
if (mail.source === 'icloud-mail' && typeof ensureIcloudMailSession === 'function') {
await addLog('步骤 4:正在确认 iCloud 邮箱登录态...', 'info');
await ensureIcloudMailSession({
state,
step: 4,
actionLabel: '步骤 4:确认 iCloud 邮箱登录态',
});
}
throwIfStopped();
if (
mail.provider === HOTMAIL_PROVIDER
|| mail.provider === LUCKMAIL_PROVIDER
|| mail.provider === CLOUDFLARE_TEMP_EMAIL_PROVIDER
) {
await addLog(`步骤 4:正在通过 ${mail.label} 轮询验证码...`);
} else if (mail.provider === '2925') {
await addLog(`步骤 4:正在打开${mail.label}...`);
if (typeof ensureMail2925MailboxSession === 'function') {
await ensureMail2925MailboxSession({
accountId: state.currentMail2925AccountId || null,
forceRelogin: false,
allowLoginWhenOnLoginPage: Boolean(state?.mail2925UseAccountPool),
expectedMailboxEmail: getExpectedMail2925MailboxEmail(state),
actionLabel: '步骤 4:确认 2925 邮箱登录态',
});
} else {
await focusOrOpenMailTab(mail);
}
await addLog(`步骤 4:将直接使用当前已登录的 ${mail.label} 轮询验证码。`, 'info');
} else {
await addLog(`步骤 4:正在打开${mail.label}...`);
await focusOrOpenMailTab(mail);
}
const shouldRequestFreshCodeFirst = ![
HOTMAIL_PROVIDER,
CLOUDFLARE_TEMP_EMAIL_PROVIDER,
].includes(mail.provider);
await resolveVerificationStep(4, state, mail, {
filterAfterTimestamp: mail.provider === '2925' ? 0 : stepStartedAt,
filterAfterTimestamp: verificationFilterAfterTimestamp,
sessionKey: verificationSessionKey,
disableTimeBudgetCap: mail.provider === '2925',
requestFreshCodeFirst: mail.provider === HOTMAIL_PROVIDER ? false : true,
requestFreshCodeFirst: shouldRequestFreshCodeFirst,
resendIntervalMs: (mail.provider === HOTMAIL_PROVIDER || mail.provider === '2925')
? 0
: STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS,
+21
View File
@@ -26,6 +26,20 @@
throwIfStopped,
} = deps;
function isManagementSecretConfigError(error) {
const message = String(typeof error === 'string' ? error : error?.message || '').trim();
if (!message) {
return false;
}
const mentionsSecret = /管理密钥|Admin Secret|X-Admin-Key|CPA Key/i.test(message);
if (!mentionsSecret) {
return false;
}
return /缺少|未配置|请输入|无效|错误|失败|401|认证失败|未授权|unauthorized|invalid/i.test(message);
}
async function executeStep7(state) {
if (!state.email) {
throw new Error('缺少邮箱地址,请先完成步骤 3。');
@@ -102,6 +116,13 @@
if (isAddPhoneAuthFailure(err)) {
throw err;
}
if (isManagementSecretConfigError(err)) {
await addLog(
`步骤 7:检测到来源后台管理密钥缺失或错误,不再重试,当前流程停止。原因:${getErrorMessage(err)}`,
'error'
);
throw err;
}
lastError = err;
if (attempt >= STEP6_MAX_ATTEMPTS) {
break;
+128
View File
@@ -1,14 +1,142 @@
(function attachBackgroundStep1(root, factory) {
root.MultiPageBackgroundStep1 = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep1Module() {
const STEP1_COOKIE_CLEAR_DOMAINS = [
'chatgpt.com',
'chat.openai.com',
'openai.com',
'auth.openai.com',
'auth0.openai.com',
'accounts.openai.com',
];
const STEP1_COOKIE_CLEAR_ORIGINS = [
'https://chatgpt.com',
'https://chat.openai.com',
'https://auth.openai.com',
'https://auth0.openai.com',
'https://accounts.openai.com',
'https://openai.com',
];
function normalizeCookieDomainForStep1(domain) {
return String(domain || '').trim().replace(/^\.+/, '').toLowerCase();
}
function shouldClearStep1Cookie(cookie) {
const domain = normalizeCookieDomainForStep1(cookie?.domain);
if (!domain) return false;
return STEP1_COOKIE_CLEAR_DOMAINS.some((target) => (
domain === target || domain.endsWith(`.${target}`)
));
}
function buildStep1CookieRemovalUrl(cookie) {
const host = normalizeCookieDomainForStep1(cookie?.domain);
const rawPath = String(cookie?.path || '/');
const path = rawPath.startsWith('/') ? rawPath : `/${rawPath}`;
return `https://${host}${path}`;
}
function getStep1ErrorMessage(error) {
return error?.message || String(error || '未知错误');
}
async function collectStep1Cookies(chromeApi) {
if (!chromeApi.cookies?.getAll) {
return [];
}
const stores = chromeApi.cookies.getAllCookieStores
? await chromeApi.cookies.getAllCookieStores()
: [{ id: undefined }];
const cookies = [];
const seen = new Set();
for (const store of stores) {
const storeId = store?.id;
const batch = await chromeApi.cookies.getAll(storeId ? { storeId } : {});
for (const cookie of batch || []) {
if (!shouldClearStep1Cookie(cookie)) continue;
const key = [
cookie.storeId || storeId || '',
cookie.domain || '',
cookie.path || '',
cookie.name || '',
cookie.partitionKey ? JSON.stringify(cookie.partitionKey) : '',
].join('|');
if (seen.has(key)) continue;
seen.add(key);
cookies.push(cookie);
}
}
return cookies;
}
async function removeStep1Cookie(chromeApi, cookie) {
const details = {
url: buildStep1CookieRemovalUrl(cookie),
name: cookie.name,
};
if (cookie.storeId) {
details.storeId = cookie.storeId;
}
if (cookie.partitionKey) {
details.partitionKey = cookie.partitionKey;
}
try {
const result = await chromeApi.cookies.remove(details);
return Boolean(result);
} catch (error) {
console.warn('[MultiPage:step1] remove cookie failed', {
domain: cookie?.domain,
name: cookie?.name,
message: getStep1ErrorMessage(error),
});
return false;
}
}
function createStep1Executor(deps = {}) {
const {
addLog,
chrome: chromeApi = globalThis.chrome,
completeStepFromBackground,
openSignupEntryTab,
} = deps;
async function clearOpenAiCookiesBeforeStep1() {
if (!chromeApi?.cookies?.getAll || !chromeApi.cookies?.remove) {
await addLog('步骤 1:当前浏览器不支持 cookies API,跳过打开官网前 cookie 清理。', 'warn');
return;
}
await addLog('步骤 1:打开 ChatGPT 官网前清理 ChatGPT / OpenAI cookies...', 'info');
const cookies = await collectStep1Cookies(chromeApi);
let removedCount = 0;
for (const cookie of cookies) {
if (await removeStep1Cookie(chromeApi, cookie)) {
removedCount += 1;
}
}
if (chromeApi.browsingData?.removeCookies) {
try {
await chromeApi.browsingData.removeCookies({
since: 0,
origins: STEP1_COOKIE_CLEAR_ORIGINS,
});
} catch (error) {
await addLog(`步骤 1browsingData 补扫 cookies 失败:${getStep1ErrorMessage(error)}`, 'warn');
}
}
await addLog(`步骤 1:已清理 ${removedCount} 个 ChatGPT / OpenAI cookies。`, 'ok');
}
async function executeStep1() {
await clearOpenAiCookiesBeforeStep1();
await addLog('步骤 1:正在打开 ChatGPT 官网...');
await openSignupEntryTab(1);
await completeStepFromBackground(1, {});
+125 -1
View File
@@ -12,6 +12,7 @@
getTabId,
isLocalhostOAuthCallbackUrl,
isTabAlive,
normalizeCodex2ApiUrl,
normalizeSub2ApiUrl,
rememberSourceLastUrl,
reuseOrCreateTab,
@@ -21,7 +22,86 @@
SUB2API_STEP9_RESPONSE_TIMEOUT_MS,
} = deps;
function normalizeString(value = '') {
return String(value || '').trim();
}
function parseLocalhostCallback(rawUrl) {
let parsed;
try {
parsed = new URL(rawUrl);
} catch {
throw new Error('步骤 10 捕获到的 localhost OAuth 回调地址格式无效,请重新执行步骤 9。');
}
const code = normalizeString(parsed.searchParams.get('code'));
const state = normalizeString(parsed.searchParams.get('state'));
if (!code || !state) {
throw new Error('步骤 10 捕获到的 localhost OAuth 回调地址缺少 code 或 state,请重新执行步骤 9。');
}
return {
url: parsed.toString(),
code,
state,
};
}
function getCodex2ApiErrorMessage(payload, responseStatus = 500) {
const details = [
payload?.error,
payload?.message,
payload?.detail,
payload?.reason,
]
.map((value) => normalizeString(value))
.find(Boolean);
return details || `Codex2API 请求失败(HTTP ${responseStatus})。`;
}
async function fetchCodex2ApiJson(origin, path, options = {}) {
const controller = new AbortController();
const timeoutMs = Math.max(1000, Math.floor(Number(options.timeoutMs) || 30000));
const timer = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(`${origin}${path}`, {
method: options.method || 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'X-Admin-Key': normalizeString(options.adminKey),
},
body: options.body === undefined ? undefined : JSON.stringify(options.body),
signal: controller.signal,
});
let payload = {};
try {
payload = await response.json();
} catch {
payload = {};
}
if (!response.ok) {
throw new Error(getCodex2ApiErrorMessage(payload, response.status));
}
return payload;
} catch (error) {
if (error?.name === 'AbortError') {
throw new Error('Codex2API 请求超时,请稍后重试。');
}
throw error;
} finally {
clearTimeout(timer);
}
}
async function executeStep10(state) {
if (getPanelMode(state) === 'codex2api') {
return executeCodex2ApiStep10(state);
}
if (getPanelMode(state) === 'sub2api') {
return executeSub2ApiStep10(state);
}
@@ -79,7 +159,8 @@
source: 'background',
payload: { localhostUrl: state.localhostUrl, vpsPassword: state.vpsPassword },
}, {
timeoutMs: 30000,
timeoutMs: 125000,
responseTimeoutMs: 125000,
retryDelayMs: 700,
logMessage: '步骤 10:CPA 面板通信未就绪,正在等待页面恢复...',
});
@@ -89,6 +170,48 @@
}
}
async function executeCodex2ApiStep10(state) {
if (state.localhostUrl && !isLocalhostOAuthCallbackUrl(state.localhostUrl)) {
throw new Error('步骤 9 捕获到的 localhost OAuth 回调地址无效,请重新执行步骤 9。');
}
if (!state.localhostUrl) {
throw new Error('缺少 localhost 回调地址,请先完成步骤 9。');
}
if (!state.codex2apiSessionId) {
throw new Error('缺少 Codex2API 会话信息,请重新执行步骤 7。');
}
if (!normalizeString(state.codex2apiAdminKey)) {
throw new Error('尚未配置 Codex2API 管理密钥,请先在侧边栏填写。');
}
const callback = parseLocalhostCallback(state.localhostUrl);
const expectedState = normalizeString(state.codex2apiOAuthState);
if (expectedState && expectedState !== callback.state) {
throw new Error('Codex2API 回调 state 与当前授权会话不匹配,请重新执行步骤 7。');
}
const codex2apiUrl = normalizeCodex2ApiUrl(state.codex2apiUrl);
const origin = new URL(codex2apiUrl).origin;
await addLog('步骤 10:正在向 Codex2API 提交回调并创建账号...');
const result = await fetchCodex2ApiJson(origin, '/api/admin/oauth/exchange-code', {
adminKey: state.codex2apiAdminKey,
method: 'POST',
body: {
session_id: state.codex2apiSessionId,
code: callback.code,
state: callback.state,
},
});
const verifiedStatus = normalizeString(result?.message) || 'Codex2API OAuth 账号添加成功';
await addLog(`步骤 10${verifiedStatus}`, 'ok');
await completeStepFromBackground(10, {
localhostUrl: callback.url,
verifiedStatus,
});
}
async function executeSub2ApiStep10(state) {
if (state.localhostUrl && !isLocalhostOAuthCallbackUrl(state.localhostUrl)) {
throw new Error('步骤 9 捕获到的 localhost OAuth 回调地址无效,请重新执行步骤 9。');
@@ -160,6 +283,7 @@
return {
executeCpaStep10,
executeCodex2ApiStep10,
executeStep10,
executeSub2ApiStep10,
};
+164 -8
View File
@@ -7,6 +7,7 @@
chrome,
completeStepFromBackground,
ensureContentScriptReadyOnTab,
ensureSignupAuthEntryPageReady,
ensureSignupEntryPageReady,
ensureSignupPostEmailPageReadyInTab,
getTabId,
@@ -16,6 +17,107 @@
SIGNUP_PAGE_INJECT_FILES,
} = deps;
function getErrorMessage(error) {
return String(typeof error === 'string' ? error : error?.message || '');
}
function isSignupEntryUnavailableErrorMessage(errorLike) {
const message = getErrorMessage(errorLike);
return /未找到可用的邮箱输入入口|当前页面没有可用的注册入口,也不在邮箱\/密码页/.test(message);
}
function isRetryableStep2TransportErrorMessage(errorLike) {
const message = getErrorMessage(errorLike);
return /Content script on signup-page did not respond in \d+s|Receiving end does not exist|message channel closed|A listener indicated an asynchronous response|port closed before a response was received|did not respond in \d+s/i.test(message);
}
function isLikelyLoggedInChatgptHomeUrl(rawUrl) {
const url = String(rawUrl || '').trim();
if (!url) {
return false;
}
try {
const parsed = new URL(url);
const host = String(parsed.hostname || '').toLowerCase();
if (!['chatgpt.com', 'www.chatgpt.com'].includes(host)) {
return false;
}
const path = String(parsed.pathname || '');
if (/^\/(?:auth\/|create-account\/|email-verification|log-in|add-phone)(?:[/?#]|$)/i.test(path)) {
return false;
}
return true;
} catch {
return false;
}
}
async function shouldForceAuthEntryRetry(tabId) {
if (!Number.isInteger(tabId) || typeof chrome?.tabs?.get !== 'function') {
return false;
}
try {
const tab = await chrome.tabs.get(tabId);
const currentUrl = String(tab?.url || '');
return isLikelyLoggedInChatgptHomeUrl(currentUrl);
} catch {
return false;
}
}
async function getTabUrl(tabId) {
if (!Number.isInteger(tabId) || typeof chrome?.tabs?.get !== 'function') {
return '';
}
try {
const tab = await chrome.tabs.get(tabId);
return String(tab?.url || '');
} catch {
return '';
}
}
async function completeStep2AsLoggedInSession(tabId, resolvedEmail, reasonMessage = '') {
const currentUrl = await getTabUrl(tabId);
if (!isLikelyLoggedInChatgptHomeUrl(currentUrl)) {
return false;
}
const reasonText = getErrorMessage(reasonMessage);
const reasonSuffix = reasonText ? `(触发原因:${reasonText}` : '';
await addLog(`步骤 2:检测到当前会话已登录 ChatGPT,已跳过注册链路(步骤 3/4/5),将直接进入步骤 6。${reasonSuffix}`, 'warn');
await completeStepFromBackground(2, {
email: resolvedEmail,
nextSignupState: 'already_logged_in_home',
nextSignupUrl: currentUrl || 'https://chatgpt.com/',
skippedPasswordStep: true,
skipRegistrationFlow: true,
});
return true;
}
async function submitSignupEmail(resolvedEmail, options = {}) {
const {
timeoutMs = 35000,
retryDelayMs = 700,
logMessage = '步骤 2:官网注册入口正在切换,等待页面恢复后继续输入邮箱...',
} = options;
try {
return await sendToContentScriptResilient('signup-page', {
type: 'EXECUTE_STEP',
step: 2,
source: 'background',
payload: { email: resolvedEmail },
}, {
timeoutMs,
retryDelayMs,
logMessage,
});
} catch (error) {
return { error: getErrorMessage(error) };
}
}
async function executeStep2(state) {
const resolvedEmail = await resolveSignupEmailForFlow(state);
@@ -34,19 +136,73 @@
});
}
const step2Result = await sendToContentScriptResilient('signup-page', {
type: 'EXECUTE_STEP',
step: 2,
source: 'background',
payload: { email: resolvedEmail },
}, {
timeoutMs: 20000,
if (await shouldForceAuthEntryRetry(signupTabId)) {
await addLog('步骤 2:检测到当前位于已登录 ChatGPT 首页,先切换认证入口页再提交邮箱。', 'warn');
try {
signupTabId = (await ensureSignupAuthEntryPageReady(2)).tabId;
} catch (entryError) {
const entryErrorMessage = getErrorMessage(entryError);
if (await completeStep2AsLoggedInSession(signupTabId, resolvedEmail, entryErrorMessage)) {
return;
}
await addLog('步骤 2:切换认证入口失败,正在重新打开官网入口并重试提交邮箱...', 'warn');
signupTabId = (await ensureSignupEntryPageReady(2)).tabId;
}
}
let step2Result = await submitSignupEmail(resolvedEmail, {
timeoutMs: 35000,
retryDelayMs: 700,
logMessage: '步骤 2:官网注册入口正在切换,等待页面恢复后继续输入邮箱...',
});
if (step2Result?.error) {
throw new Error(step2Result.error);
const errorMessage = getErrorMessage(step2Result.error);
if (isSignupEntryUnavailableErrorMessage(errorMessage)) {
await addLog('步骤 2:未找到邮箱输入入口,正在切换认证入口页后重试一次...', 'warn');
signupTabId = (await ensureSignupAuthEntryPageReady(2)).tabId;
step2Result = await submitSignupEmail(resolvedEmail, {
timeoutMs: 35000,
retryDelayMs: 700,
logMessage: '步骤 2:认证入口页已打开,正在重新提交邮箱...',
});
if (step2Result?.error) {
const retryErrorMessage = getErrorMessage(step2Result.error);
if (isSignupEntryUnavailableErrorMessage(retryErrorMessage)) {
if (await completeStep2AsLoggedInSession(signupTabId, resolvedEmail, retryErrorMessage)) {
return;
}
await addLog('步骤 2:认证入口仍不可用,正在重新进入官网注册入口再重试一次...', 'warn');
signupTabId = (await ensureSignupEntryPageReady(2)).tabId;
step2Result = await submitSignupEmail(resolvedEmail, {
timeoutMs: 35000,
retryDelayMs: 700,
logMessage: '步骤 2:重试官网注册入口后正在重新提交邮箱...',
});
}
}
} else if (isRetryableStep2TransportErrorMessage(errorMessage)) {
await addLog('步骤 2:注册入口页通信超时,正在切换认证入口页并重试提交邮箱...', 'warn');
signupTabId = (await ensureSignupAuthEntryPageReady(2)).tabId;
step2Result = await submitSignupEmail(resolvedEmail, {
timeoutMs: 45000,
retryDelayMs: 700,
logMessage: '步骤 2:认证入口页已打开,正在重新提交邮箱...',
});
}
}
if (step2Result?.error) {
const finalErrorMessage = getErrorMessage(step2Result.error);
if (
(isSignupEntryUnavailableErrorMessage(finalErrorMessage)
|| isRetryableStep2TransportErrorMessage(finalErrorMessage))
&& await completeStep2AsLoggedInSession(signupTabId, resolvedEmail, finalErrorMessage)
) {
return;
}
throw new Error(finalErrorMessage);
}
if (!step2Result?.alreadyOnPasswordPage) {