You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Node.js开发Firebase云函数实现Gmail邮件监听及业务联动需求

基于Node.js Firebase云函数实现Gmail收件箱监听与Firestore联动方案

一、依赖安装与初始化

首先在Firebase云函数目录下安装所需依赖:

npm install firebase-functions firebase-admin imap mailparser nodemailer

初始化Firebase Admin SDK,用于操作Firestore和FCM:

const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();

二、实现Gmail收件箱定时轮询(替代长连接)

由于云函数是无状态的,无法保持长期IMAP连接,因此采用定时轮询未读邮件的方案更可靠。以下是核心代码:

const Imap = require('imap');
const { simpleParser } = require('mailparser');

// Gmail IMAP配置
const imapConfig = {
  user: 'your-gmail@example.com',
  password: 'your-app-specific-password', // 开启2FA时用应用专用密码,否则用账户密码
  host: 'imap.gmail.com',
  port: 993,
  tls: true,
  tlsOptions: { rejectUnauthorized: false }
};

// 定时检查新邮件的云函数
exports.checkNewEmails = functions.runWith({ 
  timeoutSeconds: 540, 
  memory: '2GB' 
}).pubsub.schedule('every 1 minutes').onRun(async () => {
  await fetchAndProcessUnseenEmails();
  return null;
});

// 拉取并处理未读邮件
async function fetchAndProcessUnseenEmails() {
  const imap = new Imap(imapConfig);
  return new Promise((resolve, reject) => {
    imap.on('ready', () => {
      imap.openBox('INBOX', false, async (err) => {
        if (err) {
          imap.end();
          return reject(err);
        }
        // 搜索未读邮件
        imap.search(['UNSEEN'], async (err, results) => {
          if (err) {
            imap.end();
            return reject(err);
          }
          if (results.length === 0) {
            imap.end();
            return resolve();
          }
          // 批量获取邮件内容
          const fetch = imap.fetch(results, { bodies: '' });
          fetch.on('message', (msg, seqno) => {
            msg.on('body', (stream) => {
              simpleParser(stream, async (err, parsedEmail) => {
                if (err) console.error('解析邮件失败:', err);
                else await processEmail(parsedEmail);
                // 标记邮件为已读
                imap.addFlags(seqno, ['\\Seen'], (err) => {
                  if (err) console.error('标记已读失败:', err);
                });
              });
            });
          });
          fetch.on('end', () => {
            imap.end();
            resolve();
          });
        });
      });
    });
    imap.on('error', (err) => {
      imap.end();
      reject(err);
    });
    imap.connect();
  });
}

三、邮件主题解析与关联逻辑

核心是从邮件主题提取订单ID,或匹配现有工单,然后创建关联的Firestore文档:

async function processEmail(parsedEmail) {
  const subject = parsedEmail.subject;
  let orderId = null;
  let ticketId = null;

  // 1. 提取订单ID(根据实际主题格式调整正则)
  const orderMatch = subject.match(/订单 #(\d{5,})/i); // 示例:匹配"订单 #12345"格式
  if (orderMatch) {
    orderId = orderMatch[1];
    // 验证订单是否存在于Firestore
    const orderDoc = await db.collection('orders').doc(orderId).get();
    if (!orderDoc.exists) orderId = null;
  }

  // 2. 无有效订单ID时,匹配现有工单
  if (!orderId) {
    // 精确匹配工单主题(可根据需求改为模糊匹配)
    const ticketsSnapshot = await db.collection('tickets')
      .where('subject', '==', subject)
      .limit(1)
      .get();
    
    if (!ticketsSnapshot.empty) {
      ticketId = ticketsSnapshot.docs[0].id;
    } else {
      // 无匹配工单时,自动创建新工单
      const newTicket = await db.collection('tickets').add({
        subject: subject,
        sender: parsedEmail.from.text,
        content: parsedEmail.text || parsedEmail.html,
        createdAt: admin.firestore.FieldValue.serverTimestamp()
      });
      ticketId = newTicket.id;
    }
  }

  // 3. 创建关联的邮件记录文档
  await db.collection('email_records').add({
    subject: subject,
    sender: parsedEmail.from.text,
    content: parsedEmail.text || parsedEmail.html,
    createdAt: admin.firestore.FieldValue.serverTimestamp(),
    orderId: orderId || null,
    ticketId: ticketId || null
  });

  // 4. 发送通知
  await sendNotifications(orderId, ticketId, parsedEmail);
}

四、发送FCM与邮件通知

实现FCM推送和邮件通知功能:

const nodemailer = require('nodemailer');

// 配置邮件发送器
const transporter = nodemailer.createTransport({
  service: 'gmail',
  auth: {
    user: 'your-gmail@example.com',
    pass: 'your-app-specific-password'
  }
});

async function sendNotifications(orderId, ticketId, parsedEmail) {
  // 1. 发送FCM通知
  const fcmPayload = {
    notification: {
      title: '新邮件提醒',
      body: `收到与${orderId ? `订单#${orderId}` : `工单#${ticketId}`}相关的邮件`
    },
    topic: 'email_alerts' // 可改为按订单/工单订阅的token发送
  };

  try {
    await admin.messaging().send(fcmPayload);
    console.log('FCM通知发送成功');
  } catch (err) {
    console.error('FCM通知发送失败:', err);
  }

  // 2. 发送邮件通知(示例:发给客服)
  const mailOptions = {
    from: 'your-gmail@example.com',
    to: 'support@yourdomain.com',
    subject: `新邮件关联${orderId ? `订单#${orderId}` : `工单#${ticketId}`}`,
    text: `发件人:${parsedEmail.from.text}\n主题:${parsedEmail.subject}\n\n邮件内容:\n${parsedEmail.text}`
  };

  try {
    await transporter.sendMail(mailOptions);
    console.log('邮件通知发送成功');
  } catch (err) {
    console.error('邮件通知发送失败:', err);
  }
}

五、部署与关键注意事项

  1. Gmail设置:登录Gmail账户,开启IMAP(设置→转发和POP/IMAP→启用IMAP);若开启两步验证,需在Google账户安全设置中生成应用专用密码替代普通密码。
  2. 云函数权限:确保云函数拥有Firestore读写权限及FCM发送权限,可通过Firebase控制台的IAM配置调整。
  3. 错误处理:建议在代码中添加更多try/catch块,避免单封邮件处理失败导致整个函数崩溃。
  4. 性能优化:若邮件量较大,可增加云函数内存配置(如升级到4GB),调整轮询频率(如每5分钟一次)。
  5. 部署命令:执行firebase deploy --only functions部署云函数。

内容的提问来源于stack exchange,提问作者Mohamad Eizeddin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 08:05:27