Files
GuJumpgate/background/steps/wait-registration-success.js

376 lines
14 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
(function attachBackgroundStep6(root, factory) {
root.MultiPageBackgroundStep6 = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep6Module() {
const DEFAULT_REGISTRATION_SUCCESS_WAIT_MS = 4000;
const LOCAL_CPA_JSON_NO_RT_PANEL_MODE = 'local-cpa-json-no-rt';
const LOCAL_CPA_JSON_EXPORT_NODE_ID = 'local-cpa-json-export';
const CHATGPT_SESSION_EXPORT_URL = 'https://chatgpt.com/';
const STEP6_COOKIE_CLEAR_DOMAINS = [
'chatgpt.com',
'chat.openai.com',
'pay.openai.com',
'openai.com',
'auth.openai.com',
'auth0.openai.com',
'accounts.openai.com',
'paypal.com',
'stripe.com',
'checkout.stripe.com',
'meiguodizhi.com',
'mail-api.yuecheng.shop',
'yuecheng.shop',
];
const STEP6_COOKIE_CLEAR_ORIGINS = [
'https://chatgpt.com',
'https://chat.openai.com',
'https://pay.openai.com',
'https://auth.openai.com',
'https://auth0.openai.com',
'https://accounts.openai.com',
'https://openai.com',
'https://www.paypal.com',
'https://paypal.com',
'https://checkout.stripe.com',
'https://www.meiguodizhi.com',
'https://meiguodizhi.com',
'https://mail-api.yuecheng.shop',
];
function normalizeStep6CookieDomain(domain) {
return String(domain || '').trim().replace(/^\.+/, '').toLowerCase();
}
function shouldClearStep6Cookie(cookie) {
const domain = normalizeStep6CookieDomain(cookie?.domain);
if (!domain) return false;
return STEP6_COOKIE_CLEAR_DOMAINS.some((target) => (
domain === target || domain.endsWith(`.${target}`)
));
}
function buildStep6CookieRemovalUrl(cookie) {
const host = normalizeStep6CookieDomain(cookie?.domain);
const rawPath = String(cookie?.path || '/');
const path = rawPath.startsWith('/') ? rawPath : `/${rawPath}`;
return `https://${host}${path}`;
}
async function collectStep6Cookies(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 (!shouldClearStep6Cookie(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 removeStep6Cookie(chromeApi, cookie, getErrorMessage) {
const details = {
url: buildStep6CookieRemovalUrl(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:step6] remove cookie failed', {
domain: cookie?.domain,
name: cookie?.name,
message: getErrorMessage(error),
});
return false;
}
}
function createStep6Executor(deps = {}) {
const {
addLog = async () => {},
buildLocalHelperEndpoint = null,
chrome: chromeApi = globalThis.chrome,
completeNodeFromBackground,
createLocalCliProxyApi = null,
ensureContentScriptReadyOnTab = async () => {},
getErrorMessage = (error) => error?.message || String(error || '未知错误'),
getPanelMode = (state = {}) => String(state?.panelMode || '').trim() || 'cpa',
getTabId = async () => null,
normalizeHotmailLocalBaseUrl = (value) => String(value || '').trim(),
registrationSuccessWaitMs = DEFAULT_REGISTRATION_SUCCESS_WAIT_MS,
sessionExportInjectFiles = ['content/utils.js', 'content/operation-delay.js', 'content/plus-checkout.js'],
sendToContentScriptResilient = null,
sleepWithStop = async (ms) => new Promise((resolve) => setTimeout(resolve, Math.max(0, Number(ms) || 0))),
} = deps;
function normalizeString(value = '') {
return String(value || '').trim();
}
function isLocalCpaJsonNoRtMode(state = {}) {
return normalizeString(getPanelMode(state)) === LOCAL_CPA_JSON_NO_RT_PANEL_MODE;
}
function getLocalCliProxyApi() {
const factory = createLocalCliProxyApi
|| globalThis.MultiPageBackgroundLocalCliProxyApi?.createLocalCliProxyApi
|| null;
if (typeof factory !== 'function') {
throw new Error('本地 CPA JSON 无RT 模块未加载,无法导出认证文件。');
}
return factory({
crypto: globalThis.crypto,
fetch: typeof globalThis.fetch === 'function' ? globalThis.fetch.bind(globalThis) : null,
sessionToJsonConverter: globalThis.MultiPageSessionToJsonConverter,
});
}
async function saveLocalCpaJsonArtifactViaHelper(helperBaseUrl, artifact) {
const endpoint = typeof buildLocalHelperEndpoint === 'function'
? buildLocalHelperEndpoint(helperBaseUrl, '/save-auth-json')
: new URL('/save-auth-json', `${helperBaseUrl.replace(/\/+$/, '')}/`).toString();
const response = await fetch(endpoint, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
filePath: artifact.filePath,
directoryPath: artifact.directoryPath,
content: artifact.jsonText,
}),
});
let payload = {};
try {
payload = await response.json();
} catch {
payload = {};
}
if (!response.ok || payload?.ok === false) {
const helperError = normalizeString(payload?.error);
if (/Missing email\/clientId\/refreshToken/i.test(helperError)) {
throw new Error('本地 helper 未识别 /save-auth-json,当前运行的 hotmail_helper.py 版本过旧或不是当前项目目录。请停止旧 helper,并从当前 FlowPilot-FlowPilot1.0.2 目录重新启动本地助手。');
}
throw new Error(helperError || `本地 helper 写入失败(HTTP ${response.status})。`);
}
return {
...artifact,
filePath: normalizeString(payload?.filePath) || artifact.filePath,
};
}
async function openChatGptSessionExportTab(state = {}) {
if (chromeApi?.tabs?.create) {
const tab = await chromeApi.tabs.create({
url: CHATGPT_SESSION_EXPORT_URL,
active: false,
});
const tabId = Number(tab?.id);
if (Number.isInteger(tabId) && tabId > 0) {
return {
source: 'plus-checkout',
tabId,
temporary: true,
};
}
}
const fallbackTabId = Number(state?.plusCheckoutTabId || await getTabId('plus-checkout') || await getTabId('signup-page'));
if (!Number.isInteger(fallbackTabId) || fallbackTabId <= 0) {
throw new Error('未找到可读取 ChatGPT 会话的标签页,无法导出本地 CPA JSON 无RT。');
}
return {
source: 'plus-checkout',
tabId: fallbackTabId,
temporary: false,
};
}
async function closeTemporarySessionExportTab(tabInfo = {}) {
if (!tabInfo?.temporary || !Number.isInteger(Number(tabInfo?.tabId)) || !chromeApi?.tabs?.remove) {
return;
}
await chromeApi.tabs.remove(Number(tabInfo.tabId)).catch(() => {});
}
async function readChatGptSessionForExport(state = {}, visibleStep = 7) {
if (typeof sendToContentScriptResilient !== 'function') {
throw new Error('当前环境缺少 ChatGPT 会话读取通道,无法导出本地 CPA JSON 无RT。');
}
const tabInfo = await openChatGptSessionExportTab(state);
try {
await ensureContentScriptReadyOnTab(tabInfo.source, tabInfo.tabId, {
inject: sessionExportInjectFiles,
injectSource: tabInfo.source,
timeoutMs: 30000,
retryDelayMs: 800,
logMessage: `步骤 ${visibleStep}:正在连接 ChatGPT 页面,准备读取当前会话并导出 JSON...`,
logStep: visibleStep,
logStepKey: LOCAL_CPA_JSON_EXPORT_NODE_ID,
});
const sessionResult = await sendToContentScriptResilient(tabInfo.source, {
type: 'PLUS_CHECKOUT_GET_STATE',
step: visibleStep,
source: 'background',
payload: {
includeSession: true,
includeAccessToken: true,
},
}, {
timeoutMs: 15000,
retryDelayMs: 500,
logMessage: `步骤 ${visibleStep}:正在等待 ChatGPT 页面返回当前登录会话...`,
logStep: visibleStep,
logStepKey: LOCAL_CPA_JSON_EXPORT_NODE_ID,
});
if (sessionResult?.error) {
throw new Error(sessionResult.error);
}
return sessionResult;
} finally {
await closeTemporarySessionExportTab(tabInfo);
}
}
async function exportLocalCpaJsonNoRt(state = {}, options = {}) {
const visibleStep = Math.max(1, Math.floor(Number(options.visibleStep) || 7));
const helperBaseUrl = normalizeHotmailLocalBaseUrl(state.hotmailLocalBaseUrl);
const pluginDir = normalizeString(state.localCpaJsonPluginDir);
if (!helperBaseUrl) {
throw new Error('尚未配置 Hotmail 本地助手地址,请先在侧边栏填写。');
}
if (!pluginDir) {
throw new Error('尚未配置本地插件目录,请先在侧边栏填写。');
}
const sessionResult = await readChatGptSessionForExport(state, visibleStep);
const api = getLocalCliProxyApi();
const artifact = await api.buildAuthJsonArtifact({
pluginDir,
relativeAuthDir: state.localCpaJsonRelativeAuthDir,
session: sessionResult?.session,
accessToken: sessionResult?.accessToken,
sessionToken: sessionResult?.session?.sessionToken,
email: sessionResult?.email || sessionResult?.session?.user?.email || state?.email,
expiresAt: sessionResult?.expiresAt || sessionResult?.session?.expires,
accountId: sessionResult?.session?.account?.id,
userId: sessionResult?.session?.user?.id,
planType: sessionResult?.session?.account?.planType,
lastRefresh: '',
sourceName: 'SessionToJson Local No RT',
});
for (const warning of Array.isArray(artifact.warnings) ? artifact.warnings : []) {
await addLog(`步骤 ${visibleStep}${warning}`, 'warn');
}
const saved = await saveLocalCpaJsonArtifactViaHelper(helperBaseUrl, artifact);
const verifiedStatus = `本地CPA JSON 无RT 已导出:${saved.filePath}`;
await addLog(`步骤 ${visibleStep}${verifiedStatus}`, 'ok');
return {
verifiedStatus,
localCpaJsonFilePath: saved.filePath,
};
}
async function clearCookiesIfEnabled(state = {}) {
if (!state?.step6CookieCleanupEnabled) {
return;
}
if (!chromeApi?.cookies?.getAll || !chromeApi.cookies?.remove) {
await addLog('步骤 6:当前浏览器不支持 cookies API,跳过第六步 Cookies 清理。', 'warn');
return;
}
try {
await addLog('步骤 6:已开启 Cookies 清理,正在清理 ChatGPT / OpenAI cookies...', 'info');
const cookies = await collectStep6Cookies(chromeApi);
let removedCount = 0;
for (const cookie of cookies) {
if (await removeStep6Cookie(chromeApi, cookie, getErrorMessage)) {
removedCount += 1;
}
}
if (chromeApi.browsingData?.removeCookies) {
try {
await chromeApi.browsingData.removeCookies({
since: 0,
origins: STEP6_COOKIE_CLEAR_ORIGINS,
});
} catch (error) {
await addLog(`步骤 6browsingData 补扫 cookies 失败:${getErrorMessage(error)}`, 'warn');
}
}
await addLog(`步骤 6:已清理 ${removedCount} 个 ChatGPT / OpenAI cookies。`, 'ok');
} catch (error) {
await addLog(`步骤 6:Cookies 清理失败,已跳过并继续后续流程:${getErrorMessage(error)}`, 'warn');
}
}
async function executeStep6(state = {}) {
const baseWaitMs = Math.max(0, Math.floor(Number(registrationSuccessWaitMs) || 0));
const waitMs = baseWaitMs;
if (waitMs > 0) {
await addLog(`步骤 6:等待 ${Math.round(waitMs / 1000)} 秒,确认注册成功并让页面稳定...`, 'info');
await sleepWithStop(waitMs);
}
await clearCookiesIfEnabled(state);
await addLog('步骤 6:注册成功等待完成,注册阶段已结束。', 'ok');
await completeNodeFromBackground('wait-registration-success', {});
}
async function executeLocalCpaJsonNoRtExport(state = {}) {
if (!isLocalCpaJsonNoRtMode(state)) {
throw new Error('当前不是本地CPA JSON 无RT 模式,不能执行无RT导出节点。');
}
await addLog('步骤 7Plus Checkout 已完成,等待 5 秒后导出本地 CPA JSON 无RT...', 'info');
await sleepWithStop(5000);
const completionPayload = await exportLocalCpaJsonNoRt(state, { visibleStep: 7 });
await completeNodeFromBackground(LOCAL_CPA_JSON_EXPORT_NODE_ID, completionPayload);
}
return {
executeLocalCpaJsonNoRtExport,
executeStep6,
};
}
return { createStep6Executor };
});