using Dapper; using GmRelay.Bot.Features.Notifications; using GmRelay.Shared.Domain; using Npgsql; namespace GmRelay.Bot.Features.Reminders.SendOneHourReminder; internal sealed record OneHourReminderSession( Guid Id, string Title, string JoinLink, DateTime ScheduledAt, string NotificationMode); public sealed class SendOneHourReminderHandler( NpgsqlDataSource dataSource, DirectSessionNotificationSender directSender, ILogger logger) : ISendOneHourReminderHandler { public async Task HandleAsync(Guid sessionId, CancellationToken ct) { await using var connection = await dataSource.OpenConnectionAsync(ct); var session = await connection.QuerySingleOrDefaultAsync( """ SELECT id, title, join_link AS JoinLink, scheduled_at AS ScheduledAt, notification_mode AS NotificationMode FROM sessions WHERE id = @SessionId AND status IN (@Confirmed, @ConfirmationSent) AND one_hour_reminder_processed_at IS NULL """, new { SessionId = sessionId, Confirmed = SessionStatus.Confirmed, ConfirmationSent = SessionStatus.ConfirmationSent }); if (session is null) { logger.LogWarning("Session {SessionId} not eligible for one-hour reminder", sessionId); return; } var recipients = (await connection.QueryAsync( """ SELECT p.telegram_id AS TelegramId, p.display_name AS DisplayName FROM session_participants sp JOIN players p ON p.id = sp.player_id WHERE sp.session_id = @SessionId AND sp.is_gm = false AND sp.registration_status = @Active AND sp.rsvp_status != @Declined """, new { SessionId = sessionId, Active = ParticipantRegistrationStatus.Active, Declined = RsvpStatus.Declined })).ToList(); var mode = SessionNotificationModeExtensions.FromDatabaseValue(session.NotificationMode); if (mode.ShouldSendDirectMessages() && recipients.Count > 0) { var text = $""" ⏰ Игра начнётся примерно через 1 час 📌 {System.Net.WebUtility.HtmlEncode(session.Title)} 📅 {session.ScheduledAt.FormatMoscow()} (МСК) 🔗 {System.Net.WebUtility.HtmlEncode(session.JoinLink)} """; await directSender.SendAsync(recipients, text, "one-hour-reminder", session.Id, ct); } await connection.ExecuteAsync( """ UPDATE sessions SET one_hour_reminder_processed_at = now(), updated_at = now() WHERE id = @SessionId AND one_hour_reminder_processed_at IS NULL """, new { SessionId = sessionId }); logger.LogInformation( "One-hour reminder processed for session {SessionId} ({Title}) with mode {NotificationMode}", sessionId, session.Title, session.NotificationMode); } }