# 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 + 客户端过滤 |