feat: 更新 Hotmail 相关术语为更准确的中文描述,优化用户界面

This commit is contained in:
QLHazyCoder
2026-04-11 14:56:09 +08:00
parent 1198f704d7
commit fc2e89a876
4 changed files with 39 additions and 39 deletions
+11 -11
View File
@@ -2,7 +2,7 @@
一个用于批量跑通 ChatGPT OAuth 注册/登录流程的 Chrome 扩展。 一个用于批量跑通 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` - 支持 `QQ Mail``163 Mail``Inbucket mailbox`
- 支持从 DuckDuckGo Email Protection 自动生成新的 `@duck.com` 地址 - 支持从 DuckDuckGo Email Protection 自动生成新的 `@duck.com` 地址
- Step 5 同时兼容两种页面: - Step 5 同时兼容两种页面:
@@ -87,20 +87,20 @@ Step 1 和 Step 9 都依赖这个地址。
支持四种验证码来源: 支持四种验证码来源:
- `Hotmail API` - `Hotmail`
- `163 Mail` - `163 Mail`
- `QQ Mail` - `QQ Mail`
- `Inbucket` - `Inbucket`
说明: 说明:
- `Hotmail API` 通过侧边栏里的 Hotmail 账号池选择账号,并直接访问 Microsoft Graph 邮件接口 - `Hotmail` 通过侧边栏里的 Hotmail 账号池选择账号,并直接访问 Microsoft Graph 邮件接口
- `QQ``163` 用于直接轮询网页邮箱 - `QQ``163` 用于直接轮询网页邮箱
- `Inbucket` 通过你在侧边栏里配置的 host 访问 `mailbox` 页面:`https://<your-inbucket-host>/m/<mailbox>/` - `Inbucket` 通过你在侧边栏里配置的 host 访问 `mailbox` 页面:`https://<your-inbucket-host>/m/<mailbox>/`
### `Hotmail 账号池` ### `Hotmail 账号池`
仅当 `Mail = Hotmail API` 时使用。 仅当 `Mail = Hotmail` 时使用。
每条账号支持保存: 每条账号支持保存:
@@ -160,7 +160,7 @@ Step 3 使用的注册邮箱。
注意: 注意:
-`Mail = Hotmail API` 时,这个输入框由账号池自动同步当前账号邮箱 -`Mail = Hotmail` 时,这个输入框由账号池自动同步当前账号邮箱
- 当前 `Auto` 按钮只负责 DuckDuckGo 地址获取 - 当前 `Auto` 按钮只负责 DuckDuckGo 地址获取
- 如果你使用 Inbucket,它只是验证码收件箱,不会自动生成 Inbucket 地址 - 如果你使用 Inbucket,它只是验证码收件箱,不会自动生成 Inbucket 地址
@@ -209,7 +209,7 @@ Step 3 使用的注册邮箱。
1. Step 1 获取 CPA OAuth 链接 1. Step 1 获取 CPA OAuth 链接
2. Step 2 打开 OpenAI 注册页 2. Step 2 打开 OpenAI 注册页
3. 根据 `Mail` 选择邮箱来源 3. 根据 `Mail` 选择邮箱来源
4. `Hotmail API` 会从账号池自动分配一个可用账号 4. `Hotmail` 会从账号池自动分配一个可用账号
5. Duck 模式下会优先尝试自动获取邮箱;失败时暂停等待手动填写 5. Duck 模式下会优先尝试自动获取邮箱;失败时暂停等待手动填写
6. 继续执行 Step 3 ~ Step 9 6. 继续执行 Step 3 ~ Step 9
@@ -256,11 +256,11 @@ Step 3 使用的注册邮箱。
根据 `Mail` 配置,轮询邮箱并提取 6 位验证码。 根据 `Mail` 配置,轮询邮箱并提取 6 位验证码。
进入邮箱轮询前,脚本会先确认认证页是否已经进入验证码页面;如果密码页出现 `糟糕,出错了 / Operation timed out` 并带有 `重试` 按钮,会先自动点击 `重试`、回到密码页重新提交,再继续等待验证码页面。 进入邮箱轮询前,脚本会先确认认证页是否已经进入验证码页面;如果密码页出现 `糟糕,出错了 / 操作超时(Operation timed out` 并带有 `重试` 按钮,会先自动点击 `重试`、回到密码页重新提交,再继续等待验证码页面。
支持: 支持:
- `Hotmail API`Microsoft Graph 邮件接口) - `Hotmail`Microsoft Graph 邮件接口)
- `content/qq-mail.js` - `content/qq-mail.js`
- `content/mail-163.js` - `content/mail-163.js`
- `content/inbucket-mail.js` - `content/inbucket-mail.js`
@@ -383,7 +383,7 @@ Step 3 使用的注册邮箱。
- 邮箱服务 - 邮箱服务
- Inbucket 主机 - Inbucket 主机
- Inbucket 邮箱名 - Inbucket 邮箱名
- Hotmail 账号池与对应 token - Hotmail 账号池与对应令牌
- 兜底开关 - 兜底开关
特点: 特点:
@@ -403,7 +403,7 @@ data/names.js 随机姓名、生日数据
content/utils.js 通用工具:等待元素、点击、日志、停止控制 content/utils.js 通用工具:等待元素、点击、日志、停止控制
content/vps-panel.js CPA 面板步骤:Step 1 / Step 9 content/vps-panel.js CPA 面板步骤:Step 1 / Step 9
content/signup-page.js OpenAI 注册/登录页步骤:Step 2 / 3 / 5 / 6 / 8 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/duck-mail.js Duck 邮箱自动获取
content/qq-mail.js QQ 邮箱验证码轮询 content/qq-mail.js QQ 邮箱验证码轮询
content/mail-163.js 163 邮箱验证码轮询 content/mail-163.js 163 邮箱验证码轮询
+18 -18
View File
@@ -402,7 +402,7 @@ async function ensureHotmailAccountForFlow(options = {}) {
} }
if (!account) { if (!account) {
throw new Error('没有可用的 Hotmail 账号。请先在侧边栏添加至少一个带 refresh token 的账号。'); throw new Error('没有可用的 Hotmail 账号。请先在侧边栏添加至少一个带刷新令牌(refresh token的账号。');
} }
if (!isAccountAllocatable(account)) { if (!isAccountAllocatable(account)) {
throw new Error(`Hotmail 账号 ${account.email || account.id} 尚未就绪,无法读取邮件。`); throw new Error(`Hotmail 账号 ${account.email || account.id} 尚未就绪,无法读取邮件。`);
@@ -416,10 +416,10 @@ async function requestHotmailMailApiLegacy(account, mailbox = 'INBOX') {
throw new Error('Hotmail 账号缺少邮箱地址。'); throw new Error('Hotmail 账号缺少邮箱地址。');
} }
if (!account?.clientId) { 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) { 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({ const url = buildHotmailMailApiLatestUrl({
@@ -513,13 +513,13 @@ function isHotmailAccessTokenUsable(account, now = Date.now()) {
async function refreshHotmailAccessToken(account) { async function refreshHotmailAccessToken(account) {
if (!account?.email) { if (!account?.email) {
throw new Error('Hotmail account email is missing.'); throw new Error('Hotmail 账号缺少邮箱地址。');
} }
if (!account?.clientId) { 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) { 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(); const { timeoutMs, scopes, tokenUrl } = getHotmailGraphRequestConfig();
@@ -544,8 +544,8 @@ async function refreshHotmailAccessToken(account) {
} catch (err) { } catch (err) {
const error = new Error( const error = new Error(
err?.name === 'AbortError' err?.name === 'AbortError'
? `Hotmail token refresh timed out after ${Math.round(timeoutMs / 1000)}s` ? `Hotmail 令牌刷新超时(>${Math.round(timeoutMs / 1000)} 秒)`
: `Hotmail token refresh failed: ${err.message}` : `Hotmail 令牌刷新失败:${err.message}`
); );
error.code = 'HOTMAIL_TOKEN_REFRESH_FAILED'; error.code = 'HOTMAIL_TOKEN_REFRESH_FAILED';
throw error; throw error;
@@ -563,7 +563,7 @@ async function refreshHotmailAccessToken(account) {
if (!response.ok || !payload?.access_token) { if (!response.ok || !payload?.access_token) {
const errorText = payload?.error_description || payload?.error?.message || payload?.error || payload?.message || text || `HTTP ${response.status}`; 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'; error.code = 'HOTMAIL_TOKEN_REFRESH_FAILED';
throw error; throw error;
} }
@@ -603,8 +603,8 @@ async function requestHotmailGraphMessages(account, mailbox = 'INBOX') {
} catch (err) { } catch (err) {
const error = new Error( const error = new Error(
err?.name === 'AbortError' err?.name === 'AbortError'
? `Hotmail Graph request timed out after ${Math.round(timeoutMs / 1000)}s: ${mailbox}` ? `Hotmail 邮件请求超时(>${Math.round(timeoutMs / 1000)} 秒):${mailbox}`
: `Hotmail Graph request failed: ${err.message}` : `Hotmail 邮件请求失败:${err.message}`
); );
error.code = 'HOTMAIL_GRAPH_REQUEST_FAILED'; error.code = 'HOTMAIL_GRAPH_REQUEST_FAILED';
throw error; throw error;
@@ -622,7 +622,7 @@ async function requestHotmailGraphMessages(account, mailbox = 'INBOX') {
if (!response.ok) { if (!response.ok) {
const errorText = payload?.error?.message || payload?.error_description || payload?.message || text || `HTTP ${response.status}`; 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 error.code = response.status === 401 || response.status === 403
? 'HOTMAIL_GRAPH_AUTH_FAILED' ? 'HOTMAIL_GRAPH_AUTH_FAILED'
: 'HOTMAIL_GRAPH_REQUEST_FAILED'; : 'HOTMAIL_GRAPH_REQUEST_FAILED';
@@ -755,9 +755,9 @@ async function pollHotmailVerificationCode(step, state, pollPayload = {}) {
}) })
.slice(0, 3) .slice(0, 3)
.map((message) => { .map((message) => {
const receivedAt = message?.receivedDateTime || 'unknown-time'; const receivedAt = message?.receivedDateTime || '未知时间';
const sender = message?.from?.emailAddress?.address || 'unknown'; const sender = message?.from?.emailAddress?.address || '未知发件人';
const subject = message?.subject || '(no subject)'; const subject = message?.subject || '(无主题)';
const preview = String(message?.bodyPreview || '').replace(/\s+/g, ' ').trim().slice(0, 80); const preview = String(message?.bodyPreview || '').replace(/\s+/g, ' ').trim().slice(0, 80);
return `[${message.mailbox || 'INBOX'}] ${receivedAt} | ${sender} | ${subject} | ${preview}`; return `[${message.mailbox || 'INBOX'}] ${receivedAt} | ${sender} | ${subject} | ${preview}`;
}) })
@@ -1367,7 +1367,7 @@ function getSourceLabel(source) {
'mail-163': '163 邮箱', 'mail-163': '163 邮箱',
'inbucket-mail': 'Inbucket 邮箱', 'inbucket-mail': 'Inbucket 邮箱',
'duck-mail': 'Duck 邮箱', 'duck-mail': 'Duck 邮箱',
'hotmail-api': 'Hotmail API', 'hotmail-api': 'Hotmail(微软 Graph',
}; };
return labels[source] || source || '未知来源'; return labels[source] || source || '未知来源';
} }
@@ -2004,7 +2004,7 @@ function waitForStepComplete(step, timeoutMs = 120000) {
throwIfStopped(); throwIfStopped();
const timer = setTimeout(() => { const timer = setTimeout(() => {
stepWaiters.delete(step); stepWaiters.delete(step);
reject(new Error(`Step ${step} timed out after ${timeoutMs / 1000}s`)); reject(new Error(`步骤 ${step} 等待超时(>${timeoutMs / 1000} 秒)`));
}, timeoutMs); }, timeoutMs);
stepWaiters.set(step, { stepWaiters.set(step, {
@@ -2705,7 +2705,7 @@ async function executeStep3(state) {
function getMailConfig(state) { function getMailConfig(state) {
const provider = state.mailProvider || 'qq'; const provider = state.mailProvider || 'qq';
if (provider === HOTMAIL_PROVIDER) { if (provider === HOTMAIL_PROVIDER) {
return { provider: HOTMAIL_PROVIDER, label: 'Hotmail API' }; return { provider: HOTMAIL_PROVIDER, label: 'Hotmail(微软 Graph' };
} }
if (provider === '163') { 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 邮箱' }; 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 邮箱' };
+6 -6
View File
@@ -58,7 +58,7 @@
<div class="data-row"> <div class="data-row">
<span class="data-label">邮箱服务</span> <span class="data-label">邮箱服务</span>
<select id="select-mail-provider" class="data-select"> <select id="select-mail-provider" class="data-select">
<option value="hotmail-api">Hotmail Graphrefresh_token</option> <option value="hotmail-api">Hotmail(微软 Graph</option>
<option value="163">163 邮箱 (mail.163.com)</option> <option value="163">163 邮箱 (mail.163.com)</option>
<option value="163-vip">163 VIP 邮箱 (webmail.vip.163.com)</option> <option value="163-vip">163 VIP 邮箱 (webmail.vip.163.com)</option>
<option value="qq">QQ 邮箱 (wx.mail.qq.com)</option> <option value="qq">QQ 邮箱 (wx.mail.qq.com)</option>
@@ -123,16 +123,16 @@
<input type="text" id="input-hotmail-email" class="data-input" placeholder="name@hotmail.com" /> <input type="text" id="input-hotmail-email" class="data-input" placeholder="name@hotmail.com" />
</div> </div>
<div class="data-row"> <div class="data-row">
<span class="data-label">Client ID</span> <span class="data-label">客户端 ID</span>
<input type="text" id="input-hotmail-client-id" class="data-input mono" placeholder="Microsoft app client id" /> <input type="text" id="input-hotmail-client-id" class="data-input mono" placeholder="微软应用客户端 ID" />
</div> </div>
<div class="data-row"> <div class="data-row">
<span class="data-label">邮箱密码</span> <span class="data-label">邮箱密码</span>
<input type="password" id="input-hotmail-password" class="data-input" placeholder="可选,仅用于记录" /> <input type="password" id="input-hotmail-password" class="data-input" placeholder="可选,仅用于记录" />
</div> </div>
<div class="data-row"> <div class="data-row">
<span class="data-label">Refresh</span> <span class="data-label">刷新令牌</span>
<input type="password" id="input-hotmail-refresh-token" class="data-input mono" placeholder="必填,粘贴 refresh_token" /> <input type="password" id="input-hotmail-refresh-token" class="data-input mono" placeholder="必填,粘贴刷新令牌(refresh token" />
</div> </div>
<div class="data-row hotmail-actions-row"> <div class="data-row hotmail-actions-row">
<span class="data-label"></span> <span class="data-label"></span>
@@ -141,7 +141,7 @@
<div class="data-row hotmail-import-row"> <div class="data-row hotmail-import-row">
<span class="data-label">批量导入</span> <span class="data-label">批量导入</span>
<div class="hotmail-import-box"> <div class="hotmail-import-box">
<textarea id="input-hotmail-import" class="data-textarea mono" placeholder="账号----密码----ID----Token&#10;name@hotmail.com----password----client-id----refresh-token"></textarea> <textarea id="input-hotmail-import" class="data-textarea mono" placeholder="账号----密码----客户端ID----刷新令牌&#10;name@hotmail.com----password----client-id----refresh-token"></textarea>
<button id="btn-import-hotmail-accounts" class="btn btn-outline btn-sm" type="button">导入</button> <button id="btn-import-hotmail-accounts" class="btn btn-outline btn-sm" type="button">导入</button>
</div> </div>
</div> </div>
+4 -4
View File
@@ -698,8 +698,8 @@ function renderHotmailAccounts() {
<span class="hotmail-status-chip ${escapeHtml(getHotmailStatusClass(account))}">${escapeHtml(getHotmailStatusLabel(account))}</span> <span class="hotmail-status-chip ${escapeHtml(getHotmailStatusClass(account))}">${escapeHtml(getHotmailStatusLabel(account))}</span>
</div> </div>
<div class="hotmail-account-meta"> <div class="hotmail-account-meta">
<span>Client ID: ${escapeHtml(account.clientId ? `${account.clientId.slice(0, 10)}...` : '未填写')}</span> <span>客户端 ID${escapeHtml(account.clientId ? `${account.clientId.slice(0, 10)}...` : '未填写')}</span>
<span>Refresh: ${account.refreshToken ? '已保存' : '未保存'}</span> <span>刷新令牌:${account.refreshToken ? '已保存' : '未保存'}</span>
<span>分配状态: ${escapeHtml(getHotmailAvailabilityLabel(account))}</span> <span>分配状态: ${escapeHtml(getHotmailAvailabilityLabel(account))}</span>
<span>上次校验: ${escapeHtml(formatDateTime(account.lastAuthAt))}</span> <span>上次校验: ${escapeHtml(formatDateTime(account.lastAuthAt))}</span>
<span>上次使用: ${escapeHtml(formatDateTime(account.lastUsedAt))}</span> <span>上次使用: ${escapeHtml(formatDateTime(account.lastUsedAt))}</span>
@@ -1172,11 +1172,11 @@ btnAddHotmailAccount?.addEventListener('click', async () => {
return; return;
} }
if (!clientId) { if (!clientId) {
showToast('请先填写 Microsoft client ID。', 'warn'); showToast('请先填写微软应用客户端 ID。', 'warn');
return; return;
} }
if (!refreshToken) { if (!refreshToken) {
showToast('请先填写 refresh token。', 'warn'); showToast('请先填写刷新令牌(refresh token。', 'warn');
return; return;
} }