feat(discord): add /reschedule slash command and handler
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
namespace GmRelay.DiscordBot.Features.Sessions;
|
||||
|
||||
using NetCord.Rest;
|
||||
using NetCord.Services.ApplicationCommands;
|
||||
|
||||
[SlashCommand("reschedule", "Initiate reschedule voting for a session")]
|
||||
public class DiscordRescheduleCommand : ApplicationCommandModule<SlashCommandContext>
|
||||
{
|
||||
private readonly DiscordRescheduleHandler _handler;
|
||||
private readonly ILogger<DiscordRescheduleCommand> _logger;
|
||||
|
||||
public DiscordRescheduleCommand(DiscordRescheduleHandler handler, ILogger<DiscordRescheduleCommand> logger)
|
||||
{
|
||||
_handler = handler;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task ExecuteAsync(
|
||||
[SlashCommandParameter(Name = "session", Description = "Session ID to reschedule")] string sessionIdText,
|
||||
[SlashCommandParameter(Name = "option1", Description = "First time option (YYYY-MM-DD HH:mm)")] string option1,
|
||||
[SlashCommandParameter(Name = "option2", Description = "Second time option (YYYY-MM-DD HH:mm)")] string option2,
|
||||
[SlashCommandParameter(Name = "option3", Description = "Third time option (optional)")] string? option3 = null,
|
||||
[SlashCommandParameter(Name = "deadline", Description = "Voting deadline (YYYY-MM-DD HH:mm)")] string deadline = "")
|
||||
{
|
||||
var guild = Context.Guild
|
||||
?? throw new InvalidOperationException("This command can only be used in a guild.");
|
||||
|
||||
if (!Guid.TryParse(sessionIdText, out var sessionId))
|
||||
{
|
||||
await Context.Interaction.SendResponseAsync(
|
||||
InteractionCallback.Message("❌ Некорректный ID сессии."));
|
||||
return;
|
||||
}
|
||||
|
||||
var options = new List<string> { option1, option2 };
|
||||
if (!string.IsNullOrWhiteSpace(option3))
|
||||
options.Add(option3);
|
||||
|
||||
var parsedOptions = new List<DateTimeOffset>();
|
||||
foreach (var opt in options)
|
||||
{
|
||||
var result = DiscordNewSessionHandler.ParseTimeInput(opt);
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
await Context.Interaction.SendResponseAsync(
|
||||
InteractionCallback.Message($"❌ {opt}: {result.Error}"));
|
||||
return;
|
||||
}
|
||||
parsedOptions.Add(result.Value);
|
||||
}
|
||||
|
||||
var deadlineResult = DiscordNewSessionHandler.ParseTimeInput(deadline);
|
||||
if (!deadlineResult.IsSuccess)
|
||||
{
|
||||
await Context.Interaction.SendResponseAsync(
|
||||
InteractionCallback.Message($"❌ Дедлайн: {deadlineResult.Error}"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (deadlineResult.Value >= parsedOptions.Min())
|
||||
{
|
||||
await Context.Interaction.SendResponseAsync(
|
||||
InteractionCallback.Message("❌ Дедлайн должен быть раньше первого варианта времени."));
|
||||
return;
|
||||
}
|
||||
|
||||
var resolvedPermissions = GetResolvedPermissions(guild, Context.User.Id);
|
||||
|
||||
try
|
||||
{
|
||||
var result = await _handler.HandleAsync(
|
||||
guildId: guild.Id.ToString(),
|
||||
channelId: Context.Channel.Id.ToString(),
|
||||
userId: Context.User.Id,
|
||||
userDisplayName: Context.User.GlobalName ?? Context.User.Username,
|
||||
resolvedPermissions: resolvedPermissions,
|
||||
guildOwnerId: guild.OwnerId,
|
||||
sessionId: sessionId,
|
||||
options: parsedOptions,
|
||||
deadline: deadlineResult.Value,
|
||||
CancellationToken.None);
|
||||
|
||||
await Context.Interaction.SendResponseAsync(
|
||||
InteractionCallback.Message(
|
||||
$"🗳 Голосование за перенос запущено! Дедлайн: {deadlineResult.Value:yyyy-MM-dd HH:mm} UTC."));
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
await Context.Interaction.SendResponseAsync(
|
||||
InteractionCallback.Message($":no_entry: {ex.Message}"));
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
await Context.Interaction.SendResponseAsync(
|
||||
InteractionCallback.Message($":warning: {ex.Message}"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to initiate reschedule for session {SessionId}", sessionId);
|
||||
await Context.Interaction.SendResponseAsync(
|
||||
InteractionCallback.Message(":boom: Ошибка при запуске голосования."));
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong GetResolvedPermissions(NetCord.Gateway.Guild guild, ulong userId)
|
||||
{
|
||||
if (!guild.Users.TryGetValue(userId, out var guildUser))
|
||||
return 0;
|
||||
ulong resolved = 0;
|
||||
foreach (var roleId in guildUser.RoleIds)
|
||||
{
|
||||
if (guild.Roles.TryGetValue(roleId, out var role))
|
||||
resolved |= (ulong)role.Permissions;
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user