Files
maraphon-app/src/Marathon.Infrastructure/InfrastructureModule.cs
T
alexei.dolgolyov e307a54bec harden(notifications): per-item marker advance + Telegram client timeout
Review follow-ups: advance the dispatcher's "since" marker after each delivered
alert (not once per batch) so a future throwing sink can't re-deliver already-sent
alerts; give the Telegram HttpClient a 15s timeout so a hung connection can't stall
the dispatch loop.
2026-05-29 01:08:02 +03:00

75 lines
3.0 KiB
C#

using Marathon.Application.Abstractions;
using Marathon.Application.Configuration;
using Marathon.Infrastructure.Configuration;
using Marathon.Infrastructure.Notifications;
using Marathon.Infrastructure.Persistence;
using Marathon.Infrastructure.Scraping;
using Marathon.Infrastructure.Workers;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Marathon.Infrastructure;
/// <summary>
/// Top-level DI composition entry-point for all Infrastructure sub-modules
/// (Persistence, Scraping, Workers).
/// </summary>
/// <remarks>
/// Call <see cref="AddMarathonInfrastructure"/> once from the host's DI
/// setup. This replaces the previous reflection-based wiring in
/// <c>App.xaml.cs::TryAddApplicationAndInfrastructure</c>.
/// </remarks>
public static class InfrastructureModule
{
/// <summary>
/// Registers the complete Infrastructure layer:
/// <list type="bullet">
/// <item>EF Core / SQLite persistence (<see cref="PersistenceModule.AddMarathonPersistence"/>).</item>
/// <item>HttpClient + AngleSharp + Polly scraping (<see cref="ScrapingModule.AddMarathonScraping"/>).</item>
/// <item><see cref="WorkerOptions"/> bound to the <c>Workers</c> config section.</item>
/// <item>Three <see cref="Microsoft.Extensions.Hosting.BackgroundService"/> pollers.</item>
/// </list>
/// </summary>
public static IServiceCollection AddMarathonInfrastructure(
this IServiceCollection services,
IConfiguration config)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(config);
services.AddMarathonPersistence(config);
services.AddMarathonScraping(config);
services
.AddOptions<WorkerOptions>()
.Bind(config.GetSection(WorkerOptions.SectionName));
services
.AddOptions<AnomalyOptions>()
.Bind(config.GetSection(AnomalyOptions.SectionName));
services
.AddOptions<ScrapingThrottle>()
.Bind(config.GetSection(ScrapingThrottle.SectionName));
services
.AddOptions<NotificationOptions>()
.Bind(config.GetSection(NotificationOptions.SectionName));
services.AddHostedService<UpcomingEventsPoller>();
services.AddHostedService<LiveOddsPoller>();
services.AddHostedService<ResultsWatchListPoller>();
services.AddHostedService<AnomalyDetectionPoller>();
// Outbound anomaly notifications (Telegram). Sink + dispatcher are always
// registered; the dispatcher idles until Notifications:Enabled is true and
// the sink no-ops until a bot token + chat id are configured.
services.AddHttpClient(TelegramNotificationSink.HttpClientName, client =>
client.Timeout = TimeSpan.FromSeconds(15));
services.AddSingleton<INotificationSink, TelegramNotificationSink>();
services.AddHostedService<AnomalyNotificationDispatcher>();
return services;
}
}