using InboxIntel.Application.Abstractions; using InboxIntel.Infrastructure.Configuration; using InboxIntel.Infrastructure.Persistence; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace InboxIntel.Infrastructure.Notifications; /// /// Hourly tick that sends the inbox digest to opted-in users once their /// configured frequency has elapsed. Mirrors . /// public class DigestWorker : BackgroundService { private readonly IServiceScopeFactory _scopeFactory; private readonly DigestOptions _options; private readonly ILogger _logger; public DigestWorker(IServiceScopeFactory scopeFactory, IOptions options, ILogger logger) { _scopeFactory = scopeFactory; _options = options.Value; _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.LogInformation("DigestWorker started; send hour = {Hour}:00 UTC, frequency = {Days}d", _options.SendHourUtc, _options.FrequencyDays); while (!stoppingToken.IsCancellationRequested) { try { if (DateTimeOffset.UtcNow.Hour == _options.SendHourUtc) await SendDueDigestsAsync(stoppingToken); } catch (Exception ex) { _logger.LogError(ex, "DigestWorker tick failed"); } await Task.Delay(TimeSpan.FromHours(1), stoppingToken); } } private async Task SendDueDigestsAsync(CancellationToken ct) { using var scope = _scopeFactory.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); var digest = scope.ServiceProvider.GetRequiredService(); var emailSender = scope.ServiceProvider.GetRequiredService(); if (!emailSender.IsEnabled) return; var cutoff = DateTimeOffset.UtcNow.AddDays(-_options.FrequencyDays); var dueUserIds = await db.Users .Where(u => u.DigestEnabled && (u.LastDigestSentUtc == null || u.LastDigestSentUtc <= cutoff)) .Select(u => u.Id) .ToListAsync(ct); foreach (var userId in dueUserIds) { try { await digest.SendDigestAsync(userId, ct); } catch (Exception ex) { _logger.LogError(ex, "Digest send failed for user {UserId}", userId); } } } }