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

如何用Gmail API和C#实现邮件自动转发功能?

实现Gmail新邮件自动转发(C#)

核心修改点

  • 扩展API权限,新增GmailService.Scope.GmailReadonly和GmailModify,确保能读取邮件并修改状态
  • 添加未读邮件查询逻辑
  • 实现邮件转发的封装逻辑
  • 新增邮件状态标记(标记为已读)避免重复处理
  • 添加定时轮询机制

完整代码实现

using System;
using System.IO;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Gmail.v1;
using Google.Apis.Gmail.v1.Data;
using Google.Apis.Services;
using Google.Apis.Util.Store;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;

namespace GmailAutoForward
{
    internal class Program
    {
        // 扩展权限:需要读取、修改、发送邮件
        static string[] Scopes = { 
            GmailService.Scope.GmailSend, 
            GmailService.Scope.GmailReadonly, 
            GmailService.Scope.GmailModify 
        };
        static string ApplicationName = "Gmail Auto Forwarder";
        // 目标转发邮箱,替换为你的目标地址
        static string ForwardToEmail = "target-email@gmail.com";
        // 轮询间隔(毫秒,这里设为5分钟)
        static int PollingInterval = 300000;

        static async Task Main(string[] args)
        {
            // 初始化认证
            UserCredential credential;
            using (var stream = new FileStream("credentials.json", FileMode.Open, FileAccess.Read))
            {
                string credPath = "token_forward.json";
                credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
                    GoogleClientSecrets.FromStream(stream).Secrets,
                    Scopes,
                    "user",
                    CancellationToken.None,
                    new FileDataStore(credPath, true));
                Console.WriteLine("认证文件已保存至: " + credPath);
            }

            // 创建Gmail服务实例
            var service = new GmailService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,
                ApplicationName = ApplicationName,
            });

            Console.WriteLine("邮件自动转发服务已启动,按Ctrl+C停止...");
            while (true)
            {
                await ProcessUnreadEmails(service);
                await Task.Delay(PollingInterval);
            }
        }

        /// <summary>
        /// 处理所有未读邮件
        /// </summary>
        private static async Task ProcessUnreadEmails(GmailService service)
        {
            try
            {
                // 查询未读邮件(只获取ID和基本信息)
                var listRequest = service.Users.Messages.List("me");
                listRequest.Q = "is:unread";
                listRequest.MaxResults = 10; // 每次最多处理10封,可调整
                var messagesList = await listRequest.ExecuteAsync();

                if (messagesList.Messages == null || messagesList.Messages.Count == 0)
                {
                    Console.WriteLine($"{DateTime.Now}: 无未读邮件");
                    return;
                }

                Console.WriteLine($"{DateTime.Now}: 发现{messagesList.Messages.Count}封未读邮件,开始处理...");

                foreach (var msg in messagesList.Messages)
                {
                    // 获取完整邮件内容
                    var fullMsg = await service.Users.Messages.Get("me", msg.Id).ExecuteAsync();
                    // 转发邮件
                    await ForwardEmail(service, fullMsg);
                    // 标记为已读
                    await MarkEmailAsRead(service, msg.Id);
                    Console.WriteLine($"已转发并标记邮件ID {msg.Id} 为已读");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"处理邮件时出错: {ex.Message}");
            }
        }

        /// <summary>
        /// 转发指定邮件
        /// </summary>
        private static async Task ForwardEmail(GmailService service, Message originalMsg)
        {
            // 解析原邮件主题,添加"FWD: "前缀
            string originalSubject = GetHeaderValue(originalMsg.Payload.Headers, "Subject");
            string forwardSubject = string.IsNullOrEmpty(originalSubject) ? "转发邮件" : $"FWD: {originalSubject}";

            // 提取原邮件文本内容
            string originalBody = GetEmailBody(originalMsg);

            // 构造转发邮件的原始内容
            string rawContent = $"To: {ForwardToEmail}\r\n" +
                                $"Subject: {forwardSubject}\r\n" +
                                $"Content-Type: text/plain; charset=utf-8\r\n\r\n" +
                                $"--- 原始邮件内容 ---\n\n{originalBody}";

            // 编码并发送
            var forwardMsg = new Message
            {
                Raw = Base64UrlEncode(rawContent)
            };
            await service.Users.Messages.Send(forwardMsg, "me").ExecuteAsync();
        }

        /// <summary>
        /// 标记邮件为已读
        /// </summary>
        private static async Task MarkEmailAsRead(GmailService service, string messageId)
        {
            var modifyRequest = new ModifyMessageRequest
            {
                RemoveLabelIds = new[] { "UNREAD" }
            };
            await service.Users.Messages.Modify(modifyRequest, "me", messageId).ExecuteAsync();
        }

        /// <summary>
        /// 获取邮件头指定字段的值
        /// </summary>
        private static string GetHeaderValue(IList<MessagePartHeader> headers, string name)
        {
            var header = headers?.FirstOrDefault(h => string.Equals(h.Name, name, StringComparison.OrdinalIgnoreCase));
            return header?.Value;
        }

        /// <summary>
        /// 提取邮件文本内容(支持纯文本和多部分邮件)
        /// </summary>
        private static string GetEmailBody(Message msg)
        {
            // 处理纯文本邮件
            if (msg.Payload.MimeType == "text/plain")
            {
                return string.IsNullOrEmpty(msg.Payload.Body.Data) ? "" : Base64UrlDecode(msg.Payload.Body.Data);
            }

            // 处理多部分邮件,查找纯文本部分
            if (msg.Payload.Parts != null)
            {
                var plainTextPart = msg.Payload.Parts.FirstOrDefault(p => p.MimeType == "text/plain");
                if (plainTextPart != null && !string.IsNullOrEmpty(plainTextPart.Body.Data))
                {
                    return Base64UrlDecode(plainTextPart.Body.Data);
                }
            }

            return "无法提取邮件内容";
        }

        /// <summary>
        /// Base64URL编码
        /// </summary>
        private static string Base64UrlEncode(string input)
        {
            var inputBytes = System.Text.Encoding.UTF8.GetBytes(input);
            return Convert.ToBase64String(inputBytes)
                .Replace('+', '-')
                .Replace('/', '_')
                .Replace("=", "");
        }

        /// <summary>
        /// Base64URL解码
        /// </summary>
        private static string Base64UrlDecode(string input)
        {
            input = input.Replace('-', '+').Replace('_', '/');
            // 补全缺失的等号
            switch (input.Length % 4)
            {
                case 2: input += "=="; break;
                case 3: input += "="; break;
            }
            var outputBytes = Convert.FromBase64String(input);
            return System.Text.Encoding.UTF8.GetString(outputBytes);
        }
    }
}

关键说明

  • 权限调整:必须添加GmailReadonly和GmailModify权限,否则无法读取未读邮件和标记状态
  • 轮询机制:代码中设置为5分钟轮询一次,可根据需求修改PollingInterval的值
  • 邮件内容处理:支持纯文本和多类型邮件的文本内容提取,如需转发HTML内容,可修改GetEmailBody和转发内容的MIME类型
  • 重复处理避免:转发后立即标记邮件为已读,确保同一封邮件不会被重复转发
  • 错误处理:添加基础异常捕获,避免单个邮件处理失败导致整个服务停止

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 13:50:28