Loading...
Loading...
Compare original and translation side by side
Skill by ara.so — Daily 2026 Skills collection.
由ara.so提供的技能 —— 2026每日技能合集。
git clone https://github.com/QLHazyCoder/codex-oauth-automation-extension.git
cd codex-oauth-automation-extensionchrome://extensions/git clone https://github.com/QLHazyCoder/codex-oauth-automation-extension.git
cd codex-oauth-automation-extensionchrome://extensions/background.js # Main orchestrator: steps 1–9, tab management, state
manifest.json # Extension manifest
data/names.js # Random name/birthday data
content/utils.js # Shared helpers: waitForElement, click, stop control
content/vps-panel.js # CPA panel: Step 1 / Step 9
content/signup-page.js # OpenAI signup/login: Steps 2/3/5/6/8
content/duck-mail.js # DuckDuckGo @duck.com address generation
content/qq-mail.js # QQ Mail OTP polling
content/mail-163.js # 163 Mail OTP polling
content/inbucket-mail.js # Inbucket mailbox OTP polling
sidepanel/ # Sidebar UI (HTML/CSS/JS)background.js # 主协调器:步骤1–9、标签页管理、状态
manifest.json # 扩展清单
data/names.js # 随机姓名/生日数据
content/utils.js # 共享工具函数:waitForElement、click、停止控制
content/vps-panel.js # CPA面板:步骤1 / 步骤9
content/signup-page.js # OpenAI注册/登录:步骤2/3/5/6/8
content/duck-mail.js # DuckDuckGo @duck.com地址生成
content/qq-mail.js # QQ邮箱OTP轮询
content/mail-163.js # 163邮箱OTP轮询
content/inbucket-mail.js # Inbucket邮箱OTP轮询
sidepanel/ # 侧边栏UI(HTML/CSS/JS)| Field | Description | Example |
|---|---|---|
| Your OAuth management panel URL | |
| Verification code source | |
| Registration email (or click Auto for @duck.com) | |
| Custom password; leave blank to auto-generate | |
| Inbucket host (only when Mail=Inbucket) | |
| Inbucket mailbox name (only when Mail=Inbucket) | |
| 字段 | 说明 | 示例 |
|---|---|---|
| 你的OAuth管理面板URL | |
| 验证码来源 | |
| 注册邮箱(或点击Auto生成@duck.com邮箱) | |
| 自定义密码;留空则自动生成 | |
| Inbucket主机地址(仅当Mail=Inbucket时需填写) | |
| Inbucket邮箱名称(仅当Mail=Inbucket时需填写) | |
chrome.storage.sessionchrome.storage.localchrome.storage.sessionchrome.storage.localCodex OAuthOAuthCodex OAuthOAuthSign up / Register / 创建账户Sign up / Register / 创建账户Operation timed outopenainoreplyverifyauthduckduckgoforwardverifyverificationcode验证confirmOperation timed outopenainoreplyverifyauthduckduckgoforwardverifyverificationcode验证confirminput[name='age']input[name='age']debuggerchrome.webNavigation.onBeforeNavigatehttp(s)://localhost:<port>/auth/callback?code=...&state=...debuggerchrome.webNavigation.onBeforeNavigatehttp(s)://localhost:<port>/auth/callback?code=...&state=...codestate认证成功!http://localhost:1455/auth*codestate认证成功!http://localhost:1455/auth*Auto flow:
Step 1 → Step 2 → [Duck email auto-fetch, retry up to 5x]
↓ (if Duck fails) → Pause, wait for manual email input → Continue
Step 3 → Step 4 → Step 5 → Step 6 → Step 7 → Step 8 → Step 9
→ Repeat for N rounds自动流程:
步骤1 → 步骤2 → [自动获取Duck邮箱,最多重试5次]
↓(若Duck邮箱获取失败)→ 暂停,等待手动输入邮箱 → 继续
步骤3 → 步骤4 → 步骤5 → 步骤6 → 步骤7 → 步骤8 → 步骤9
→ 重复N轮// Wait for a DOM element with stop-signal support
async function waitForElement(selector, timeout = 30000) {
const start = Date.now();
while (Date.now() - start < timeout) {
// Check stop signal from background
const { stopFlow } = await chrome.storage.session.get('stopFlow');
if (stopFlow) throw new Error('STOPPED');
const el = document.querySelector(selector);
if (el) return el;
await new Promise(r => setTimeout(r, 500));
}
throw new Error(`Timeout waiting for: ${selector}`);
}// 等待DOM元素,支持停止信号
async function waitForElement(selector, timeout = 30000) {
const start = Date.now();
while (Date.now() - start < timeout) {
// 检查来自background的停止信号
const { stopFlow } = await chrome.storage.session.get('stopFlow');
if (stopFlow) throw new Error('STOPPED');
const el = document.querySelector(selector);
if (el) return el;
await new Promise(r => setTimeout(r, 500));
}
throw new Error(`Timeout waiting for: ${selector}`);
}// Save OAuth link to session
await chrome.storage.session.set({ oauthLink: extractedUrl });
// Read current email and password
const { currentEmail, currentPassword } = await chrome.storage.session.get([
'currentEmail',
'currentPassword'
]);
// Update step status
await chrome.storage.session.set({
stepStatus: { ...existingStatus, step3: 'done' }
});// 将OAuth链接保存到会话
await chrome.storage.session.set({ oauthLink: extractedUrl });
// 读取当前邮箱和密码
const { currentEmail, currentPassword } = await chrome.storage.session.get([
'currentEmail',
'currentPassword'
]);
// 更新步骤状态
await chrome.storage.session.set({
stepStatus: { ...existingStatus, step3: 'done' }
});// background.js → content script
const [tab] = await chrome.tabs.query({ url: '*://chat.openai.com/*' });
const result = await chrome.tabs.sendMessage(tab.id, {
action: 'FILL_EMAIL',
email: 'user@duck.com',
password: 'GeneratedPass1!'
});// background.js → 内容脚本
const [tab] = await chrome.tabs.query({ url: '*://chat.openai.com/*' });
const result = await chrome.tabs.sendMessage(tab.id, {
action: 'FILL_EMAIL',
email: 'user@duck.com',
password: 'GeneratedPass1!'
});// Only targets unread messages
const unseenEntries = document.querySelectorAll('.message-list-entry.unseen');
// From 2nd poll onwards, click the refresh button
if (pollCount > 1) {
const refreshBtn = document.querySelector('[data-action="refresh"]');
if (refreshBtn) refreshBtn.click();
await sleep(1000);
}
// After reading, delete the email to avoid re-matching// 仅针对未读消息
const unseenEntries = document.querySelectorAll('.message-list-entry.unseen');
// 从第2次轮询开始,点击刷新按钮
if (pollCount > 1) {
const refreshBtn = document.querySelector('[data-action="refresh"]');
if (refreshBtn) refreshBtn.click();
await sleep(1000);
}
// 读取后删除邮件,避免重复匹配// Attach debugger to tab for synthetic input events
await chrome.debugger.attach({ tabId }, '1.3');
const { x, y } = buttonBounds;
await chrome.debugger.sendCommand({ tabId }, 'Input.dispatchMouseEvent', {
type: 'mousePressed', x, y, button: 'left', clickCount: 1
});
await chrome.debugger.sendCommand({ tabId }, 'Input.dispatchMouseEvent', {
type: 'mouseReleased', x, y, button: 'left', clickCount: 1
});
await chrome.debugger.detach({ tabId });// 附加调试器到标签页以生成合成输入事件
await chrome.debugger.attach({ tabId }, '1.3');
const { x, y } = buttonBounds;
await chrome.debugger.sendCommand({ tabId }, 'Input.dispatchMouseEvent', {
type: 'mousePressed', x, y, button: 'left', clickCount: 1
});
await chrome.debugger.sendCommand({ tabId }, 'Input.dispatchMouseEvent', {
type: 'mouseReleased', x, y, button: 'left', clickCount: 1
});
await chrome.debugger.detach({ tabId });chrome.webNavigation.onBeforeNavigate.addListener(async (details) => {
// Only main frame, only the auth tab
if (details.frameId !== 0) return;
if (details.tabId !== authTabId) return;
const url = details.url;
// Strict: must be localhost /auth/callback with code + state
if (/^https?:\/\/localhost:\d+\/auth\/callback\?/.test(url)) {
const parsed = new URL(url);
if (parsed.searchParams.get('code') && parsed.searchParams.get('state')) {
await chrome.storage.session.set({ callbackUrl: url });
}
}
});chrome.webNavigation.onBeforeNavigate.addListener(async (details) => {
// 仅主框架、仅认证标签页
if (details.frameId !== 0) return;
if (details.tabId !== authTabId) return;
const url = details.url;
// 严格匹配:必须是localhost /auth/callback且包含code + state
if (/^https?:\/\/localhost:\d+\/auth\/callback\?/.test(url)) {
const parsed = new URL(url);
if (parsed.searchParams.get('code') && parsed.searchParams.get('state')) {
await chrome.storage.session.set({ callbackUrl: url });
}
}
});// From sidebar: send stop
await chrome.runtime.sendMessage({ action: 'STOP_FLOW' });
// In background.js: set flag and broadcast to all content scripts
await chrome.storage.session.set({ stopFlow: true });
const tabs = await chrome.tabs.query({});
for (const tab of tabs) {
chrome.tabs.sendMessage(tab.id, { action: 'STOP_FLOW' }).catch(() => {});
}// 从侧边栏发送停止指令
await chrome.runtime.sendMessage({ action: 'STOP_FLOW' });
// 在background.js中:设置标志并广播到所有内容脚本
await chrome.storage.session.set({ stopFlow: true });
const tabs = await chrome.tabs.query({});
for (const tab of tabs) {
chrome.tabs.sendMessage(tab.id, { action: 'STOP_FLOW' }).catch(() => {});
}// Triggered by sidebar "Auto" button next to Email field
// content/duck-mail.js opens:
// https://duckduckgo.com/email/settings/autofill
// Looks for existing private address or generates new one
const generateBtn = document.querySelector('[data-testid="generate-address"]');
if (generateBtn) generateBtn.click();
await waitForElement('.address-display');
const newAddress = document.querySelector('.address-display').textContent.trim();// 由邮箱字段旁的侧边栏"Auto"按钮触发
// content/duck-mail.js打开:
// https://duckduckgo.com/email/settings/autofill
// 查找现有私密地址或生成新地址
const generateBtn = document.querySelector('[data-testid="generate-address"]');
if (generateBtn) generateBtn.click();
await waitForElement('.address-display');
const newAddress = document.querySelector('.address-display').textContent.trim();// Save config
await chrome.storage.local.set({
cpaUrl: 'https://your-host/management.html#/oauth',
mailService: 'Inbucket', // '163 Mail' | 'QQ Mail' | 'Inbucket'
inbucketHost: 'your-inbucket-host',
inbucketMailbox: 'tmp-mailbox',
customPassword: '', // empty = auto-generate
});
// Load config
const config = await chrome.storage.local.get([
'cpaUrl', 'mailService', 'inbucketHost', 'inbucketMailbox', 'customPassword'
]);// 保存配置
await chrome.storage.local.set({
cpaUrl: 'https://your-host/management.html#/oauth',
mailService: 'Inbucket', // '163 Mail' | 'QQ Mail' | 'Inbucket'
inbucketHost: 'your-inbucket-host',
inbucketMailbox: 'tmp-mailbox',
customPassword: '', // 空值 = 自动生成
});
// 加载配置
const config = await chrome.storage.local.get([
'cpaUrl', 'mailService', 'inbucketHost', 'inbucketMailbox', 'customPassword'
]);content/signup-page.jscontent/signup-page.jshttps://<host>/m/<mailbox>/https://<host>/m/<mailbox>/management.html#/oauthcontent/vps-panel.jsmanagement.html#/oauthcontent/vps-panel.jshttp://localhost:1455/auth*background.jshttp://localhost:1455/auth*background.js1. Configure sidebar (CPA URL, Mail service, credentials)
2. Run Step 1 manually → verify OAuth link appears
3. Run Steps 2-4 manually → confirm email + OTP flow works
4. If successful, enable Auto with N=5 for a test batch
5. Scale up rounds once flow is stableNote: Always test single-step flow before enabling Auto. The most fragile steps are Step 8 (OAuth consent click) and Step 4/7 (OTP timing). Use Inbucket for most reliable OTP delivery in automated runs.
1. 配置侧边栏(CPA URL、邮件服务、凭据)
2. 手动运行步骤1 → 验证OAuth链接已显示
3. 手动运行步骤2-4 → 确认邮箱+OTP流程正常
4. 若成功,启用自动模式并设置N=5进行测试批量运行
5. 流程稳定后再增加轮数注意: 启用自动模式前务必测试单步流程。最不稳定的步骤是步骤8(OAuth授权点击)和步骤4/7(OTP时序)。在自动化运行中,使用Inbucket可获得最可靠的OTP投递。