From fc2e89a8765d42669ad26b127243bf8f3711f1e5 Mon Sep 17 00:00:00 2001 From: QLHazyCoder <2825305047@qq.com> Date: Sat, 11 Apr 2026 14:56:09 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=20Hotmail=20?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E6=9C=AF=E8=AF=AD=E4=B8=BA=E6=9B=B4=E5=87=86?= =?UTF-8?q?=E7=A1=AE=E7=9A=84=E4=B8=AD=E6=96=87=E6=8F=8F=E8=BF=B0=EF=BC=8C?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=94=A8=E6=88=B7=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 22 +++++++++++----------- background.js | 36 ++++++++++++++++++------------------ sidepanel/sidepanel.html | 12 ++++++------ sidepanel/sidepanel.js | 8 ++++---- 4 files changed, 39 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 3a387bc..bcd61e6 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ 一个用于批量跑通 ChatGPT OAuth 注册/登录流程的 Chrome 扩展。 -当前版本基于侧边栏控制,支持单步执行、整套自动执行、停止当前流程、保存常用配置,以及通过 DuckDuckGo / QQ / 163 / Inbucket / Hotmail API 协助获取验证码。 +当前版本基于侧边栏控制,支持单步执行、整套自动执行、停止当前流程、保存常用配置,以及通过 DuckDuckGo / QQ / 163 / Inbucket / Hotmail 协助获取验证码。 ## 最新版本测试结果 @@ -42,7 +42,7 @@ - 支持自定义密码;留空时自动生成强密码 - 自动显示当前使用中的密码,便于后续保存 - 自动获取注册验证码与登录验证码 -- 支持 `Hotmail API`:直接使用 `email + client_id + refresh_token` 刷新 Microsoft token,并通过 Microsoft Graph 读取最新邮件 +- 支持 `Hotmail`:直接使用 `邮箱 + 客户端 ID + 刷新令牌(refresh token)` 刷新微软令牌,并通过 Microsoft Graph 读取最新邮件 - 支持 `QQ Mail`、`163 Mail`、`Inbucket mailbox` - 支持从 DuckDuckGo Email Protection 自动生成新的 `@duck.com` 地址 - Step 5 同时兼容两种页面: @@ -87,20 +87,20 @@ Step 1 和 Step 9 都依赖这个地址。 支持四种验证码来源: -- `Hotmail API` +- `Hotmail` - `163 Mail` - `QQ Mail` - `Inbucket` 说明: -- `Hotmail API` 通过侧边栏里的 Hotmail 账号池选择账号,并直接访问 Microsoft Graph 邮件接口 +- `Hotmail` 通过侧边栏里的 Hotmail 账号池选择账号,并直接访问 Microsoft Graph 邮件接口 - `QQ` 和 `163` 用于直接轮询网页邮箱 - `Inbucket` 通过你在侧边栏里配置的 host 访问 `mailbox` 页面:`https:///m//` ### `Hotmail 账号池` -仅当 `Mail = Hotmail API` 时使用。 +仅当 `Mail = Hotmail` 时使用。 每条账号支持保存: @@ -160,7 +160,7 @@ Step 3 使用的注册邮箱。 注意: -- 当 `Mail = Hotmail API` 时,这个输入框由账号池自动同步当前账号邮箱 +- 当 `Mail = Hotmail` 时,这个输入框由账号池自动同步当前账号邮箱 - 当前 `Auto` 按钮只负责 DuckDuckGo 地址获取 - 如果你使用 Inbucket,它只是验证码收件箱,不会自动生成 Inbucket 地址 @@ -209,7 +209,7 @@ Step 3 使用的注册邮箱。 1. Step 1 获取 CPA OAuth 链接 2. Step 2 打开 OpenAI 注册页 3. 根据 `Mail` 选择邮箱来源 -4. `Hotmail API` 会从账号池自动分配一个可用账号 +4. `Hotmail` 会从账号池自动分配一个可用账号 5. Duck 模式下会优先尝试自动获取邮箱;失败时暂停等待手动填写 6. 继续执行 Step 3 ~ Step 9 @@ -256,11 +256,11 @@ Step 3 使用的注册邮箱。 根据 `Mail` 配置,轮询邮箱并提取 6 位验证码。 -进入邮箱轮询前,脚本会先确认认证页是否已经进入验证码页面;如果密码页出现 `糟糕,出错了 / Operation timed out` 并带有 `重试` 按钮,会先自动点击 `重试`、回到密码页重新提交,再继续等待验证码页面。 +进入邮箱轮询前,脚本会先确认认证页是否已经进入验证码页面;如果密码页出现 `糟糕,出错了 / 操作超时(Operation timed out)` 并带有 `重试` 按钮,会先自动点击 `重试`、回到密码页重新提交,再继续等待验证码页面。 支持: -- `Hotmail API`(Microsoft Graph 邮件接口) +- `Hotmail`(Microsoft Graph 邮件接口) - `content/qq-mail.js` - `content/mail-163.js` - `content/inbucket-mail.js` @@ -383,7 +383,7 @@ Step 3 使用的注册邮箱。 - 邮箱服务 - Inbucket 主机 - Inbucket 邮箱名 -- Hotmail 账号池与对应 token +- Hotmail 账号池与对应令牌 - 兜底开关 特点: @@ -403,7 +403,7 @@ data/names.js 随机姓名、生日数据 content/utils.js 通用工具:等待元素、点击、日志、停止控制 content/vps-panel.js CPA 面板步骤:Step 1 / Step 9 content/signup-page.js OpenAI 注册/登录页步骤:Step 2 / 3 / 5 / 6 / 8 -hotmail-utils.js Hotmail API 相关通用 helper +hotmail-utils.js Hotmail 收信相关通用辅助 content/duck-mail.js Duck 邮箱自动获取 content/qq-mail.js QQ 邮箱验证码轮询 content/mail-163.js 163 邮箱验证码轮询 diff --git a/background.js b/background.js index 0505ee4..bf09bf9 100644 --- a/background.js +++ b/background.js @@ -402,7 +402,7 @@ async function ensureHotmailAccountForFlow(options = {}) { } if (!account) { - throw new Error('没有可用的 Hotmail 账号。请先在侧边栏添加至少一个带 refresh token 的账号。'); + throw new Error('没有可用的 Hotmail 账号。请先在侧边栏添加至少一个带刷新令牌(refresh token)的账号。'); } if (!isAccountAllocatable(account)) { throw new Error(`Hotmail 账号 ${account.email || account.id} 尚未就绪,无法读取邮件。`); @@ -416,10 +416,10 @@ async function requestHotmailMailApiLegacy(account, mailbox = 'INBOX') { throw new Error('Hotmail 账号缺少邮箱地址。'); } if (!account?.clientId) { - throw new Error(`Hotmail 账号 ${account.email || account.id} 缺少 client ID。`); + throw new Error(`Hotmail 账号 ${account.email || account.id} 缺少客户端 ID。`); } if (!account?.refreshToken) { - throw new Error(`Hotmail 账号 ${account.email || account.id} 缺少 refresh token。`); + throw new Error(`Hotmail 账号 ${account.email || account.id} 缺少刷新令牌(refresh token)。`); } const url = buildHotmailMailApiLatestUrl({ @@ -513,13 +513,13 @@ function isHotmailAccessTokenUsable(account, now = Date.now()) { async function refreshHotmailAccessToken(account) { if (!account?.email) { - throw new Error('Hotmail account email is missing.'); + throw new Error('Hotmail 账号缺少邮箱地址。'); } if (!account?.clientId) { - throw new Error(`Hotmail account ${account.email || account.id} is missing a client ID.`); + throw new Error(`Hotmail 账号 ${account.email || account.id} 缺少客户端 ID。`); } if (!account?.refreshToken) { - throw new Error(`Hotmail account ${account.email || account.id} is missing a refresh token.`); + throw new Error(`Hotmail 账号 ${account.email || account.id} 缺少刷新令牌(refresh token)。`); } const { timeoutMs, scopes, tokenUrl } = getHotmailGraphRequestConfig(); @@ -544,8 +544,8 @@ async function refreshHotmailAccessToken(account) { } catch (err) { const error = new Error( err?.name === 'AbortError' - ? `Hotmail token refresh timed out after ${Math.round(timeoutMs / 1000)}s` - : `Hotmail token refresh failed: ${err.message}` + ? `Hotmail 令牌刷新超时(>${Math.round(timeoutMs / 1000)} 秒)` + : `Hotmail 令牌刷新失败:${err.message}` ); error.code = 'HOTMAIL_TOKEN_REFRESH_FAILED'; throw error; @@ -563,7 +563,7 @@ async function refreshHotmailAccessToken(account) { if (!response.ok || !payload?.access_token) { const errorText = payload?.error_description || payload?.error?.message || payload?.error || payload?.message || text || `HTTP ${response.status}`; - const error = new Error(`Hotmail token refresh failed: ${errorText}`); + const error = new Error(`Hotmail 令牌刷新失败:${errorText}`); error.code = 'HOTMAIL_TOKEN_REFRESH_FAILED'; throw error; } @@ -603,8 +603,8 @@ async function requestHotmailGraphMessages(account, mailbox = 'INBOX') { } catch (err) { const error = new Error( err?.name === 'AbortError' - ? `Hotmail Graph request timed out after ${Math.round(timeoutMs / 1000)}s: ${mailbox}` - : `Hotmail Graph request failed: ${err.message}` + ? `Hotmail 邮件请求超时(>${Math.round(timeoutMs / 1000)} 秒):${mailbox}` + : `Hotmail 邮件请求失败:${err.message}` ); error.code = 'HOTMAIL_GRAPH_REQUEST_FAILED'; throw error; @@ -622,7 +622,7 @@ async function requestHotmailGraphMessages(account, mailbox = 'INBOX') { if (!response.ok) { const errorText = payload?.error?.message || payload?.error_description || payload?.message || text || `HTTP ${response.status}`; - const error = new Error(`Hotmail Graph request failed: ${errorText}`); + const error = new Error(`Hotmail 邮件请求失败:${errorText}`); error.code = response.status === 401 || response.status === 403 ? 'HOTMAIL_GRAPH_AUTH_FAILED' : 'HOTMAIL_GRAPH_REQUEST_FAILED'; @@ -755,9 +755,9 @@ async function pollHotmailVerificationCode(step, state, pollPayload = {}) { }) .slice(0, 3) .map((message) => { - const receivedAt = message?.receivedDateTime || 'unknown-time'; - const sender = message?.from?.emailAddress?.address || 'unknown'; - const subject = message?.subject || '(no subject)'; + const receivedAt = message?.receivedDateTime || '未知时间'; + const sender = message?.from?.emailAddress?.address || '未知发件人'; + const subject = message?.subject || '(无主题)'; const preview = String(message?.bodyPreview || '').replace(/\s+/g, ' ').trim().slice(0, 80); return `[${message.mailbox || 'INBOX'}] ${receivedAt} | ${sender} | ${subject} | ${preview}`; }) @@ -1367,7 +1367,7 @@ function getSourceLabel(source) { 'mail-163': '163 邮箱', 'inbucket-mail': 'Inbucket 邮箱', 'duck-mail': 'Duck 邮箱', - 'hotmail-api': 'Hotmail API', + 'hotmail-api': 'Hotmail(微软 Graph)', }; return labels[source] || source || '未知来源'; } @@ -2004,7 +2004,7 @@ function waitForStepComplete(step, timeoutMs = 120000) { throwIfStopped(); const timer = setTimeout(() => { stepWaiters.delete(step); - reject(new Error(`Step ${step} timed out after ${timeoutMs / 1000}s`)); + reject(new Error(`步骤 ${step} 等待超时(>${timeoutMs / 1000} 秒)`)); }, timeoutMs); stepWaiters.set(step, { @@ -2705,7 +2705,7 @@ async function executeStep3(state) { function getMailConfig(state) { const provider = state.mailProvider || 'qq'; if (provider === HOTMAIL_PROVIDER) { - return { provider: HOTMAIL_PROVIDER, label: 'Hotmail API' }; + return { provider: HOTMAIL_PROVIDER, label: 'Hotmail(微软 Graph)' }; } if (provider === '163') { return { source: 'mail-163', url: 'https://mail.163.com/js6/main.jsp?df=mail163_letter#module=mbox.ListModule%7C%7B%22fid%22%3A1%2C%22order%22%3A%22date%22%2C%22desc%22%3Atrue%7D', label: '163 邮箱' }; diff --git a/sidepanel/sidepanel.html b/sidepanel/sidepanel.html index bee432c..501f39c 100644 --- a/sidepanel/sidepanel.html +++ b/sidepanel/sidepanel.html @@ -58,7 +58,7 @@
邮箱服务
- Client ID - + 客户端 ID +
邮箱密码
- Refresh - + 刷新令牌 +
@@ -141,7 +141,7 @@
批量导入
- +
diff --git a/sidepanel/sidepanel.js b/sidepanel/sidepanel.js index 7d0df03..37d2094 100644 --- a/sidepanel/sidepanel.js +++ b/sidepanel/sidepanel.js @@ -698,8 +698,8 @@ function renderHotmailAccounts() { ${escapeHtml(getHotmailStatusLabel(account))}