7a2965b43f
PR #106 extracted DeleteSessionHandler and ListSessionsHandler to GmRelay.Shared, but forgot to register the shared implementations in Program.cs. This caused an InvalidOperationException at startup on Native AOT builds because the Bot wrappers could not resolve their shared dependencies. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
115 lines
6.8 KiB
C#
115 lines
6.8 KiB
C#
using GmRelay.Bot.Features.Notifications;
|
|
using GmRelay.Bot.Features.Sessions.CreateSession;
|
|
using GmRelay.Bot.Features.Sessions.RescheduleSession;
|
|
using GmRelay.Bot.Infrastructure.Database;
|
|
using GmRelay.Shared.Features.Sessions.RescheduleSession;
|
|
using GmRelay.Bot.Infrastructure.Health;
|
|
using GmRelay.Bot.Infrastructure.Logging;
|
|
using GmRelay.Bot.Infrastructure.Telegram;
|
|
using GmRelay.Shared.Features.Confirmation.HandleRsvp;
|
|
using GmRelay.Shared.Features.Confirmation.SendConfirmation;
|
|
using GmRelay.Shared.Features.Notifications;
|
|
using GmRelay.Shared.Features.Reminders.SendJoinLink;
|
|
using GmRelay.Shared.Features.Reminders.SendOneHourReminder;
|
|
using GmRelay.Shared.Features.Sessions.CreateSession;
|
|
using GmRelay.Shared.Infrastructure.Scheduling;
|
|
using GmRelay.Shared.Platform;
|
|
using Npgsql;
|
|
using Telegram.Bot;
|
|
|
|
[module: Dapper.DapperAot]
|
|
|
|
var builder = Host.CreateApplicationBuilder(args);
|
|
|
|
// ── Aspire service defaults (OpenTelemetry, health checks) ───────────
|
|
builder.AddServiceDefaults();
|
|
|
|
// ── PostgreSQL (ручная регистрация — AOT safe, без Aspire-магии) ─────
|
|
builder.Services.AddSingleton<NpgsqlDataSource>(sp =>
|
|
{
|
|
var config = sp.GetRequiredService<IConfiguration>();
|
|
var loggerFactory = sp.GetRequiredService<ILoggerFactory>();
|
|
var connectionString = config.GetConnectionString("gmrelaydb")
|
|
?? throw new InvalidOperationException(
|
|
"ConnectionStrings:gmrelaydb is required. Set via environment variable ConnectionStrings__gmrelaydb.");
|
|
|
|
var logger = loggerFactory.CreateLogger("GmRelay.Bot.Startup");
|
|
logger.LogInformation(
|
|
"Configured PostgreSQL data source with connection string {ConnectionString}",
|
|
SecretRedactor.RedactConnectionString(connectionString));
|
|
|
|
return NpgsqlDataSource.Create(connectionString);
|
|
});
|
|
|
|
// ── Database migrations ──────────────────────────────────────────────
|
|
builder.Services.AddSingleton<DbMigrator>();
|
|
|
|
// ── Telegram Bot Client ──────────────────────────────────────────────
|
|
builder.Services.AddSingleton<ITelegramBotClient>(sp =>
|
|
{
|
|
var config = sp.GetRequiredService<IConfiguration>();
|
|
var token = config["Telegram:BotToken"]
|
|
?? throw new InvalidOperationException(
|
|
"Telegram:BotToken is required. Set via environment variable Telegram__BotToken or appsettings.json.");
|
|
return new TelegramBotClient(token);
|
|
});
|
|
builder.Services.AddSingleton<ITelegramUpdateSource, TelegramUpdateSource>();
|
|
builder.Services.AddSingleton<IPlatformMessenger, TelegramPlatformMessenger>();
|
|
builder.Services.AddSingleton(new PlatformSchedulerOptions(PlatformKind.Telegram));
|
|
|
|
// ── Feature handlers (explicit registration — AOT safe) ──────────────
|
|
builder.Services.AddSingleton<SendConfirmationHandler>();
|
|
builder.Services.AddSingleton<ISendConfirmationHandler>(sp => sp.GetRequiredService<SendConfirmationHandler>());
|
|
builder.Services.AddSingleton<PlatformDirectNotificationSender>();
|
|
builder.Services.AddSingleton<HandleRsvpHandler>();
|
|
builder.Services.AddSingleton<SendJoinLinkHandler>();
|
|
builder.Services.AddSingleton<ISendJoinLinkHandler>(sp => sp.GetRequiredService<SendJoinLinkHandler>());
|
|
builder.Services.AddSingleton<SendOneHourReminderHandler>();
|
|
builder.Services.AddSingleton<ISendOneHourReminderHandler>(sp => sp.GetRequiredService<SendOneHourReminderHandler>());
|
|
builder.Services.AddSingleton<GmRelay.Shared.Features.Sessions.CreateSession.CreateSessionHandler>();
|
|
builder.Services.AddSingleton<GmRelay.Bot.Features.Sessions.CreateSession.CreateSessionHandler>();
|
|
builder.Services.AddSingleton<IScheduleMessageUpdateLock, ScheduleMessageUpdateLock>();
|
|
builder.Services.AddSingleton<JoinSessionHandler>();
|
|
builder.Services.AddSingleton<LeaveSessionHandler>();
|
|
builder.Services.AddSingleton<PromoteWaitlistedPlayerHandler>();
|
|
builder.Services.AddSingleton<CancelSessionHandler>();
|
|
builder.Services.AddSingleton<GmRelay.Shared.Features.Sessions.ListSessions.DeleteSessionHandler>();
|
|
builder.Services.AddSingleton<GmRelay.Bot.Features.Sessions.ListSessions.DeleteSessionHandler>();
|
|
builder.Services.AddSingleton<GmRelay.Shared.Features.Sessions.ListSessions.ListSessionsHandler>();
|
|
builder.Services.AddSingleton<GmRelay.Bot.Features.Sessions.ListSessions.ListSessionsHandler>();
|
|
builder.Services.AddSingleton<GmRelay.Shared.Features.Sessions.ExportCalendar.ExportCalendarHandler>();
|
|
builder.Services.AddSingleton<GmRelay.Bot.Features.Sessions.ExportCalendar.ExportCalendarHandler>();
|
|
builder.Services.AddSingleton<InitiateRescheduleHandler>();
|
|
builder.Services.AddSingleton<GmRelay.Shared.Features.Sessions.RescheduleSession.HandleRescheduleTimeInputHandler>();
|
|
builder.Services.AddSingleton<GmRelay.Bot.Features.Sessions.RescheduleSession.HandleRescheduleTimeInputHandler>();
|
|
builder.Services.AddSingleton<GmRelay.Shared.Features.Sessions.RescheduleSession.HandleRescheduleVoteHandler>();
|
|
builder.Services.AddSingleton<GmRelay.Bot.Features.Sessions.RescheduleSession.HandleRescheduleVoteHandler>();
|
|
builder.Services.AddSingleton<RescheduleVotingFinalizer>();
|
|
|
|
builder.Services.AddSingleton<DirectSessionNotificationSender>();
|
|
|
|
// ── Telegram infrastructure ──────────────────────────────────────────
|
|
builder.Services.AddSingleton<UpdateRouter>();
|
|
builder.Services.AddSingleton<ITelegramUpdateHandler>(sp => sp.GetRequiredService<UpdateRouter>());
|
|
builder.Services.AddHostedService<TelegramMiniAppMenuButtonService>();
|
|
builder.Services.AddHostedService<TelegramBotService>();
|
|
|
|
// ── Clock and scheduling ──────────────────────────────────────────────
|
|
builder.Services.AddSingleton<ISystemClock, GmRelay.Bot.Infrastructure.Scheduling.SystemClock>();
|
|
builder.Services.AddSingleton<ISessionTriggerStore, DbSessionTriggerStore>();
|
|
|
|
// ── Session scheduler ────────────────────────────────────────────────
|
|
builder.Services.AddHostedService<SessionSchedulerService>();
|
|
builder.Services.AddHostedService<RescheduleVotingDeadlineService>();
|
|
|
|
// ── Health check server ──────────────────────────────────────────────
|
|
builder.Services.AddHostedService<BotHealthCheckHostedService>();
|
|
|
|
var host = builder.Build();
|
|
|
|
// ── Run database migrations on startup ───────────────────────────────
|
|
var migrator = host.Services.GetRequiredService<DbMigrator>();
|
|
migrator.MigrateUp();
|
|
|
|
await host.RunAsync();
|