6.1 KiB
多注册流程邮件分层设计
本文专门解决“邮件轮询虽然看起来像公共能力,但实际 OpenAI 规则已经写进 provider 脚本和后台轮询里”这个问题。
1. 先说结论
邮件相关能力必须拆成两层,不能再只写一句“mail polling 通用化”:
mail provider driverflow 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.jscontent/mail-163.jscontent/mail-2925.jscontent/inbucket-mail.js
例如:
openaichatgptverificationlog-in codestrictChatGPTCodeOnly
这意味着现在不是“通用 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 / 会话可用
- 列邮件、开邮件、取正文、删邮件
- 会话级去重
- 输出统一格式的消息对象
建议输出结构:
{
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 定义:
- 要找的邮件类型
- 过滤条件
- 选择规则
- 提取规则
- 命中后的消费动作
建议接口:
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-linkafterConsume负责删除邮件、记录 cursor、保存已试 code
4. 建议的共享 mail service 接口
mailService.poll({
providerId: 'gmail',
rule: flow.mailRules.signupCode,
state,
maxAttempts: 5,
intervalMs: 3000,
});
内部流程:
- provider driver 按 query 拉取候选消息
- rule 决定哪封命中
- rule 提取 artifact
- service 返回统一结果
返回值示例:
{
ok: true,
artifact: {
type: 'code',
value: '123456',
},
message: { ...normalizedMessage },
}
5. OpenAI flow 当前应该拆出的规则
OpenAI 至少需要单独拆出这些规则文件:
flows/openai/mail-rules/signup-code.jsflows/openai/mail-rules/login-code.jsflows/openai/mail-rules/add-email-code.js
它们负责承接现在散落在这些地方的规则:
verification-flow.js中的 sender / subject filtersgmail-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. 迁移顺序
- 先把
background/verification-flow.js里的查询构造迁到flows/openai/mail-rules/* - 再把各 provider 脚本里的 OpenAI / ChatGPT 正则搬出到 rule 层
- 保留 provider 里的“消息抓取 / 页面操作 / 删除策略”
- 等第二个 flow 接入时,只新增它自己的
mail-rules,不改 Gmail / 163 / 2925 driver
9. 本文对应解决的缺口
本文主要补齐以下缺口:
- “Mail polling 通用化”表述过于粗糙,没有说明 provider 与 rule 两层
- provider 内容脚本里已经嵌入 OpenAI 规则
strictChatGPTCodeOnly这类开关的归属边界不清- LuckMail 当前其实还是 OpenAI flow 私有邮件能力,不是共享 provider