# 多注册流程邮件分层设计

本文专门解决“邮件轮询虽然看起来像公共能力，但实际 OpenAI 规则已经写进 provider 脚本和后台轮询里”这个问题。

## 1. 先说结论

邮件相关能力必须拆成两层，不能再只写一句“mail polling 通用化”：

1. `mail provider driver`
2. `flow mail rules`

前者负责“怎么从 Gmail / 163 / 2925 / Inbucket / API 邮箱里拿到邮件”，后者负责“哪封邮件算当前 flow 需要的验证码 / magic link / 激活链接”。

## 2. 当前源码里的真实耦合

### 2.1 后台轮询 payload 已经带着 OpenAI 过滤条件

`background/verification-flow.js` 当前在 `getVerificationPollPayload()` 里直接写死：

- `senderFilters: ['openai', 'noreply', 'verify', 'auth', ...]`
- `subjectFilters: ['verify', 'verification', 'code', '验证码', 'confirm', ...]`

这不是 provider 通用逻辑，而是 OpenAI flow 规则。

### 2.2 内容脚本也已经写死 OpenAI / ChatGPT 正则

当前多个 provider 脚本里直接内嵌了 OpenAI / ChatGPT 识别：

- `content/gmail-mail.js`
- `content/mail-163.js`
- `content/mail-2925.js`
- `content/inbucket-mail.js`

例如：

- `openai`
- `chatgpt`
- `verification`
- `log-in code`
- `strictChatGPTCodeOnly`

这意味着现在不是“通用 provider + OpenAI 规则”，而是“OpenAI 规则渗透进 provider 实现本身”。

### 2.3 LuckMail 不能直接算进共享邮件层

LuckMail 当前还有一个额外边界：它不只是“邮箱提供商”，还绑定了 OpenAI 项目购邮与复用语义。

当前事实包括：

- `DEFAULT_LUCKMAIL_PROJECT_CODE = 'openai'`
- sidepanel 明确只展示 `openai` 项目的 LuckMail 邮箱

所以 LuckMail 当前应留在 OpenAI flow 内，不直接并入通用 `mailProviders`。

## 3. 目标分层

## 3.1 第一层：`mail provider driver`

这一层只负责 provider 访问与消息归一化，不理解具体网站业务。

职责包括：

- 打开 provider 页面或请求 provider API
- 等待页面 ready / 会话可用
- 列邮件、开邮件、取正文、删邮件
- 会话级去重
- 输出统一格式的消息对象

建议输出结构：

```js
{
  providerId: 'gmail',
  messageId: 'abc123',
  receivedAt: 1710000000000,
  sender: 'OpenAI <noreply@openai.com>',
  subject: 'Your code is 123456',
  previewText: 'Your code is 123456',
  normalizedText: 'your code is 123456',
  html: '<html>...</html>',
  links: ['https://...'],
  targetHints: ['user@example.com'],
  metadata: {},
}
```

这一层不要再写：

- “这是不是 OpenAI 邮件”
- “是不是 ChatGPT 登录码”
- “这个 code 是否必须 6 位”

## 3.2 第二层：`flow mail rules`

这一层由具体 flow 定义：

- 要找的邮件类型
- 过滤条件
- 选择规则
- 提取规则
- 命中后的消费动作

建议接口：

```js
flow.mailRules = {
  signupCode: {
    buildQuery(state) {},
    matchMessage(message, state) {},
    extractArtifact(message, state) {},
    afterConsume(message, provider, state) {},
  },
  loginCode: {
    buildQuery(state) {},
    matchMessage(message, state) {},
    extractArtifact(message, state) {},
  },
};
```

其中：

- `buildQuery` 负责时间窗、目标邮箱、允许的发件人线索
- `matchMessage` 负责判定这是不是当前 flow 的邮件
- `extractArtifact` 负责提取 `code / magic-link / activation-link`
- `afterConsume` 负责删除邮件、记录 cursor、保存已试 code

## 4. 建议的共享 mail service 接口

```js
mailService.poll({
  providerId: 'gmail',
  rule: flow.mailRules.signupCode,
  state,
  maxAttempts: 5,
  intervalMs: 3000,
});
```

内部流程：

1. provider driver 按 query 拉取候选消息
2. rule 决定哪封命中
3. rule 提取 artifact
4. service 返回统一结果

返回值示例：

```js
{
  ok: true,
  artifact: {
    type: 'code',
    value: '123456',
  },
  message: { ...normalizedMessage },
}
```

## 5. OpenAI flow 当前应该拆出的规则

OpenAI 至少需要单独拆出这些规则文件：

- `flows/openai/mail-rules/signup-code.js`
- `flows/openai/mail-rules/login-code.js`
- `flows/openai/mail-rules/add-email-code.js`

它们负责承接现在散落在这些地方的规则：

- `verification-flow.js` 中的 sender / subject filters
- `gmail-mail.js` / `mail-163.js` / `mail-2925.js` / `inbucket-mail.js` 中的 OpenAI / ChatGPT 正则
- `strictChatGPTCodeOnly`
- Step 4 与 Step 8 不同的时间窗和目标邮箱逻辑

## 6. provider driver 的拆分建议

### 6.1 可以进入共享层的 provider

这些 provider 的访问方式本身有共享价值，可以做成共享 mail driver：

- Gmail
- 163 / 126 / 163 VIP
- 2925
- Inbucket
- iCloud Mail
- Hotmail helper / Graph Mail
- Cloud Mail / SkyMail

### 6.2 暂时不进入共享层的 provider

- LuckMail：当前仍是 OpenAI 项目购邮与验证码轮询能力的一部分

如果未来第二个 flow 也明确复用 LuckMail，且复用的是“同一套 project / token / 生命周期”，再考虑把它抽出来。

## 7. `registrationEmailState` 与邮件层的关系

`registrationEmailState` 负责“当前流程邮箱身份”，不是“provider 拉信规则”。

所以后续边界应是：

- `registrationEmailState`：运行态身份记录
- `mail provider driver`：拉信能力
- `flow mail rules`：判定与提取

不要再在 `registrationEmailState` 或 provider driver 里夹带 OpenAI `add-email` 业务判断。

## 8. 迁移顺序

1. 先把 `background/verification-flow.js` 里的查询构造迁到 `flows/openai/mail-rules/*`
2. 再把各 provider 脚本里的 OpenAI / ChatGPT 正则搬出到 rule 层
3. 保留 provider 里的“消息抓取 / 页面操作 / 删除策略”
4. 等第二个 flow 接入时，只新增它自己的 `mail-rules`，不改 Gmail / 163 / 2925 driver

## 9. 本文对应解决的缺口

本文主要补齐以下缺口：

- “Mail polling 通用化”表述过于粗糙，没有说明 provider 与 rule 两层
- provider 内容脚本里已经嵌入 OpenAI 规则
- `strictChatGPTCodeOnly` 这类开关的归属边界不清
- LuckMail 当前其实还是 OpenAI flow 私有邮件能力，不是共享 provider

