223 lines
6.1 KiB
Markdown
223 lines
6.1 KiB
Markdown
# 多注册流程邮件分层设计
|
||
|
||
本文专门解决“邮件轮询虽然看起来像公共能力,但实际 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
|
||
|