第4课:IMAP 邮件系统 —— 怎么连邮箱、收邮件
本课目标
搞懂"程序是怎么读你邮箱里的邮件的"。这是整个系统的起点——没有邮件数据,后面的一切都是空谈。
IMAP 是什么?
IMAP(Internet Mail Access Protocol) = 互联网邮件访问协议。
简单说:它是一套规则,让程序能远程访问邮箱服务器上的邮件,而不需要把邮件下载到本地。
graph TD
A["你的 Gmail 邮箱服务器"]
A -->|"IMAP 协议(加密连接,端口993)"| B["你的程序(email-agent)"]
B --> C["给我最新的50封邮件"]
B --> D["搜索包含 invoice 的邮件"]
B --> E["把这封邮件标为已读"]
你每天用 Gmail 网页看邮件,用的也是类似的机制——只不过 Google 用的是自己的 API,而我们用标准的 IMAP。
邮件系统的两个角色
IMAP Manager:连接管理员
位置:database/imap-manager.ts
它负责建立和维护与 Gmail 的连接。就像你家的网线管理员——确保网络通畅。
// 简化版核心逻辑
class ImapManager {
private connection; // IMAP 连接对象
async connect() {
// 用你的邮箱和应用密码连接 Gmail
this.connection = new Imap({
host: 'imap.gmail.com',
port: 993,
tls: true, // 加密连接
user: 'you@gmail.com',
password: 'your-app-password',
});
}
async fetchEmails(count: number) {
// 从服务器拉取最新的 N 封邮件
// 返回原始邮件数据(包含 headers、body、attachments)
}
async startIDLE() {
// 开启"待命"模式
// Gmail 有新邮件时,服务器会主动通知我们
// 而不是我们隔几秒去问一次"有新邮件吗?"
}
}
IDLE 模式很重要——它让你的程序能实时收到新邮件通知,而不是傻傻地每隔几秒轮询一次。
Email Sync:邮件搬运工
位置:database/email-sync.ts
它负责把从 IMAP 拉到的原始邮件解析、整理、存到数据库。
graph TD
A["Gmail 服务器"]
A -->|"IMAP Manager 拉取原始数据"| B["原始邮件数据(MIME 格式,一堆乱码般的文本)"]
B -->|"mailparser 解析"| C["结构化邮件对象<br>from, to, subject, date,<br>body_text, body_html, attachments"]
C -->|"存入 SQLite"| D["数据库里整整齐齐的邮件记录"]
同步流程详解
当你启动 bun run dev 时,邮件同步是这样发生的:
第一步:初始同步
程序启动 → 连接 Gmail → 拉取最近 N 封邮件 → 全部存入数据库
第二步:实时监听
开启 IDLE 模式 → 等待 Gmail 推送新邮件通知
第三步:新邮件到达
Gmail 推送通知 → 拉取新邮件 → 解析 → 存入数据库 → 触发 Listeners
↑
这一步是"自动化"的关键!
触发 Listeners 的关键代码
// email-sync.ts(简化版)
async function syncNewEmails() {
const rawEmails = await imapManager.fetchNewEmails();
for (const raw of rawEmails) {
// 解析邮件
const email = await parseEmail(raw);
// 存入数据库
await database.insertEmail(email);
// 关键:通知所有监听器 "有新邮件来了!"
await listenersManager.checkEvent('email_received', email);
// ↑ 事件类型 ↑ 邮件数据
}
}
就是这个 checkEvent 调用,把"收到新邮件"和"自动处理"连接起来了。
邮件里都有什么?
一封邮件被解析后,变成这样的数据:
interface Email {
messageId: string; // 唯一标识
threadId: string; // 对话线程 ID
from: string; // 发件人
to: string[]; // 收件人
cc: string[]; // 抄送
subject: string; // 主题
date_sent: Date; // 发送时间
body_text: string; // 纯文本内容
body_html: string; // HTML 内容
snippet: string; // 摘要(前100字)
is_read: boolean; // 是否已读
is_starred: boolean; // 是否标星
labels: string[]; // 标签(如 "Finance", "Urgent")
has_attachments: boolean; // 是否有附件
attachments: Attachment[]; // 附件列表
folder: string; // 所在文件夹(INBOX, Sent 等)
}
这些字段后面都会用到——搜索用 subject 和 body_text,Listener 用 from 和 labels 判断分类,看板用 date 和各种自定义字段。
IMAP 的基本操作
除了收邮件,IMAP 还支持这些操作(都在 demo 里有用到):
| 操作 | 说明 | 谁会用 |
|---|---|---|
| FETCH | 拉取邮件内容 | 初始同步、新邮件到达时 |
| SEARCH | 在服务器端搜索 | AI 的搜索子Agent |
| STORE | 修改邮件标记 | 标记已读/未读、加星标 |
| COPY | 复制邮件到其他文件夹 | 归档操作 |
| IDLE | 实时等待新邮件 | 后台监听 |
本课小结
- IMAP 是程序和邮箱服务器之间的"通信协议"
- IMAP Manager 负责建立连接、拉取原始数据
- Email Sync 负责解析邮件、存入数据库、触发 Listeners
- IDLE 模式 让系统能实时响应新邮件
checkEvent('email_received', email)是连接"收邮件"和"自动化"的桥梁