100 lines
4.3 KiB
Markdown
100 lines
4.3 KiB
Markdown
# Team 邀请小号流程分析报告
|
||
|
||
## 1. 邀请流程 (Phase-7)
|
||
|
||
完整流程在 `cmd/gptplus/main.go` 第 582-718 行:
|
||
|
||
```
|
||
Team 创建成功 → 获取 workspace token → 循环 invite_count 次:
|
||
1. 创建新 HTTP client (带独立代理 session)
|
||
2. 调用 auth.Register() 注册新账号 ← 消耗一个邮箱
|
||
3. 调用 chatgpt.InviteToTeam() 发送邀请 (POST /backend-api/accounts/{id}/invites)
|
||
4. 成员登录获取 token
|
||
5. 调用 auth.ObtainCodexTokens() 获取 Team 作用域 token (最多重试 3 次)
|
||
6. 保存成员 auth 文件
|
||
```
|
||
|
||
邀请 API: `POST /backend-api/accounts/{team_account_id}/invites`
|
||
- payload: `{"email_addresses": ["xxx@outlook.com"], "role": "standard-user", "resend_emails": true}`
|
||
- 需要 workspace-scoped access token
|
||
|
||
**注意:当前流程只发送邀请,没有"接受邀请"的步骤。** 成员注册后直接通过 `ObtainCodexTokens` 获取 Team token,假设邀请自动生效(因为是 admin 邀请已注册邮箱)。
|
||
|
||
---
|
||
|
||
## 2. 小号获取邮件的逻辑
|
||
|
||
### 当前行为
|
||
小号注册时调用的是**同一个 `emailProv`**(第 618 行):
|
||
```go
|
||
memberRegResult, memberRegErr := auth.Register(ctx, memberClient, emailProv, memberPassword)
|
||
```
|
||
|
||
`auth.Register()` 内部会调用 `emailProv.CreateMailbox()` 获取邮箱,然后调用 `emailProv.WaitForVerificationCode()` 获取 OTP。
|
||
|
||
### 有没有问题?
|
||
**之前有问题(已修复):** `WaitForVerificationCode` 使用的 `$filter` 查询被 Outlook REST API 拒绝(InefficientFilter),导致永远拿不到 OTP。现在已改为无 filter + 客户端过滤。
|
||
|
||
**潜在问题:** 无 filter 模式下拉取最近 10 封邮件,如果多个小号同时注册,收件箱里会有多封 OTP 邮件。由于有 `notBefore` 时间过滤 + `$orderby=ReceivedDateTime+desc`(最新优先),且代码取到第一个匹配的 OTP 就返回,所以**正常情况下不会混淆**。但如果两个小号在同一秒内收到 OTP,理论上可能取错(概率极低,因为是串行注册)。
|
||
|
||
---
|
||
|
||
## 3. 小号是否遵循邮箱设定(Outlook)?
|
||
|
||
**是的。** 小号使用的是同一个 `emailProv` 实例:
|
||
|
||
- 主账号和所有小号共享同一个 `OutlookProvider`
|
||
- 每次 `CreateMailbox()` 调用会返回 `accounts[nextIdx]` 并递增 `nextIdx`
|
||
- 所以小号注册时会**按顺序消耗 outmail.txt 中的 Outlook 账号**
|
||
|
||
如果配置是 `email.provider: "outlook"`,所有小号都会用 Outlook 邮箱。
|
||
|
||
---
|
||
|
||
## 4. 邮箱消耗顺序问题(核心问题)
|
||
|
||
### 场景
|
||
- outmail.txt 有 10 个邮箱 (索引 0-9)
|
||
- `invite_count: 4`
|
||
- 第一次运行:邮箱 #0 注册 Plus → Team 成功 → 邀请 4 个小号
|
||
|
||
### 当前行为
|
||
```
|
||
邮箱 #0 → 主账号注册 (CreateMailbox, nextIdx: 0→1)
|
||
邮箱 #1 → 小号1注册 (CreateMailbox, nextIdx: 1→2)
|
||
邮箱 #2 → 小号2注册 (CreateMailbox, nextIdx: 2→3)
|
||
邮箱 #3 → 小号3注册 (CreateMailbox, nextIdx: 3→4)
|
||
邮箱 #4 → 小号4注册 (CreateMailbox, nextIdx: 4→5)
|
||
```
|
||
|
||
**一次完整运行消耗 5 个邮箱(1 主 + 4 小号)。**
|
||
|
||
### 下一次运行会怎样?
|
||
|
||
**会从邮箱 #0 重新开始,不是 #5。**
|
||
|
||
原因:`OutlookProvider.nextIdx` 是内存变量,每次程序启动时初始化为 0。没有持久化记录哪些邮箱已经用过。
|
||
|
||
```go
|
||
// NewOutlookProvider 每次创建时 nextIdx = 0
|
||
return &OutlookProvider{
|
||
accounts: accounts,
|
||
}, nil
|
||
```
|
||
|
||
### 后果
|
||
- 第二次运行会尝试用邮箱 #0 注册,但 #0 已经注册过 → **注册失败**
|
||
- 如果 OpenAI 返回的是 "Failed to register username" 而不是 "email already exists",程序无法区分
|
||
- 需要手动从 outmail.txt 删除已用过的邮箱,或者实现已用邮箱的持久化追踪
|
||
|
||
---
|
||
|
||
## 5. 问题汇总与建议
|
||
|
||
| # | 问题 | 严重度 | 建议 |
|
||
|---|------|--------|------|
|
||
| 1 | **nextIdx 不持久化**:重启后从 #0 开始,已用邮箱会重复注册失败 | 🔴 高 | 用文件记录已用邮箱索引,或注册成功后从 outmail.txt 中移除 |
|
||
| 2 | **无接受邀请步骤**:依赖 ObtainCodexTokens 自动加入 | 🟡 中 | 目前看起来可行,但可能需要验证在某些情况下是否需要显式 accept |
|
||
| 3 | **OTP 串号风险**:多小号共用收件箱时理论上可能取错 OTP | 🟢 低 | 串行注册 + notBefore 过滤已足够,暂不需处理 |
|
||
| 4 | **Outlook filter 已修复** | ✅ | 已改为无 filter + 客户端过滤 |
|