fix(discord): update sessions via interactions
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
using GmRelay.DiscordBot.Infrastructure.Discord;
|
||||
using GmRelay.DiscordBot.Rendering;
|
||||
using GmRelay.Shared.Domain;
|
||||
using GmRelay.Shared.Features.Confirmation.HandleRsvp;
|
||||
using GmRelay.Shared.Features.Sessions.CreateSession;
|
||||
using GmRelay.Shared.Platform;
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
using NetCord;
|
||||
using NetCord.Rest;
|
||||
@@ -14,6 +16,7 @@ public sealed class DiscordSessionInteractionModule(
|
||||
JoinSessionHandler joinSessionHandler,
|
||||
LeaveSessionHandler leaveSessionHandler,
|
||||
HandleRsvpHandler rsvpHandler,
|
||||
DiscordDeleteSessionHandler deleteSessionHandler,
|
||||
DiscordRescheduleVoteHandler voteHandler,
|
||||
DiscordInteractionReplyCache interactionReplies,
|
||||
ILogger<DiscordSessionInteractionModule> logger) : ComponentInteractionModule<ButtonInteractionContext>
|
||||
@@ -28,21 +31,22 @@ public sealed class DiscordSessionInteractionModule(
|
||||
}
|
||||
|
||||
var input = CreateInput(parsedSessionId);
|
||||
await RespondAsync(InteractionCallback.DeferredMessage(MessageFlags.Ephemeral));
|
||||
await RespondAsync(InteractionCallback.DeferredModifyMessage);
|
||||
SessionInteractionResult result;
|
||||
try
|
||||
{
|
||||
await joinSessionHandler.HandleAsync(
|
||||
DiscordSessionInteractionMapper.CreateJoinCommand(input),
|
||||
result = await joinSessionHandler.HandleAsync(
|
||||
DiscordSessionInteractionMapper.CreateJoinCommand(input) with { DeferScheduleUpdate = true },
|
||||
CancellationToken.None);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Failed to handle Discord join interaction for session {SessionId}", parsedSessionId);
|
||||
await CompleteResponseAsync("Не удалось обработать кнопку.");
|
||||
await FollowupEphemeralAsync("Не удалось обработать кнопку.");
|
||||
return;
|
||||
}
|
||||
|
||||
await CompleteWithStoredReplyAsync(input.InteractionId);
|
||||
await CompleteScheduleUpdateResponseAsync(input.InteractionId, result);
|
||||
}
|
||||
|
||||
[ComponentInteraction("leave_session")]
|
||||
@@ -55,21 +59,56 @@ public sealed class DiscordSessionInteractionModule(
|
||||
}
|
||||
|
||||
var input = CreateInput(parsedSessionId);
|
||||
await RespondAsync(InteractionCallback.DeferredMessage(MessageFlags.Ephemeral));
|
||||
await RespondAsync(InteractionCallback.DeferredModifyMessage);
|
||||
SessionInteractionResult result;
|
||||
try
|
||||
{
|
||||
await leaveSessionHandler.HandleAsync(
|
||||
DiscordSessionInteractionMapper.CreateLeaveCommand(input),
|
||||
result = await leaveSessionHandler.HandleAsync(
|
||||
DiscordSessionInteractionMapper.CreateLeaveCommand(input) with { DeferScheduleUpdate = true },
|
||||
CancellationToken.None);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Failed to handle Discord leave interaction for session {SessionId}", parsedSessionId);
|
||||
await CompleteResponseAsync("Не удалось обработать кнопку.");
|
||||
await FollowupEphemeralAsync("Не удалось обработать кнопку.");
|
||||
return;
|
||||
}
|
||||
|
||||
await CompleteWithStoredReplyAsync(input.InteractionId);
|
||||
await CompleteScheduleUpdateResponseAsync(input.InteractionId, result);
|
||||
}
|
||||
|
||||
[ComponentInteraction("delete_session")]
|
||||
public async Task DeleteAsync(string sessionId)
|
||||
{
|
||||
if (!Guid.TryParse(sessionId, out var parsedSessionId))
|
||||
{
|
||||
await RespondAsync(CreateEphemeralReply("Session button is outdated."));
|
||||
return;
|
||||
}
|
||||
|
||||
var input = CreateInput(parsedSessionId);
|
||||
var member = Context.User as GuildInteractionUser;
|
||||
var resolvedPermissions = member is null ? 0UL : (ulong)member.Permissions;
|
||||
|
||||
await RespondAsync(InteractionCallback.DeferredModifyMessage);
|
||||
try
|
||||
{
|
||||
var result = await deleteSessionHandler.HandleAsync(
|
||||
guildId: input.GuildId,
|
||||
channelId: input.ChannelId,
|
||||
userId: input.UserId,
|
||||
resolvedPermissions: resolvedPermissions,
|
||||
guildOwnerId: 0,
|
||||
sessionId: parsedSessionId,
|
||||
CancellationToken.None);
|
||||
|
||||
await CompleteDeleteResponseAsync(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Failed to handle Discord delete interaction for session {SessionId}", parsedSessionId);
|
||||
await FollowupEphemeralAsync("Не удалось удалить сессию.");
|
||||
}
|
||||
}
|
||||
|
||||
[ComponentInteraction("rsvp")]
|
||||
@@ -124,7 +163,7 @@ public sealed class DiscordSessionInteractionModule(
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Failed to handle Discord RSVP interaction for session {SessionId}", parsedSessionId);
|
||||
await CompleteResponseAsync("Не удалось обработать кнопку.");
|
||||
await CompleteResponseAsync("Не удалось обработать кнопку.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -190,9 +229,85 @@ public sealed class DiscordSessionInteractionModule(
|
||||
await CompleteResponseAsync(reply?.Text ?? "Session updated.");
|
||||
}
|
||||
|
||||
private async Task CompleteScheduleUpdateResponseAsync(string interactionId, SessionInteractionResult result)
|
||||
{
|
||||
var updatedView = result.UpdatedView;
|
||||
if (updatedView is not null && SourceMessageHasDeleteAction())
|
||||
{
|
||||
updatedView = DiscordListSessionsHandler.AddManagerActions(updatedView);
|
||||
}
|
||||
|
||||
if (updatedView is not null)
|
||||
{
|
||||
var (embeds, actionRows) = DiscordSessionBatchRenderer.Render(updatedView);
|
||||
await ModifyResponseAsync(options =>
|
||||
{
|
||||
options.Embeds = embeds;
|
||||
options.Components = actionRows;
|
||||
});
|
||||
}
|
||||
|
||||
var reply = interactionReplies.Take(interactionId);
|
||||
await FollowupEphemeralAsync(reply?.Text ?? result.ReplyText);
|
||||
}
|
||||
|
||||
private async Task CompleteDeleteResponseAsync(DiscordDeleteSessionResult result)
|
||||
{
|
||||
if (result.UpdatedView is not null)
|
||||
{
|
||||
var (embeds, actionRows) = DiscordSessionBatchRenderer.Render(result.UpdatedView);
|
||||
await ModifyResponseAsync(options =>
|
||||
{
|
||||
options.Embeds = embeds;
|
||||
options.Components = actionRows;
|
||||
});
|
||||
}
|
||||
else if (result.EmptyMessage is not null)
|
||||
{
|
||||
await ModifyResponseAsync(options =>
|
||||
{
|
||||
options.Content = result.EmptyMessage;
|
||||
options.Embeds = [];
|
||||
options.Components = [];
|
||||
});
|
||||
}
|
||||
|
||||
await FollowupEphemeralAsync(result.ReplyText);
|
||||
}
|
||||
|
||||
private Task CompleteResponseAsync(string text) =>
|
||||
ModifyResponseAsync(options => options.Content = text);
|
||||
|
||||
private Task FollowupEphemeralAsync(string text) =>
|
||||
FollowupAsync(new InteractionMessageProperties()
|
||||
.WithContent(text)
|
||||
.WithFlags(MessageFlags.Ephemeral));
|
||||
|
||||
private bool SourceMessageHasDeleteAction() =>
|
||||
Context.Interaction.Message?.Components.Any(ComponentContainsDeleteAction) == true;
|
||||
|
||||
private static bool ComponentContainsDeleteAction(object? component)
|
||||
{
|
||||
if (component is null)
|
||||
return false;
|
||||
|
||||
if (component is IInteractiveComponent interactive
|
||||
&& interactive.CustomId.StartsWith("delete_session:", StringComparison.Ordinal))
|
||||
return true;
|
||||
|
||||
var nestedComponents = component.GetType().GetProperty("Components")?.GetValue(component) as IEnumerable;
|
||||
if (nestedComponents is null)
|
||||
return false;
|
||||
|
||||
foreach (var nestedComponent in nestedComponents)
|
||||
{
|
||||
if (ComponentContainsDeleteAction(nestedComponent))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static InteractionCallbackProperties CreateEphemeralReply(string text) =>
|
||||
InteractionCallback.Message(
|
||||
new InteractionMessageProperties()
|
||||
|
||||
Reference in New Issue
Block a user