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);
}
}