diff --git a/README.md b/README.md index ef1aff3..e87ebfe 100644 --- a/README.md +++ b/README.md @@ -506,7 +506,7 @@ Cloudflare 模式下,插件不会再调用 Cloudflare API 创建路由。 3. `Fill Password` 4. `Get Signup Code` 5. `Fill Name / Birthday` -6. `Clear Login Cookies` +6. `Wait Registration Success` 7. `Login via OAuth` 8. `Get Login Code` 9. `Manual OAuth Confirm` @@ -544,7 +544,7 @@ Cloudflare 模式下,插件不会再调用 Cloudflare API 创建路由。 - 打开 `https://chatgpt.com/` - 确认官网首页或注册入口弹窗已经可操作 -这一步不再获取 `OAuth` 链接;`OAuth` 链接会在 Step 6 内部按需刷新。 +这一步不再获取 `OAuth` 链接;`OAuth` 链接会在 Step 7 内部按需刷新。 ### Step 2: Signup + Email @@ -600,15 +600,14 @@ Cloudflare 模式下,插件不会再调用 Cloudflare API 创建路由。 如果资料页出现顶部“我同意以下所有各项”总勾选框,脚本会优先自动勾选,再点击 `完成帐户创建`。 点击 `完成帐户创建` 后,Step 5 会立刻记为完成,不再等待页面跳转结果;自动运行在进入 Step 6 前只会等待当前页面加载完成,不再接管 ChatGPT 跳转或 onboarding 跳过逻辑。 -### Step 6: Clear Login Cookies +### Step 6: Wait Registration Success -这一步只负责登录前清理环境: +这一步只负责等待注册完成后的页面状态稳定: -- 开始前先等待 10 秒 -- 直接删除 `chatgpt.com / openai.com` 相关 cookies -- 必要时再用 `browsingData` 补扫一次 +- 固定等待 20 秒 +- 不再清理 `chatgpt.com / openai.com` 相关 cookies +- 等待完成后直接进入后续 OAuth 登录链路 -把 cookies 清理独立成单独步骤后,后续登录链路的重开锚点就不再落在这里。 ### Step 7: Login via OAuth diff --git a/background.js b/background.js index d90c623..74003d3 100644 --- a/background.js +++ b/background.js @@ -32,7 +32,7 @@ importScripts( 'background/steps/fill-password.js', 'background/steps/fetch-signup-code.js', 'background/steps/fill-profile.js', - 'background/steps/clear-login-cookies.js', + 'background/steps/wait-registration-success.js', 'background/steps/create-plus-checkout.js', 'background/steps/fill-plus-checkout.js', 'background/steps/gopay-manual-confirm.js', @@ -709,23 +709,7 @@ const PERSISTED_SETTING_DEFAULTS = { const PERSISTED_SETTING_KEYS = Object.keys(PERSISTED_SETTING_DEFAULTS); const SETTINGS_EXPORT_SCHEMA_VERSION = 1; const SETTINGS_EXPORT_FILENAME_PREFIX = 'multipage-settings'; -const STEP6_PRE_LOGIN_COOKIE_CLEAR_DELAY_MS = 25000; -const PRE_LOGIN_COOKIE_CLEAR_DOMAINS = [ - 'chatgpt.com', - 'chat.openai.com', - 'openai.com', - 'auth.openai.com', - 'auth0.openai.com', - 'accounts.openai.com', -]; -const PRE_LOGIN_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', -]; +const STEP6_REGISTRATION_SUCCESS_WAIT_MS = 20000; const DEFAULT_STATE = { currentStep: 0, // 当前流程执行到的步骤编号。 @@ -8413,7 +8397,7 @@ const AUTO_RUN_BACKGROUND_COMPLETED_STEP_KEYS = new Set([ 'open-chatgpt', 'submit-signup-email', 'fetch-signup-code', - 'clear-login-cookies', + 'wait-registration-success', 'plus-checkout-create', 'plus-checkout-billing', 'paypal-approve', @@ -10419,8 +10403,10 @@ const step5Executor = self.MultiPageBackgroundStep5?.createStep5Executor({ sendToContentScript, }); const step6Executor = self.MultiPageBackgroundStep6?.createStep6Executor({ + addLog, completeStepFromBackground, - runPreStep6CookieCleanup, + registrationSuccessWaitMs: STEP6_REGISTRATION_SUCCESS_WAIT_MS, + sleepWithStop, }); const step7Executor = self.MultiPageBackgroundStep7?.createStep7Executor({ addLog, @@ -10581,7 +10567,7 @@ const stepExecutorsByKey = { 'fill-password': (state) => step3Executor.executeStep3(state), 'fetch-signup-code': (state) => step4Executor.executeStep4(state), 'fill-profile': (state) => step5Executor.executeStep5(state), - 'clear-login-cookies': () => step6Executor.executeStep6(), + 'wait-registration-success': () => step6Executor.executeStep6(), 'plus-checkout-create': (state) => plusCheckoutCreateExecutor.executePlusCheckoutCreate(state), 'plus-checkout-billing': (state) => plusCheckoutBillingExecutor.executePlusCheckoutBilling(state), 'gopay-subscription-confirm': (state) => goPayManualConfirmExecutor.executeGoPayManualConfirm(state), @@ -10960,125 +10946,6 @@ async function executeStep5(state) { return step5Executor.executeStep5(state); } -// ============================================================ -// Step 6 Cookie Cleanup -// ============================================================ - -function normalizeCookieDomainForMatch(domain) { - return String(domain || '').trim().replace(/^\.+/, '').toLowerCase(); -} - -function shouldClearPreLoginCookie(cookie) { - const domain = normalizeCookieDomainForMatch(cookie?.domain); - if (!domain) return false; - return PRE_LOGIN_COOKIE_CLEAR_DOMAINS.some((target) => ( - domain === target || domain.endsWith(`.${target}`) - )); -} - -function buildCookieRemovalUrl(cookie) { - const host = normalizeCookieDomainForMatch(cookie?.domain); - const path = String(cookie?.path || '/').startsWith('/') - ? String(cookie?.path || '/') - : `/${String(cookie?.path || '')}`; - return `https://${host}${path}`; -} - -async function collectCookiesForPreLoginCleanup() { - if (!chrome.cookies?.getAll) { - return []; - } - - const stores = chrome.cookies.getAllCookieStores - ? await chrome.cookies.getAllCookieStores() - : [{ id: undefined }]; - const cookies = []; - const seen = new Set(); - - for (const store of stores) { - const storeId = store?.id; - const batch = await chrome.cookies.getAll(storeId ? { storeId } : {}); - for (const cookie of batch || []) { - if (!shouldClearPreLoginCookie(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 removeCookieDirectly(cookie) { - const details = { - url: buildCookieRemovalUrl(cookie), - name: cookie.name, - }; - - if (cookie.storeId) { - details.storeId = cookie.storeId; - } - if (cookie.partitionKey) { - details.partitionKey = cookie.partitionKey; - } - - try { - const result = await chrome.cookies.remove(details); - return Boolean(result); - } catch (err) { - console.warn(LOG_PREFIX, '[removeCookieDirectly] failed', { - domain: cookie?.domain, - name: cookie?.name, - message: getErrorMessage(err), - }); - return false; - } -} - -async function runPreStep6CookieCleanup() { - await addLog( - `步骤 6:开始前等待 ${Math.round(STEP6_PRE_LOGIN_COOKIE_CLEAR_DELAY_MS / 1000)} 秒,然后直接删除 ChatGPT / OpenAI cookies...`, - 'info' - ); - - await sleepWithStop(STEP6_PRE_LOGIN_COOKIE_CLEAR_DELAY_MS); - - if (!chrome.cookies?.getAll || !chrome.cookies?.remove) { - await addLog('步骤 6:当前浏览器不支持 cookies API,无法直接删除 cookies。', 'warn'); - return; - } - - const cookies = await collectCookiesForPreLoginCleanup(); - let removedCount = 0; - - for (const cookie of cookies) { - throwIfStopped(); - if (await removeCookieDirectly(cookie)) { - removedCount += 1; - } - } - - if (chrome.browsingData?.removeCookies) { - try { - await chrome.browsingData.removeCookies({ - since: 0, - origins: PRE_LOGIN_COOKIE_CLEAR_ORIGINS, - }); - } catch (err) { - await addLog(`步骤 6:browsingData 补扫 cookies 失败:${getErrorMessage(err)}`, 'warn'); - } - } - - await addLog(`步骤 6:已直接删除 ${removedCount} 个 ChatGPT / OpenAI cookies,准备继续获取链接并登录。`, 'ok'); -} - // ============================================================ // Step 7: Login and ensure the auth page reaches the login verification page // ============================================================ diff --git a/background/steps/clear-login-cookies.js b/background/steps/clear-login-cookies.js deleted file mode 100644 index 10b87ea..0000000 --- a/background/steps/clear-login-cookies.js +++ /dev/null @@ -1,19 +0,0 @@ -(function attachBackgroundStep6(root, factory) { - root.MultiPageBackgroundStep6 = factory(); -})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep6Module() { - function createStep6Executor(deps = {}) { - const { - completeStepFromBackground, - runPreStep6CookieCleanup, - } = deps; - - async function executeStep6() { - await runPreStep6CookieCleanup(); - await completeStepFromBackground(6); - } - - return { executeStep6 }; - } - - return { createStep6Executor }; -}); diff --git a/background/steps/wait-registration-success.js b/background/steps/wait-registration-success.js new file mode 100644 index 0000000..76ffe46 --- /dev/null +++ b/background/steps/wait-registration-success.js @@ -0,0 +1,28 @@ +(function attachBackgroundStep6(root, factory) { + root.MultiPageBackgroundStep6 = factory(); +})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep6Module() { + const DEFAULT_REGISTRATION_SUCCESS_WAIT_MS = 20000; + + function createStep6Executor(deps = {}) { + const { + addLog = async () => {}, + completeStepFromBackground, + registrationSuccessWaitMs = DEFAULT_REGISTRATION_SUCCESS_WAIT_MS, + sleepWithStop = async (ms) => new Promise((resolve) => setTimeout(resolve, Math.max(0, Number(ms) || 0))), + } = deps; + + async function executeStep6() { + const waitMs = Math.max(0, Math.floor(Number(registrationSuccessWaitMs) || 0)); + if (waitMs > 0) { + await addLog(`步骤 6:等待 ${Math.round(waitMs / 1000)} 秒,确认注册成功并让页面稳定...`, 'info'); + await sleepWithStop(waitMs); + } + await addLog('步骤 6:注册成功等待完成,准备继续获取 OAuth 链接并登录。', 'ok'); + await completeStepFromBackground(6); + } + + return { executeStep6 }; + } + + return { createStep6Executor }; +}); diff --git a/data/step-definitions.js b/data/step-definitions.js index 7d800cb..6cc1cae 100644 --- a/data/step-definitions.js +++ b/data/step-definitions.js @@ -14,7 +14,7 @@ { id: 3, order: 30, key: 'fill-password', title: '填写密码并继续' }, { id: 4, order: 40, key: 'fetch-signup-code', title: '获取注册验证码' }, { id: 5, order: 50, key: 'fill-profile', title: '填写姓名和生日' }, - { id: 6, order: 60, key: 'clear-login-cookies', title: '清理登录 Cookies' }, + { id: 6, order: 60, key: 'wait-registration-success', title: '等待注册成功' }, { id: 7, order: 70, key: 'oauth-login', title: '刷新 OAuth 并登录' }, { id: 8, order: 80, key: 'fetch-login-code', title: '获取登录验证码' }, { id: 9, order: 90, key: 'confirm-oauth', title: '自动确认 OAuth' }, diff --git a/tests/auto-step-random-delay.test.js b/tests/auto-step-random-delay.test.js index 0683996..7855c19 100644 --- a/tests/auto-step-random-delay.test.js +++ b/tests/auto-step-random-delay.test.js @@ -140,7 +140,7 @@ assert.strictEqual( assert.strictEqual( api.getAutoRunPreExecutionDelayMs(6, { definitions: { - 6: { key: 'clear-login-cookies' }, + 6: { key: 'wait-registration-success' }, }, }), 0, diff --git a/tests/background-step-modules.test.js b/tests/background-step-modules.test.js index d80924c..7b661cf 100644 --- a/tests/background-step-modules.test.js +++ b/tests/background-step-modules.test.js @@ -11,7 +11,7 @@ test('background imports step 1~10 modules', () => { 'background/steps/fill-password.js', 'background/steps/fetch-signup-code.js', 'background/steps/fill-profile.js', - 'background/steps/clear-login-cookies.js', + 'background/steps/wait-registration-success.js', 'background/steps/oauth-login.js', 'background/steps/fetch-login-code.js', 'background/steps/confirm-oauth.js', diff --git a/tests/background-step6-retry-limit.test.js b/tests/background-step6-retry-limit.test.js index 7541c19..bcef5b3 100644 --- a/tests/background-step6-retry-limit.test.js +++ b/tests/background-step6-retry-limit.test.js @@ -2,29 +2,34 @@ const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); -test('step 6 runs cookie cleanup and completes from background', async () => { - const source = fs.readFileSync('background/steps/clear-login-cookies.js', 'utf8'); +test('step 6 waits for registration success and completes from background', async () => { + const source = fs.readFileSync('background/steps/wait-registration-success.js', 'utf8'); const globalScope = {}; const api = new Function('self', `${source}; return self.MultiPageBackgroundStep6;`)(globalScope); const events = { - cleanupCalls: 0, + logs: [], + waits: [], completedSteps: [], }; const executor = api.createStep6Executor({ + addLog: async (message, level = 'info') => { + events.logs.push({ message, level }); + }, completeStepFromBackground: async (step) => { events.completedSteps.push(step); }, - runPreStep6CookieCleanup: async () => { - events.cleanupCalls += 1; + sleepWithStop: async (ms) => { + events.waits.push(ms); }, }); await executor.executeStep6(); - assert.equal(events.cleanupCalls, 1); + assert.deepStrictEqual(events.waits, [20000]); assert.deepStrictEqual(events.completedSteps, [6]); + assert.ok(events.logs.some(({ message }) => /等待 20 秒/.test(message))); }); test('step 7 retries up to configured limit and then fails', async () => { diff --git a/tests/step-definitions-module.test.js b/tests/step-definitions-module.test.js index deaaa74..b4c1d21 100644 --- a/tests/step-definitions-module.test.js +++ b/tests/step-definitions-module.test.js @@ -28,7 +28,7 @@ test('step definitions module exposes ordered normal and Plus step metadata', () 'fill-password', 'fetch-signup-code', 'fill-profile', - 'clear-login-cookies', + 'wait-registration-success', 'oauth-login', 'fetch-login-code', 'confirm-oauth', @@ -36,7 +36,7 @@ test('step definitions module exposes ordered normal and Plus step metadata', () ] ); assert.equal(steps[0].title, '打开 ChatGPT 官网'); - assert.equal(steps[5].title, '清理登录 Cookies'); + assert.equal(steps[5].title, '等待注册成功'); assert.equal(phoneSteps[1].title, '注册并输入手机号'); assert.equal(phoneSteps[3].title, '获取手机验证码'); @@ -58,7 +58,7 @@ test('step definitions module exposes ordered normal and Plus step metadata', () 'platform-verify', ] ); - assert.equal(plusSteps.some((step) => step.key === 'clear-login-cookies'), false); + assert.equal(plusSteps.some((step) => step.key === 'wait-registration-success'), false); assert.equal(plusSteps.some((step) => step.key === 'fetch-login-code'), true); assert.equal(plusSteps.find((step) => step.key === 'paypal-approve')?.title, 'PayPal 登录与授权'); assert.equal(plusPhoneSteps[1].title, '注册并输入手机号'); diff --git a/项目完整链路说明.md b/项目完整链路说明.md index 44e6981..34844fd 100644 --- a/项目完整链路说明.md +++ b/项目完整链路说明.md @@ -19,6 +19,7 @@ - 提交邮箱/手机号和密码 - 轮询注册验证码(邮箱或短信) - 填写姓名和生日 +- 等待注册成功状态稳定 - 刷新 OAuth 链接并登录(邮箱或手机号) - 轮询登录验证码(邮箱或短信) - 自动确认 OAuth 同意页 @@ -45,7 +46,7 @@ - 查询 GitHub Releases 并展示更新卡片;当前更新服务会区分 `Ultra`、历史 `Pro` 与 legacy `v` 三个版本族,排序时固定以 `Ultra` 为最高正式系列,同时会在读取缓存后重新排序,避免历史 `Pro` 或 `v` 版本误显示为比 `Ultra` 更新 - 展示一个单独的“接码”开关、注册方式 `signupMethod` 与“接码平台”下拉;接码平台当前支持 HeroSMS / 5sim / NexSMS。普通模式下开启接码后可把注册方式切到手机号注册,并在 OAuth 登录链路命中手机号登录验证码页时继续复用同一手机号续跑短信验证 - 侧栏在接码卡内提供一个独立运行态“注册手机号”输入框,位于接码订单运行状态下方;第 2 步自动拿到号码后立即回填,用户手动接管手机号注册时也可以直接改写这一个运行态槽位。这个输入框表达账号身份,不等同于接码订单;后续自动拉短信仍依赖 `signupPhoneActivation / signupPhoneCompletedActivation` -- 展示 `Plus 模式` 开关与 Plus 支付方式配置;支付方式支持 PayPal / GoPay / GPC,PayPal 展示账号池下拉与添加按钮,GoPay 展示手机号和 PIN,GPC 展示卡密、专用手机号、OTP 渠道、本地 macOS SMS helper 开关与 URL、PIN;步骤列表切换为 Plus 模式 13 步定义,普通模式的 Cookie 清理步骤不再显示或执行,登录验证码步骤会移动到 Plus 可见第 11 步 +- 展示 `Plus 模式` 开关与 Plus 支付方式配置;支付方式支持 PayPal / GoPay / GPC,PayPal 展示账号池下拉与添加按钮,GoPay 展示手机号和 PIN,GPC 展示卡密、专用手机号、OTP 渠道、本地 macOS SMS helper 开关与 URL、PIN;步骤列表切换为 Plus 模式 13 步定义,普通模式的注册成功等待步骤不再显示或执行,登录验证码步骤会移动到 Plus 可见第 11 步 - 为 Hotmail / 2925 账号池复用同一套“添加账号 / 取消添加 / 批量导入 / 收起列表”表单交互;共享的显隐控制放在 `sidepanel/account-pool-ui.js`,各自 manager 只保留 provider 相关字段校验与业务操作 ### 2.2 Background Service Worker @@ -414,14 +415,13 @@ IP 代理模块在同步、切换、Change、出口探测和自动运行成功 文件: -- [background/steps/clear-login-cookies.js](c:/Users/projectf/Downloads/codex注册扩展/background/steps/clear-login-cookies.js) +- [background/steps/wait-registration-success.js](c:/Users/projectf/Downloads/codex注册扩展/background/steps/wait-registration-success.js) 流程: -1. 等待登录前清理延迟 -2. 直接删除 ChatGPT / OpenAI cookies -3. 必要时用 `browsingData` 补扫 cookies -4. 完成后进入后续登录链路 +1. 固定等待 20 秒 +2. 不再删除 ChatGPT / OpenAI cookies +3. 完成后进入后续登录链路 ### Step 7 @@ -542,7 +542,7 @@ Codex2API 补充: ## 6.1 Plus 模式链路 -Plus 模式通过 `plusModeEnabled` 开启,目标是在普通注册资料完成后,不执行原 Step 6 Cookie 清理,也不执行原 Step 8 登录验证码步骤,而是先完成 Plus Checkout 与 PayPal 授权,再复用现有 OAuth 后半段。 +Plus 模式通过 `plusModeEnabled` 开启,目标是在普通注册资料完成后,不执行普通模式 Step 6 注册成功等待,也不执行原 Step 8 登录验证码步骤,而是先完成 Plus Checkout 与 PayPal 授权,再复用现有 OAuth 后半段。 Plus 模式可见步骤: @@ -560,7 +560,7 @@ Plus 模式可见步骤: 隐藏与跳过规则: -- 原 Step 6 `清理登录 Cookies`:Plus 模式下隐藏且不执行,因为 Plus Checkout 创建依赖当前 ChatGPT 登录态。 +- 普通模式 Step 6 `等待注册成功`:Plus 模式下隐藏且不执行,因为 Plus Checkout 创建会直接承接当前 ChatGPT 登录态。 - 原 Step 8 `获取登录验证码`:Plus 模式下移动到可见第 11 步,继续负责登录验证码页的收码与提交。 - 原 Step 9 不能跳过;它负责点击 OAuth 同意页并捕获 `localhostUrl`。 - 原 Step 10 不能跳过;它依赖 `localhostUrl` 完成平台侧账号创建或回调验证。 diff --git a/项目文件结构说明.md b/项目文件结构说明.md index 8392c47..4164348 100644 --- a/项目文件结构说明.md +++ b/项目文件结构说明.md @@ -62,7 +62,7 @@ ## `background/steps/` -- `background/steps/clear-login-cookies.js`:步骤 6 实现,负责登录前 Cookie 清理。 +- `background/steps/wait-registration-success.js`:步骤 6 实现,负责注册资料提交后等待 20 秒,让注册成功状态和页面跳转稳定。 - `background/steps/confirm-oauth.js`:步骤 9 实现,负责 OAuth 同意页按钮定位、点击、localhost 回调监听与回调完成;等待 callback 期间会动态检查 OAuth 总预算开关,关闭后仅保留本地回调等待超时。 - `background/steps/create-plus-checkout.js`:Plus 模式第 6 步实现,负责在已登录 ChatGPT 页面创建 Plus checkout session,打开 `chatgpt.com/checkout/openai_ie/{checkout_session_id}` 短链,并记录 checkout 运行态。 - `background/steps/fetch-login-code.js`:步骤 8 实现,负责按真实认证页状态分发登录后续流程:邮箱验证码页走邮箱轮询、验证码回填与回退控制;真实 `phone-verification` 页才复用手机号验证码共享流程;手机号注册登录后若进入 `add-email`,会先生成/解析邮箱并提交绑定,再进入邮箱验证码轮询;邮箱分支对非 2925 provider 会固定当前验证码页显示邮箱作为本次 Step 8 的目标邮箱,当 provider 为 2925 时,会在轮询前先确保当前 2925 账号已自动登录。 @@ -204,7 +204,7 @@ - `tests/background-signup-step2-branching.test.js`:测试在 Gmail / 2925 模式下,已有兼容别名邮箱时应直接复用,不应再次重生成。 - `tests/background-step-modules.test.js`:测试步骤模块文件都已由后台入口加载。 - `tests/background-step5-submit-short-circuit.test.js`:测试步骤 5 会把生成好的资料直接转发给内容脚本,并依赖完成信号收尾。 -- `tests/background-step6-retry-limit.test.js`:测试步骤 6 的 Cookie 清理,以及步骤 7 的有限重试上限与 `add-phone` 命中后的立即跳出行为。 +- `tests/background-step6-retry-limit.test.js`:测试步骤 6 的注册成功等待,以及步骤 7 的有限重试上限与 `add-phone` 命中后的立即跳出行为。 - `tests/background-step7-recovery.test.js`:测试步骤 8 获取登录验证码后直接提交(不再回放步骤 7),覆盖邮箱验证码页、真实手机验证码页、`add-email` 绑定邮箱后收码、2925 固定回看窗口与关闭重发间隔。 - `tests/background-step-registry.test.js`:测试后台步骤注册表和共享步骤定义已接入。 - `tests/background-tab-runtime-module.test.js`:测试标签运行时模块已接入且导出工厂,并覆盖等待标签完成、等待标签稳定完成,以及等待过程中的 Stop 中断行为。