118 lines
4.0 KiB
C#
118 lines
4.0 KiB
C#
using Dapper;
|
|
using GmRelay.Shared.Domain;
|
|
using GmRelay.Shared.Features.Notifications;
|
|
using GmRelay.Shared.Platform;
|
|
using Microsoft.Extensions.Logging;
|
|
using Npgsql;
|
|
|
|
namespace GmRelay.Shared.Features.Reminders.SendOneHourReminder;
|
|
|
|
internal sealed record OneHourReminderSessionRow(
|
|
Guid Id,
|
|
string Title,
|
|
string JoinLink,
|
|
DateTime ScheduledAt,
|
|
string NotificationMode);
|
|
|
|
internal sealed record OneHourReminderRecipientRow(
|
|
string Platform,
|
|
string ExternalUserId,
|
|
string DisplayName,
|
|
string? ExternalUsername);
|
|
|
|
public sealed class SendOneHourReminderHandler(
|
|
NpgsqlDataSource dataSource,
|
|
PlatformDirectNotificationSender directSender,
|
|
ILogger<SendOneHourReminderHandler> logger) : ISendOneHourReminderHandler
|
|
{
|
|
public async Task HandleAsync(Guid sessionId, CancellationToken ct)
|
|
{
|
|
await using var connection = await dataSource.OpenConnectionAsync(ct);
|
|
|
|
var session = await connection.QuerySingleOrDefaultAsync<OneHourReminderSessionRow>(
|
|
"""
|
|
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<OneHourReminderRecipientRow>(
|
|
"""
|
|
SELECT COALESCE(p.platform, 'Telegram') AS Platform,
|
|
COALESCE(p.external_user_id, p.telegram_id::TEXT) AS ExternalUserId,
|
|
p.display_name AS DisplayName,
|
|
COALESCE(p.external_username, p.telegram_username) AS ExternalUsername
|
|
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
|
|
}))
|
|
.Select(row => new PlatformUser(
|
|
ParsePlatform(row.Platform),
|
|
row.ExternalUserId,
|
|
row.DisplayName,
|
|
row.ExternalUsername))
|
|
.ToList();
|
|
|
|
var mode = SessionNotificationModeExtensions.FromDatabaseValue(session.NotificationMode);
|
|
if (mode.ShouldSendDirectMessages() && recipients.Count > 0)
|
|
{
|
|
await directSender.SendAsync(
|
|
PlatformDirectSessionNotificationKind.OneHourReminder,
|
|
recipients,
|
|
session.Id,
|
|
session.Title,
|
|
session.ScheduledAt,
|
|
session.JoinLink,
|
|
actorDisplayName: null,
|
|
reason: null,
|
|
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);
|
|
}
|
|
|
|
private static PlatformKind ParsePlatform(string platform) =>
|
|
Enum.Parse<PlatformKind>(platform, ignoreCase: true);
|
|
}
|