feat: add TrustedSenderService to scan Sent folder for trusted contacts
This commit is contained in:
97
src/SpamGuard/Services/TrustedSenderService.cs
Normal file
97
src/SpamGuard/Services/TrustedSenderService.cs
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
// src/SpamGuard/Services/TrustedSenderService.cs
|
||||||
|
namespace SpamGuard.Services;
|
||||||
|
|
||||||
|
using MailKit;
|
||||||
|
using MailKit.Search;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using MimeKit;
|
||||||
|
using SpamGuard.Configuration;
|
||||||
|
using SpamGuard.State;
|
||||||
|
|
||||||
|
public sealed class TrustedSenderService : BackgroundService
|
||||||
|
{
|
||||||
|
private readonly ImapClientFactory _imapFactory;
|
||||||
|
private readonly TrustedSenderStore _store;
|
||||||
|
private readonly SpamGuardOptions _options;
|
||||||
|
private readonly ILogger<TrustedSenderService> _logger;
|
||||||
|
|
||||||
|
public TrustedSenderService(
|
||||||
|
ImapClientFactory imapFactory,
|
||||||
|
TrustedSenderStore store,
|
||||||
|
IOptions<SpamGuardOptions> options,
|
||||||
|
ILogger<TrustedSenderService> logger)
|
||||||
|
{
|
||||||
|
_imapFactory = imapFactory;
|
||||||
|
_store = store;
|
||||||
|
_options = options.Value;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("TrustedSenderService started");
|
||||||
|
|
||||||
|
while (!stoppingToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ScanSentFolderAsync(stoppingToken);
|
||||||
|
_store.Save();
|
||||||
|
_logger.LogInformation("Trusted sender scan complete. {Count} trusted senders", _store.Count);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error scanning sent folder");
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(TimeSpan.FromMinutes(_options.Monitoring.TrustedSenderRefreshMinutes), stoppingToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ScanSentFolderAsync(CancellationToken ct)
|
||||||
|
{
|
||||||
|
using var client = await _imapFactory.CreateConnectedClientAsync(ct);
|
||||||
|
|
||||||
|
var sentFolder = client.GetFolder(MailKit.SpecialFolder.Sent)
|
||||||
|
?? throw new InvalidOperationException("Could not find Sent folder");
|
||||||
|
|
||||||
|
await sentFolder.OpenAsync(FolderAccess.ReadOnly, ct);
|
||||||
|
|
||||||
|
var uids = await sentFolder.SearchAsync(SearchQuery.All, ct);
|
||||||
|
_logger.LogDebug("Found {Count} messages in Sent folder", uids.Count);
|
||||||
|
|
||||||
|
var addresses = new List<string>();
|
||||||
|
foreach (var uid in uids)
|
||||||
|
{
|
||||||
|
var headers = await sentFolder.GetHeadersAsync(uid, ct);
|
||||||
|
ExtractAddresses(headers, addresses);
|
||||||
|
}
|
||||||
|
|
||||||
|
_store.AddRange(addresses);
|
||||||
|
await client.DisconnectAsync(true, ct);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ExtractAddresses(HeaderList headers, List<string> addresses)
|
||||||
|
{
|
||||||
|
foreach (var headerName in new[] { "To", "Cc" })
|
||||||
|
{
|
||||||
|
var value = headers[headerName];
|
||||||
|
if (string.IsNullOrEmpty(value)) continue;
|
||||||
|
|
||||||
|
if (InternetAddressList.TryParse(value, out var list))
|
||||||
|
{
|
||||||
|
foreach (var address in list.Mailboxes)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(address.Address))
|
||||||
|
addresses.Add(address.Address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user