namespace GmRelay.DiscordBot.Features.Sessions; using NetCord.Rest; using NetCord.Services.ApplicationCommands; [SlashCommand("reschedule", "Initiate reschedule voting for a session")] public class DiscordRescheduleCommand : ApplicationCommandModule { private readonly DiscordRescheduleHandler _handler; private readonly ILogger _logger; public DiscordRescheduleCommand(DiscordRescheduleHandler handler, ILogger 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 { option1, option2 }; if (!string.IsNullOrWhiteSpace(option3)) options.Add(option3); var parsedOptions = new List(); 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; } }