using MailKit; using MailKit.Net.Imap; using MailKit.Search; using MailKit.Security; using MimeKit; namespace Service.EmailServices; /// /// 邮件抓取服务接口 /// public interface IEmailFetchService { /// /// 连接状态 /// bool IsConnected { get; } /// /// 连接到邮件服务器 /// Task ConnectAsync(string host, int port, bool useSsl, string email, string password); /// /// 从收件箱获取未读邮件 /// Task> FetchUnreadMessagesAsync(); /// /// 获取所有邮件 /// Task> FetchAllMessagesAsync(); /// /// 断开与邮件服务器的连接 /// Task DisconnectAsync(); /// /// 标记邮件为已读 /// Task MarkAsReadAsync(UniqueId uid); /// /// 确保连接有效,如断开则自动重连 /// Task EnsureConnectedAsync(); } /// /// 邮件抓取服务实现 /// public class EmailFetchService(ILogger logger) : IEmailFetchService { private ImapClient? _imapClient; private string _host = string.Empty; private int _port; private bool _useSsl; private string _email = string.Empty; private string _password = string.Empty; private DateTime _lastKeepAlive = DateTime.MinValue; private const int KeepAliveIntervalSeconds = 300; // 5分钟发送一次KeepAlive private readonly ILogger _logger = logger; public bool IsConnected => _imapClient?.IsConnected == true; public async Task ConnectAsync(string host, int port, bool useSsl, string email, string password) { try { // 保存连接信息用于自动重连 _host = host; _port = port; _useSsl = useSsl; _email = email; _password = password; // 如果已连接,先断开 if (_imapClient?.IsConnected == true) { await DisconnectAsync(); } _imapClient = new ImapClient(); if (useSsl) { await _imapClient.ConnectAsync(host, port, SecureSocketOptions.SslOnConnect); } else { await _imapClient.ConnectAsync(host, port, SecureSocketOptions.StartTlsWhenAvailable); } await _imapClient.AuthenticateAsync(email, password); _logger.LogInformation("邮箱 {Email} 连接成功", email); _lastKeepAlive = DateTime.UtcNow; return true; } catch (Exception ex) { _logger.LogError(ex, "邮件连接失败 ({Email}): {Message}", email, ex.Message); return false; } } public async Task> FetchUnreadMessagesAsync() { var result = new List<(MimeMessage, UniqueId)>(); try { // 确保连接有效 if (!await EnsureConnectedAsync()) return result; var inbox = _imapClient?.Inbox; if (inbox == null) return result; await inbox.OpenAsync(FolderAccess.ReadWrite); // 查询未读邮件 var unreadUids = await inbox.SearchAsync(SearchQuery.NotSeen); foreach (var uid in unreadUids) { var message = await inbox.GetMessageAsync(uid); result.Add((message, uid)); } return result; } catch (Exception ex) { _logger.LogError(ex, "获取未读邮件失败: {Message}", ex.Message); return result; } } public async Task> FetchAllMessagesAsync() { var result = new List<(MimeMessage, UniqueId)>(); try { // 确保连接有效 if (!await EnsureConnectedAsync()) return result; var inbox = _imapClient?.Inbox; if (inbox == null) return result; await inbox.OpenAsync(FolderAccess.ReadWrite); var uids = await inbox.SearchAsync(SearchQuery.All); foreach (var uid in uids) { var message = await inbox.GetMessageAsync(uid); result.Add((message, uid)); } return result; } catch (Exception ex) { _logger.LogError(ex, "获取所有邮件失败: {Message}", ex.Message); return result; } } public async Task DisconnectAsync() { try { if (_imapClient?.IsConnected == true) { await _imapClient.DisconnectAsync(true); _logger.LogInformation("邮箱 {Email} 已断开连接", _email); } _imapClient?.Dispose(); _imapClient = null; } catch (Exception ex) { _logger.LogError(ex, "断开连接失败 ({Email}): {Message}", _email, ex.Message); } } public async Task MarkAsReadAsync(UniqueId uid) { try { if (!await EnsureConnectedAsync()) return; var inbox = _imapClient?.Inbox; if (inbox == null) return; // 打开收件箱以读写模式 await inbox.OpenAsync(FolderAccess.ReadWrite); // 标记邮件为已读(设置Seen标记) await inbox.AddFlagsAsync(uid, MessageFlags.Seen, silent: false); _logger.LogDebug("邮件 {Uid} 标记已读操作已提交", uid); } catch (Exception ex) { _logger.LogError(ex, "标记邮件为已读失败: {Message}", ex.Message); } } /// /// 确保连接有效,如果断开则自动重连 /// public async Task EnsureConnectedAsync() { if (_imapClient?.IsConnected == true) { // 定期发送NOOP保持连接活跃(防止超时断开) var timeSinceLastKeepAlive = (DateTime.UtcNow - _lastKeepAlive).TotalSeconds; if (timeSinceLastKeepAlive > KeepAliveIntervalSeconds) { try { await _imapClient.NoOpAsync(); _lastKeepAlive = DateTime.UtcNow; _logger.LogDebug("邮箱 {Email} KeepAlive 保活信号已发送", _email); } catch (Exception ex) { // NOOP失败,说明连接已断开,继续重连逻辑 _logger.LogWarning(ex, "KeepAlive 失败,连接已断开: {Message}", ex.Message); } } return _imapClient?.IsConnected == true; } if (string.IsNullOrEmpty(_host) || string.IsNullOrEmpty(_email)) { _logger.LogWarning("未初始化连接信息,无法自动重连"); return false; } _logger.LogInformation("检测到连接断开,尝试重新连接到 {Email}...", _email); return await ConnectAsync(_host, _port, _useSsl, _email, _password); } }