fix(discord): avoid duplicate schedule send after new session
Deploy Telegram Bot / build-and-push (push) Successful in 6m0s
Deploy Telegram Bot / scan-images (push) Successful in 3m22s
Deploy Telegram Bot / deploy (push) Successful in 29s

This commit is contained in:
2026-05-26 13:40:59 +03:00
parent 682dd3fdec
commit 6ed0a120a0
2 changed files with 31 additions and 13 deletions
@@ -1,7 +1,6 @@
using Dapper; using Dapper;
using GmRelay.DiscordBot.Infrastructure.Discord; using GmRelay.DiscordBot.Infrastructure.Discord;
using GmRelay.Shared.Domain; using GmRelay.Shared.Domain;
using GmRelay.Shared.Platform;
using GmRelay.Shared.Rendering; using GmRelay.Shared.Rendering;
using Npgsql; using Npgsql;
@@ -12,7 +11,6 @@ public sealed record TimeParseResult(bool IsSuccess, DateTimeOffset Value, strin
public sealed class DiscordNewSessionHandler( public sealed class DiscordNewSessionHandler(
NpgsqlDataSource dataSource, NpgsqlDataSource dataSource,
DiscordPermissionChecker permissionChecker, DiscordPermissionChecker permissionChecker,
IPlatformMessenger messenger,
ILogger<DiscordNewSessionHandler> logger) ILogger<DiscordNewSessionHandler> logger)
{ {
public static TimeParseResult ParseTimeInput(string input) public static TimeParseResult ParseTimeInput(string input)
@@ -75,6 +73,7 @@ public sealed class DiscordNewSessionHandler(
} }
await using var transaction = await connection.BeginTransactionAsync(cancellationToken); await using var transaction = await connection.BeginTransactionAsync(cancellationToken);
var transactionCommitted = false;
try try
{ {
await connection.ExecuteAsync( await connection.ExecuteAsync(
@@ -125,23 +124,19 @@ public sealed class DiscordNewSessionHandler(
transaction); transaction);
await transaction.CommitAsync(cancellationToken); await transaction.CommitAsync(cancellationToken);
transactionCommitted = true;
logger.LogInformation("Created session {SessionId} in guild {GuildId}", sessionId, guildId); logger.LogInformation("Created session {SessionId} in guild {GuildId}", sessionId, guildId);
var sessions = new[] { new SessionBatchDto(sessionId, scheduledAt.UtcDateTime, SessionStatus.Planned, maxPlayers, joinLink ?? string.Empty) }; var sessions = new[] { new SessionBatchDto(sessionId, scheduledAt.UtcDateTime, SessionStatus.Planned, maxPlayers, joinLink ?? string.Empty) };
var view = SessionBatchViewBuilder.Build(title, sessions, Array.Empty<ParticipantBatchDto>()); return SessionBatchViewBuilder.Build(title, sessions, Array.Empty<ParticipantBatchDto>());
await messenger.SendScheduleAsync(
new PlatformScheduleMessage(
new PlatformGroup(PlatformKind.Discord, guildId, guildId, channelId),
view,
null),
cancellationToken);
return view;
} }
catch catch
{ {
await transaction.RollbackAsync(cancellationToken); if (!transactionCommitted)
{
await transaction.RollbackAsync(cancellationToken);
}
throw; throw;
} }
} }
@@ -127,6 +127,18 @@ public sealed class DiscordNewSessionHandlerTests
Assert.Contains("RollbackAsync", source, StringComparison.Ordinal); Assert.Contains("RollbackAsync", source, StringComparison.Ordinal);
} }
[Fact]
public void Handler_ShouldNotRollbackCommittedTransactionAfterPostCommitFailure()
{
var repoRoot = GetRepoRoot();
var handlerPath = Path.Combine(repoRoot, "src", "GmRelay.DiscordBot", "Features", "Sessions", "DiscordNewSessionHandler.cs");
var source = File.ReadAllText(handlerPath);
Assert.Contains("transactionCommitted = false", source, StringComparison.Ordinal);
Assert.Contains("transactionCommitted = true", source, StringComparison.Ordinal);
Assert.Contains("if (!transactionCommitted)", source, StringComparison.Ordinal);
}
[Fact] [Fact]
public void Handler_ShouldRespectCancellationToken() public void Handler_ShouldRespectCancellationToken()
{ {
@@ -148,6 +160,17 @@ public sealed class DiscordNewSessionHandlerTests
Assert.Contains("message.Embeds = embeds", source, StringComparison.Ordinal); Assert.Contains("message.Embeds = embeds", source, StringComparison.Ordinal);
} }
[Fact]
public void Handler_ShouldLeaveScheduleMessageCreationToInteractionResponse()
{
var repoRoot = GetRepoRoot();
var handlerPath = Path.Combine(repoRoot, "src", "GmRelay.DiscordBot", "Features", "Sessions", "DiscordNewSessionHandler.cs");
var source = File.ReadAllText(handlerPath);
Assert.DoesNotContain("SendScheduleAsync", source, StringComparison.Ordinal);
Assert.DoesNotContain("PlatformScheduleMessage", source, StringComparison.Ordinal);
}
private static DateTimeOffset FutureDateAt1930() private static DateTimeOffset FutureDateAt1930()
{ {
var future = DateTimeOffset.UtcNow.AddDays(7); var future = DateTimeOffset.UtcNow.AddDays(7);