A Chrome extension that automates a multi-step OAuth account registration and verification workflow. The extension operates across multiple websites (VPS panel, OpenAI auth, QQ Mail, ChatGPT) using a Side Panel as the control center and Content Scripts for DOM manipulation.
The user needs to repeatedly perform a multi-step workflow involving:
The extension automates DOM interactions at each step, with the user triggering steps manually via the Side Panel (semi-automatic mode, with future upgrade to full automation).
┌─────────────────────────────────────────────────┐
│ Chrome Extension │
├──────────┬──────────────┬───────────────────────┤
│ Side Panel │ Background │ Content Scripts │
│ (Control) │ (Service │ (Injected per site) │
│ │ Worker) │ │
│ - Step list│ - Tab mgmt │ - vps-panel.js │
│ - Buttons │ - Msg relay │ - signup-page.js │
│ - Status │ - State store│ - qq-mail.js │
│ - Logs │ - Orchestrate│ - chatgpt.js │
│ - Reset │ tab switch │ - utils.js (shared) │
└──────────┴──────────────┴───────────────────────┘
chrome.storage.session.chrome.storage.session (NOT in-memory, as MV3 service workers can be terminated after 30s of inactivity), relays and routes messages between Side Panel and Content Scripts, and handles tab-switching choreography between steps.utils.js for common utilities.All messages between components use a standard format:
{
type: "ACTION_NAME", // e.g. "FILL_SIGNUP", "CODE_FOUND", "STEP_COMPLETE", "STEP_ERROR", "LOG"
source: "qq-mail", // sender identifier
target: "signup-page", // intended receiver (optional, Background uses for routing)
step: 4, // which workflow step this relates to
payload: { ... }, // data
error: null // error message string (if any)
}
Message routing:
chrome.runtime.sendMessagechrome.tabs.sendMessage (only after content script reports ready)chrome.runtime.sendMessagetarget field and tabRegistry.Background must NOT send messages to a content script immediately after opening/navigating a tab. Instead:
{ type: "CONTENT_SCRIPT_READY", source: "vps-panel" } message on loadtabRegistryBackground maintains a tabRegistry in chrome.storage.session:
{
"vps-panel": { "tabId": 123, "ready": true },
"signup": { "tabId": 124, "ready": true },
"qq-mail": { "tabId": 125, "ready": false },
"chatgpt": { "tabId": 126, "ready": true }
}
Before operating on a tab, Background checks if the tab still exists via chrome.tabs.get(). If the tab was closed, it reopens and waits for the ready signal.
When a step requires cross-tab coordination (e.g., step 4: get code from QQ Mail → fill into signup page), the orchestration is always handled by Background:
{ type: "CODE_FOUND", payload: { code: "123456" } }chrome.storage.sessionchrome.tabs.update(tabId, { active: true }){ type: "FILL_CODE", payload: { code: "123456" } }STEP_COMPLETE or STEP_ERRORContent scripts NEVER communicate directly with each other.
| Step | Button | Executor | Action |
|---|---|---|---|
| 1 | Get OAuth Link | vps-panel.js |
Check VPS login state → Click OAuth login → Click Codex login → Read auth URL → Send to Background |
| 2 | Open Signup & Click Register | Background + signup-page.js |
Background opens auth URL in new tab → signup-page.js loads and clicks "Register" button |
| 3 | Fill Email & Password | signup-page.js |
Read email from Side Panel input (via Background) → Fill email + password (mimashisha0.0) → Submit |
| 4 | Get Signup Verification Code | qq-mail.js → Background → signup-page.js |
Background opens QQ Mail tab → qq-mail.js polls for new email from OpenAI → Extracts code → Background switches to signup tab → signup-page.js fills code and confirms |
| 5 | Fill Name & Birthday | signup-page.js |
After code verification, page transitions to profile form → Fill random English name + random birthday (age 19-25) → Click complete registration |
| 6 | Login ChatGPT | Background + chatgpt.js |
Background opens chatgpt.com in new tab → chatgpt.js clicks login → Enters email → If password field appears, fills password; otherwise waits for OTP flow → Submit |
| 7 | Get Login Verification Code | qq-mail.js → Background → chatgpt.js |
Background switches to QQ Mail tab → qq-mail.js polls for email newer than step 4's → Extracts code → Background switches to ChatGPT tab → chatgpt.js fills code → Login complete |
| 8 | Complete OAuth | Background (webNavigation) + chatgpt.js |
chatgpt.js navigates to step 1's oauthUrl → Background captures localhost redirect via webNavigation listener → Stores localhostUrl |
| 9 | VPS Verify | vps-panel.js |
Background switches to VPS panel tab → vps-panel.js pastes localhostUrl into input → Clicks verify button |
| Script | Match Patterns | all_frames |
|---|---|---|
utils.js |
(included by all below) | — |
vps-panel.js |
http://154.26.182.181:8317/* |
false |
signup-page.js |
https://auth0.openai.com/*, https://auth.openai.com/*, https://accounts.openai.com/* |
false |
qq-mail.js |
https://mail.qq.com/*, https://wx.mail.qq.com/* |
true |
chatgpt.js |
https://chatgpt.com/* |
false |
Note: qq-mail.js uses all_frames: true because old QQ Mail (mail.qq.com) renders inbox inside iframes.
Loaded before every content script. Provides:
// Wait for a DOM element to appear, with timeout
async function waitForElement(selector, timeout = 10000)
// React-compatible form filling — triggers proper event chain
function fillInput(el, value) {
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype, 'value'
).set;
nativeInputValueSetter.call(el, value);
el.dispatchEvent(new Event('input', { bubbles: true }));
el.dispatchEvent(new Event('change', { bubbles: true }));
}
// Send log message to Side Panel (via Background)
function log(message, level = 'info')
// Send step result to Background
function reportComplete(step, data = {})
function reportError(step, errorMessage)
// Standard ready signal
function reportReady(source)
reportError(1, "VPS panel not logged in, please log in first"). Then: locate OAuth login button → click → wait for Codex login button to appear → click → wait for auth URL to appear in DOM → read it → reportComplete(1, { oauthUrl })waitForElement for URL input field → fillInput with localhostUrl → click verify button → reportComplete(9)reportComplete(2)waitForElement for email input → fillInput email → fillInput password (mimashisha0.0) → click submit → reportComplete(3)waitForElement for verification code input → fillInput code → click confirm → reportComplete(4)waitForElement for name/birthday fields (page transition after code verification) → fillInput random first name, last name → fillInput random birthday → click complete → reportComplete(5)waitForElement before actingfillInput (React-compatible)reportError(step, "QQ Mail not logged in, please log in first")mail.qq.com): Content is inside iframes. With all_frames: true, the script runs in each frame. Only the frame containing the inbox list should act.wx.mail.qq.com): SPA, no iframes. All content in main frame with dynamic DOM updates.openai, noreply) or subject filter (e.g., verify, verification, code)log("Polling QQ Mail... attempt 3/20")reportComplete(4, { code, emailTimestamp })emailTimestamp → extract 6-digit code → reportComplete(7, { code })reportError(step, "No matching email found after 60s")waitForElement for login button → click → waitForElement for email input → fillInput email → submit → detect next state:
fillInput password → submit → reportComplete(6)reportComplete(6, { needsOTP: true }) (code will come from step 7)waitForElement for code input → fillInput code → submit → reportComplete(7)oauthUrl (from storage) → page will redirect → log("Navigating to OAuth URL, waiting for redirect...") → (localhost capture handled by Background)Data persisted in chrome.storage.session (survives service worker termination), passed between steps:
Step 1 → oauthUrl (authorization link)
Step 3 → email, password (from Side Panel input + hardcoded)
Step 4 → lastEmailTimestamp (to distinguish step 7's email)
Step 8 → localhostUrl (OAuth callback URL)
Additionally stored:
currentStep: which step is active (for UI restore)stepStatuses: { 1: "completed", 2: "completed", 3: "running", ... } (for UI restore)tabRegistry: tab ID mapping (for tab management)logs: array of log entries (for UI restore)flowStartTime: timestamp when flow began (for email filtering)┌──────────────────────────────────┐
│ Multi-Page Automation [Reset]│
├──────────────────────────────────┤
│ OAuth URL: [显示/未获取] │
│ Email: [________粘贴邮箱______] │
│ Status: Step 3 running... │
├──────────────────────────────────┤
│ 1 [Get OAuth Link] ✅ │
│ 2 [Open Signup] ✅ │
│ 3 [Fill Email/Password] ⏳ │
│ 4 [Get Signup Code] ⬚ 禁用 │
│ 5 [Fill Name/Birthday] ⬚ 禁用 │
│ 6 [Login ChatGPT] ⬚ 禁用 │
│ 7 [Get Login Code] ⬚ 禁用 │
│ 8 [Complete OAuth] ⬚ 禁用 │
│ 9 [VPS Verify] ⬚ 禁用 │
├──────────────────────────────────┤
│ Log: │
│ 10:23:01 [INFO] Step 1 started │
│ 10:23:03 [INFO] Found OAuth btn │
│ 10:23:05 [OK] Step 1 done │
│ 10:23:05 [INFO] oauthUrl saved │
│ 10:23:08 [INFO] Step 2 started │
│ 10:23:10 [INFO] Tab opened │
│ 10:23:12 [OK] Register clicked│
│ 10:23:15 [ERR] Step 3 failed: │
│ email input not found │
│ 10:23:15 [INFO] Retry step 3... │
└──────────────────────────────────┘
UI behaviors:
currentStep, stepStatuses, logs, and all data fields from chrome.storage.session and render.chrome.storage.session data, resets all steps to pending, clears logs. Does NOT close open tabs (user may want them).Every step must be fully debuggable. The user should never be stuck wondering "what went wrong".
Each content script logs at these checkpoints:
"Step N started""Waiting for selector: #email-input...""Found #email-input" or "Timeout waiting for #email-input after 10s""Filled email input with xxx@duck.com", "Clicked submit button""Page URL changed to https://...""Polling QQ Mail... attempt 5/20, no match yet""Verification code found: 123456""Step N completed successfully" or "Step N failed: [reason]"When a step fails, the error message should include actionable guidance:
| Error | Guidance shown in log |
|---|---|
| VPS not logged in | "Please log in to VPS panel at http://154.26.182.181:8317 and retry" |
| QQ Mail not logged in | "Please log in to QQ Mail and retry" |
| Element not found (timeout) | "Could not find [selector] on [url]. Page may have changed layout. Check DevTools." |
| Email not received (timeout) | "No matching email after 60s. Check QQ Mail manually. Email may be in spam." |
| Tab was closed | "Tab [name] was closed. Will reopen on retry." |
| Content script not ready | "Content script on [url] did not respond in 15s. Try refreshing the tab and retry." |
| Unknown error | "Unexpected error: [message]. Check DevTools console on [tab name] for details." |
[MultiPage:vps-panel], [MultiPage:qq-mail], etc.[MultiPage:bg] prefix.[MultiPage:bg] storage.set: stepStatuses = {...}reportError with guidance and pause.reportError with guidance and pause.All content script DOM operations use waitForElement(selector, timeout) from utils.js. Default timeout: 10 seconds. On timeout, reportError with the selector and URL for debugging.
lastEmailTimestamp to avoid duplicatesreportError with guidance to check spam folderchrome.webNavigation.onBeforeNavigate listener is registered in Background only when step 8 startshttp://localhostlocalhostUrl, remove listener, reportComplete(8)reportError with guidancesignup-page.js and chatgpt.js must handle page navigation/SPA route changeswaitForElement on the NEXT expected element after submission, not just submit and hope{
"permissions": [
"sidePanel",
"tabs",
"webNavigation",
"storage",
"scripting",
"activeTab"
],
"host_permissions": [
"http://154.26.182.181:8317/*",
"https://auth0.openai.com/*",
"https://auth.openai.com/*",
"https://accounts.openai.com/*",
"https://mail.qq.com/*",
"https://wx.mail.qq.com/*",
"https://chatgpt.com/*",
"http://localhost/*"
]
}
Note: scripting permission enables chrome.scripting.executeScript for dynamic injection when the auth flow redirects to unexpected domains.
chrome.sidePanel for persistent control panelchrome.storage.session: MV3 service workers can terminate after 30s idle; all flow state must be persisted, not held in memory