From f3e8d051ea23d419cb775268eee1d4da6e87de5d Mon Sep 17 00:00:00 2001 From: Q3CC <106431792+q3cc@users.noreply.github.com> Date: Fri, 24 Apr 2026 23:30:36 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=AD=A5=E9=AA=A41?= =?UTF-8?q?=E6=89=93=E5=BC=80=E5=AE=98=E7=BD=91=E5=89=8D=E6=B8=85=E7=90=86?= =?UTF-8?q?OpenAI=20Cookie?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- background/steps/open-chatgpt.js | 128 +++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/background/steps/open-chatgpt.js b/background/steps/open-chatgpt.js index 2476a9d..222454b 100644 --- a/background/steps/open-chatgpt.js +++ b/background/steps/open-chatgpt.js @@ -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(`步骤 1:browsingData 补扫 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, {}); From 0676670b4be206dc656e95e5bac37127bcfd939f Mon Sep 17 00:00:00 2001 From: Q3CC Date: Fri, 24 Apr 2026 23:45:47 +0800 Subject: [PATCH 2/2] fix: snapshot LuckMail mails before polling code --- background.js | 10 ++ tests/background-luckmail.test.js | 146 ++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+) diff --git a/background.js b/background.js index 8f5cca1..5755190 100644 --- a/background.js +++ b/background.js @@ -3113,6 +3113,16 @@ async function pollLuckmailVerificationCode(step, state, pollPayload = {}) { excludeCodes: pollPayload.excludeCodes || [], }; + const initialCursor = normalizeLuckmailMailCursor((await getState()).currentLuckmailMailCursor); + if (!initialCursor.messageId && !initialCursor.receivedAt) { + const mailList = await client.user.getTokenMails(purchase.token); + const baselineCursor = buildLuckmailBaselineCursor(mailList?.mails || []); + await setLuckmailMailCursorState(baselineCursor); + if (baselineCursor?.messageId || baselineCursor?.receivedAt) { + await addLog(`步骤 ${step}:LuckMail 已保存当前邮箱旧邮件快照,后续仅使用新收到的验证码。`, 'info'); + } + } + let lastError = null; for (let attempt = 1; attempt <= maxAttempts; attempt++) { throwIfStopped(); diff --git a/tests/background-luckmail.test.js b/tests/background-luckmail.test.js index 300cd3b..ee36635 100644 --- a/tests/background-luckmail.test.js +++ b/tests/background-luckmail.test.js @@ -291,6 +291,152 @@ return { assert.equal(snapshot.buildCalls.length, 1); }); +test('pollLuckmailVerificationCode snapshots existing mails before accepting new LuckMail code', async () => { + const bundle = extractFunction('pollLuckmailVerificationCode'); + + const factory = new Function(` +let currentState = { + currentLuckmailPurchase: { + id: 7, + email_address: 'luck@example.com', + token: 'tok-luck', + }, + currentLuckmailMailCursor: null, +}; +const logs = []; +const cursorWrites = []; +let tokenCodeCalls = 0; + +function getCurrentLuckmailPurchase(state) { + return state.currentLuckmailPurchase; +} +function createLuckmailClient() { + return { + user: { + async getTokenMails() { + if (tokenCodeCalls === 0) { + return { + mails: [ + { message_id: 'old-mail', received_at: '2026-04-14 13:31:15', verification_code: '111111' }, + ], + }; + } + return { + mails: [ + { message_id: 'new-mail', received_at: '2026-04-14 13:32:05', verification_code: '222222' }, + { message_id: 'old-mail', received_at: '2026-04-14 13:31:15', verification_code: '111111' }, + ], + }; + }, + async getTokenCode() { + tokenCodeCalls += 1; + return tokenCodeCalls === 1 + ? { + has_new_mail: true, + verification_code: '111111', + mail: { message_id: 'old-mail', received_at: '2026-04-14 13:31:15', verification_code: '111111' }, + } + : { + has_new_mail: true, + verification_code: '222222', + mail: { message_id: 'new-mail', received_at: '2026-04-14 13:32:05', verification_code: '222222' }, + }; + }, + async getTokenMailDetail(_token, messageId) { + return { message_id: messageId, received_at: '2026-04-14 13:32:05', verification_code: '222222' }; + }, + }, + }; +} +async function getState() { + return currentState; +} +async function setLuckmailMailCursorState(cursor) { + currentState = { ...currentState, currentLuckmailMailCursor: cursor }; + cursorWrites.push(cursor); +} +function normalizeLuckmailMailCursor(cursor) { + return { + messageId: cursor?.messageId || cursor?.message_id || '', + receivedAt: cursor?.receivedAt || cursor?.received_at || '', + }; +} +function normalizeLuckmailTimestamp(value) { + return Date.parse(String(value || '').replace(' ', 'T') + 'Z') || 0; +} +function buildLuckmailMailCursor(mail) { + return { messageId: mail.message_id || '', receivedAt: mail.received_at || '' }; +} +function buildLuckmailBaselineCursor(mails) { + const latest = mails[0] || null; + return latest ? buildLuckmailMailCursor(latest) : null; +} +function isLuckmailMailNewerThanCursor(mail, cursor) { + if (!cursor?.messageId && !cursor?.receivedAt) return true; + if (mail.message_id === cursor.messageId) return false; + return normalizeLuckmailTimestamp(mail.received_at) > normalizeLuckmailTimestamp(cursor.receivedAt); +} +function pickLuckmailVerificationMail(mails, filters) { + const excludeCodes = new Set(filters.excludeCodes || []); + for (const mail of mails || []) { + if (!mail.verification_code || excludeCodes.has(mail.verification_code)) continue; + return { mail, code: mail.verification_code }; + } + return null; +} +function normalizeLuckmailTokenCode(result) { + return result; +} +async function resolveLuckmailVerificationMail(client, token, filters, tokenCodeResult) { + if (tokenCodeResult?.mail) { + const inline = pickLuckmailVerificationMail([tokenCodeResult.mail], filters); + if (inline) return inline; + } + const mailList = await client.user.getTokenMails(token); + return pickLuckmailVerificationMail(mailList.mails, filters); +} +async function addLog(message, level) { + logs.push({ message, level }); +} +function throwIfStopped() {} +function isStopError() { + return false; +} +async function sleepWithStop() {} + +${bundle} + +return { + pollLuckmailVerificationCode, + snapshot() { + return { currentState, cursorWrites, logs, tokenCodeCalls }; + }, +}; +`); + + const api = factory(); + const result = await api.pollLuckmailVerificationCode(4, await api.snapshot().currentState, { + maxAttempts: 2, + intervalMs: 1000, + senderFilters: ['openai'], + subjectFilters: ['code'], + excludeCodes: [], + }); + + assert.equal(result.code, '222222'); + const snapshot = api.snapshot(); + assert.deepStrictEqual(snapshot.cursorWrites[0], { + messageId: 'old-mail', + receivedAt: '2026-04-14 13:31:15', + }); + assert.deepStrictEqual(snapshot.cursorWrites.at(-1), { + messageId: 'new-mail', + receivedAt: '2026-04-14 13:32:05', + }); + assert.equal(snapshot.logs.some((entry) => /已保存当前邮箱旧邮件快照/.test(entry.message)), true); + assert.equal(snapshot.tokenCodeCalls, 2); +}); + test('listLuckmailPurchasesByProject only keeps openai purchases', async () => { const bundle = extractFunction('listLuckmailPurchasesByProject');