424 lines
16 KiB
JavaScript
424 lines
16 KiB
JavaScript
(function attachBackgroundStep2(root, factory) {
|
||
root.MultiPageBackgroundStep2 = factory();
|
||
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep2Module() {
|
||
function createStep2Executor(deps = {}) {
|
||
const {
|
||
addLog,
|
||
chrome,
|
||
completeStepFromBackground,
|
||
ensureContentScriptReadyOnTab,
|
||
ensureSignupAuthEntryPageReady,
|
||
ensureSignupEntryPageReady,
|
||
ensureSignupPostEmailPageReadyInTab,
|
||
ensureSignupPostIdentityPageReadyInTab = ensureSignupPostEmailPageReadyInTab,
|
||
getTabId,
|
||
isTabAlive,
|
||
phoneVerificationHelpers = null,
|
||
resolveSignupMethod = () => 'email',
|
||
resolveSignupEmailForFlow,
|
||
sendToContentScriptResilient,
|
||
SIGNUP_PAGE_INJECT_FILES,
|
||
} = deps;
|
||
|
||
function getErrorMessage(error) {
|
||
return String(typeof error === 'string' ? error : error?.message || '');
|
||
}
|
||
|
||
function isSignupEntryUnavailableErrorMessage(errorLike) {
|
||
const message = getErrorMessage(errorLike);
|
||
return /未找到可用的邮箱输入入口|当前页面没有可用的注册入口,也不在邮箱\/密码页/i.test(message);
|
||
}
|
||
|
||
function isSignupPhoneEntryUnavailableErrorMessage(errorLike) {
|
||
const message = getErrorMessage(errorLike);
|
||
return /未找到可用的手机号输入入口|当前页面没有可用的手机号注册入口,也不在密码页/i.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;
|
||
}
|
||
}
|
||
|
||
function isReadySignupEntryState(state = '') {
|
||
const normalized = String(state || '').trim().toLowerCase();
|
||
return normalized === 'entry_home'
|
||
|| normalized === 'email_entry'
|
||
|| normalized === 'phone_entry'
|
||
|| normalized === 'password_page';
|
||
}
|
||
|
||
async function getSignupEntryReadyState(tabId) {
|
||
if (!Number.isInteger(tabId) || typeof sendToContentScriptResilient !== 'function') {
|
||
return '';
|
||
}
|
||
|
||
try {
|
||
const result = await sendToContentScriptResilient('signup-page', {
|
||
type: 'ENSURE_SIGNUP_ENTRY_READY',
|
||
step: 2,
|
||
source: 'background',
|
||
payload: {},
|
||
}, {
|
||
timeoutMs: 12000,
|
||
retryDelayMs: 500,
|
||
logMessage: '步骤 2:正在检查官网注册入口状态...',
|
||
});
|
||
if (result?.error) {
|
||
return '';
|
||
}
|
||
return String(result?.state || '').trim().toLowerCase();
|
||
} catch {
|
||
return '';
|
||
}
|
||
}
|
||
|
||
async function isLikelyLoggedInChatgptHomeTab(tabId) {
|
||
if (typeof chrome?.tabs?.get !== 'function') {
|
||
return false;
|
||
}
|
||
|
||
const readyState = await getSignupEntryReadyState(tabId);
|
||
if (isReadySignupEntryState(readyState)) {
|
||
return false;
|
||
}
|
||
|
||
const currentUrl = await getTabUrl(tabId);
|
||
return isLikelyLoggedInChatgptHomeUrl(currentUrl);
|
||
}
|
||
|
||
async function shouldForceAuthEntryRetry(tabId) {
|
||
if (!Number.isInteger(tabId)) {
|
||
return false;
|
||
}
|
||
return isLikelyLoggedInChatgptHomeTab(tabId);
|
||
}
|
||
|
||
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 failStep2OnLoggedInSession(tabId, reasonMessage = '') {
|
||
if (!(await isLikelyLoggedInChatgptHomeTab(tabId))) {
|
||
return false;
|
||
}
|
||
|
||
const reasonText = getErrorMessage(reasonMessage);
|
||
const reasonSuffix = reasonText ? `(触发原因:${reasonText})` : '';
|
||
const message = `步骤 2:检测到当前停留在已登录 ChatGPT 首页,已阻止自动跳过步骤 3/4/5。请先执行步骤 1 清理会话后重试。${reasonSuffix}`;
|
||
await addLog(message, 'error');
|
||
throw new Error(message);
|
||
}
|
||
|
||
async function sendSignupIdentity(payload = {}, options = {}) {
|
||
const {
|
||
timeoutMs = 35000,
|
||
retryDelayMs = 700,
|
||
logMessage = '步骤 2:官网注册入口正在切换,等待页面恢复后继续输入邮箱...',
|
||
} = options;
|
||
|
||
try {
|
||
return await sendToContentScriptResilient('signup-page', {
|
||
type: 'EXECUTE_STEP',
|
||
step: 2,
|
||
source: 'background',
|
||
payload,
|
||
}, {
|
||
timeoutMs,
|
||
retryDelayMs,
|
||
logMessage,
|
||
});
|
||
} catch (error) {
|
||
return { error: getErrorMessage(error) };
|
||
}
|
||
}
|
||
|
||
async function ensureSignupPhoneEntryReady(tabId) {
|
||
if (!Number.isInteger(tabId)) {
|
||
throw new Error('步骤 2:未找到可用的注册页标签,无法切换到手机号注册入口。');
|
||
}
|
||
|
||
const result = await sendToContentScriptResilient('signup-page', {
|
||
type: 'ENSURE_SIGNUP_PHONE_ENTRY_READY',
|
||
step: 2,
|
||
source: 'background',
|
||
payload: {},
|
||
}, {
|
||
timeoutMs: 30000,
|
||
retryDelayMs: 700,
|
||
logMessage: '步骤 2:正在打开官网注册入口并切换到手机号注册...',
|
||
});
|
||
|
||
if (result?.error) {
|
||
throw new Error(result.error);
|
||
}
|
||
return result || {};
|
||
}
|
||
|
||
async function submitSignupEmail(resolvedEmail, options = {}) {
|
||
return sendSignupIdentity({ email: resolvedEmail }, options);
|
||
}
|
||
|
||
async function submitSignupPhone(phoneNumber, activation, options = {}) {
|
||
return sendSignupIdentity({
|
||
signupMethod: 'phone',
|
||
phoneNumber,
|
||
countryId: activation?.countryId ?? null,
|
||
countryLabel: String(activation?.countryLabel || '').trim(),
|
||
}, {
|
||
logMessage: '步骤 2:官网注册入口正在切换,等待手机号注册入口恢复...',
|
||
...options,
|
||
});
|
||
}
|
||
|
||
async function ensureSignupTabForStep2() {
|
||
let signupTabId = await getTabId('signup-page');
|
||
if (!signupTabId || !(await isTabAlive('signup-page'))) {
|
||
await addLog('步骤 2:未发现可用的注册页标签,正在重新打开 ChatGPT 官网...', 'warn');
|
||
signupTabId = (await ensureSignupEntryPageReady(2)).tabId;
|
||
} else {
|
||
await chrome.tabs.update(signupTabId, { active: true });
|
||
await ensureContentScriptReadyOnTab('signup-page', signupTabId, {
|
||
inject: SIGNUP_PAGE_INJECT_FILES,
|
||
injectSource: 'signup-page',
|
||
timeoutMs: 45000,
|
||
retryDelayMs: 900,
|
||
logMessage: '步骤 2:注册入口页内容脚本未就绪,正在等待页面恢复...',
|
||
});
|
||
}
|
||
return signupTabId;
|
||
}
|
||
|
||
async function executeSignupPhoneEntry(state) {
|
||
if (typeof phoneVerificationHelpers?.prepareSignupPhoneActivation !== 'function') {
|
||
throw new Error('手机号注册流程不可用:接码模块尚未初始化。');
|
||
}
|
||
|
||
let signupTabId = await ensureSignupTabForStep2();
|
||
if (await shouldForceAuthEntryRetry(signupTabId)) {
|
||
await addLog('步骤 2:检测到当前位于已登录 ChatGPT 首页,先切换认证入口页再提交手机号。', 'warn');
|
||
try {
|
||
signupTabId = (await ensureSignupAuthEntryPageReady(2)).tabId;
|
||
} catch (entryError) {
|
||
const entryErrorMessage = getErrorMessage(entryError);
|
||
if (await failStep2OnLoggedInSession(signupTabId, entryErrorMessage)) {
|
||
return;
|
||
}
|
||
await addLog('步骤 2:切换认证入口失败,正在重新打开官网入口并重试提交手机号...', 'warn');
|
||
signupTabId = (await ensureSignupEntryPageReady(2)).tabId;
|
||
}
|
||
}
|
||
|
||
try {
|
||
await ensureSignupPhoneEntryReady(signupTabId);
|
||
} catch (entryError) {
|
||
const entryErrorMessage = getErrorMessage(entryError);
|
||
if (await failStep2OnLoggedInSession(signupTabId, entryErrorMessage)) {
|
||
return;
|
||
}
|
||
if (
|
||
isSignupPhoneEntryUnavailableErrorMessage(entryErrorMessage)
|
||
|| isSignupEntryUnavailableErrorMessage(entryErrorMessage)
|
||
|| isRetryableStep2TransportErrorMessage(entryErrorMessage)
|
||
) {
|
||
await addLog('步骤 2:手机号注册入口尚未就绪,正在重新打开官网入口后重试一次...', 'warn');
|
||
signupTabId = (await ensureSignupEntryPageReady(2)).tabId;
|
||
await ensureSignupPhoneEntryReady(signupTabId);
|
||
} else {
|
||
throw entryError;
|
||
}
|
||
}
|
||
|
||
const activation = await phoneVerificationHelpers.prepareSignupPhoneActivation(state);
|
||
let step2Result = await submitSignupPhone(activation.phoneNumber, activation, {
|
||
timeoutMs: 45000,
|
||
retryDelayMs: 700,
|
||
logMessage: '步骤 2:官网注册入口正在切换,等待手机号注册入口恢复...',
|
||
});
|
||
|
||
if (step2Result?.error) {
|
||
const errorMessage = getErrorMessage(step2Result.error);
|
||
if (
|
||
isSignupPhoneEntryUnavailableErrorMessage(errorMessage)
|
||
|| isSignupEntryUnavailableErrorMessage(errorMessage)
|
||
|| isRetryableStep2TransportErrorMessage(errorMessage)
|
||
) {
|
||
await addLog('步骤 2:手机号注册入口不可用或通信超时,正在重新准备手机号注册入口后重试一次...', 'warn');
|
||
signupTabId = (await ensureSignupEntryPageReady(2)).tabId;
|
||
await ensureSignupPhoneEntryReady(signupTabId);
|
||
step2Result = await submitSignupPhone(activation.phoneNumber, activation, {
|
||
timeoutMs: 45000,
|
||
retryDelayMs: 700,
|
||
logMessage: '步骤 2:手机号注册入口已就绪,正在重新提交手机号...',
|
||
});
|
||
}
|
||
}
|
||
|
||
if (step2Result?.error) {
|
||
const finalErrorMessage = getErrorMessage(step2Result.error);
|
||
if (
|
||
(isSignupEntryUnavailableErrorMessage(finalErrorMessage)
|
||
|| isRetryableStep2TransportErrorMessage(finalErrorMessage))
|
||
&& await failStep2OnLoggedInSession(signupTabId, finalErrorMessage)
|
||
) {
|
||
return;
|
||
}
|
||
if (typeof phoneVerificationHelpers?.cancelSignupPhoneActivation === 'function') {
|
||
await phoneVerificationHelpers.cancelSignupPhoneActivation(state, activation).catch(() => {});
|
||
}
|
||
throw new Error(finalErrorMessage);
|
||
}
|
||
|
||
await addLog(`步骤 2:手机号 ${activation.phoneNumber} 已提交,正在等待页面加载并确认下一步入口...`);
|
||
const landingResult = await ensureSignupPostIdentityPageReadyInTab(signupTabId, 2, {
|
||
skipUrlWait: Boolean(step2Result?.alreadyOnPasswordPage),
|
||
});
|
||
|
||
await completeStepFromBackground(2, {
|
||
accountIdentifierType: 'phone',
|
||
accountIdentifier: activation.phoneNumber,
|
||
signupPhoneNumber: activation.phoneNumber,
|
||
signupPhoneActivation: activation,
|
||
nextSignupState: landingResult?.state || step2Result?.state || 'password_page',
|
||
nextSignupUrl: landingResult?.url || step2Result?.url || '',
|
||
skippedPasswordStep: landingResult?.state === 'phone_verification_page' || landingResult?.state === 'profile_page',
|
||
});
|
||
}
|
||
|
||
async function executeSignupEmailEntry(state) {
|
||
const resolvedEmail = await resolveSignupEmailForFlow(state);
|
||
|
||
let signupTabId = await ensureSignupTabForStep2();
|
||
|
||
if (await shouldForceAuthEntryRetry(signupTabId)) {
|
||
await addLog('步骤 2:检测到当前位于已登录 ChatGPT 首页,先切换认证入口页再提交邮箱。', 'warn');
|
||
try {
|
||
signupTabId = (await ensureSignupAuthEntryPageReady(2)).tabId;
|
||
} catch (entryError) {
|
||
const entryErrorMessage = getErrorMessage(entryError);
|
||
if (await failStep2OnLoggedInSession(signupTabId, 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) {
|
||
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 failStep2OnLoggedInSession(signupTabId, 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 failStep2OnLoggedInSession(signupTabId, finalErrorMessage)
|
||
) {
|
||
return;
|
||
}
|
||
throw new Error(finalErrorMessage);
|
||
}
|
||
|
||
if (!step2Result?.alreadyOnPasswordPage) {
|
||
await addLog(`步骤 2:邮箱 ${resolvedEmail} 已提交,正在等待页面加载并确认下一步入口...`);
|
||
}
|
||
|
||
const landingResult = await ensureSignupPostEmailPageReadyInTab(signupTabId, 2, {
|
||
skipUrlWait: Boolean(step2Result?.alreadyOnPasswordPage),
|
||
});
|
||
|
||
await completeStepFromBackground(2, {
|
||
email: resolvedEmail,
|
||
accountIdentifierType: 'email',
|
||
accountIdentifier: resolvedEmail,
|
||
nextSignupState: landingResult?.state || 'password_page',
|
||
nextSignupUrl: landingResult?.url || step2Result?.url || '',
|
||
skippedPasswordStep: landingResult?.state === 'verification_page',
|
||
});
|
||
}
|
||
|
||
async function executeStep2(state) {
|
||
if (resolveSignupMethod(state) === 'phone') {
|
||
return executeSignupPhoneEntry(state);
|
||
}
|
||
return executeSignupEmailEntry(state);
|
||
}
|
||
|
||
return { executeStep2 };
|
||
}
|
||
|
||
return { createStep2Executor };
|
||
});
|