搞懂"程序是怎么读你邮箱里的邮件的"。这是整个系统的起点——没有邮件数据,后面的一切都是空谈。

第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 的连接。就像你家的网线管理员——确保网络通畅。

code
// 简化版核心逻辑
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 时,邮件同步是这样发生的:

code
第一步:初始同步
   程序启动 → 连接 Gmail → 拉取最近 N 封邮件 → 全部存入数据库

第二步:实时监听
   开启 IDLE 模式 → 等待 Gmail 推送新邮件通知

第三步:新邮件到达
   Gmail 推送通知 → 拉取新邮件 → 解析 → 存入数据库 → 触发 Listeners
                                                        ↑
                                            这一步是"自动化"的关键!

触发 Listeners 的关键代码

code
// 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 调用,把"收到新邮件"和"自动处理"连接起来了。


邮件里都有什么?

一封邮件被解析后,变成这样的数据:

code
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 实时等待新邮件 后台监听

本课小结

  1. IMAP 是程序和邮箱服务器之间的"通信协议"
  2. IMAP Manager 负责建立连接、拉取原始数据
  3. Email Sync 负责解析邮件、存入数据库、触发 Listeners
  4. IDLE 模式 让系统能实时响应新邮件
  5. checkEvent('email_received', email) 是连接"收邮件"和"自动化"的桥梁

沿着当前专题继续,或返回课程目录重新整理阅读顺序。

返回课程目录