using GmRelay.DiscordBot.Rendering; using NetCord; using NetCord.Rest; using NetCord.Services.ApplicationCommands; namespace GmRelay.DiscordBot.Features.Sessions; public class DiscordNewSessionCommand : ApplicationCommandModule { private readonly DiscordNewSessionHandler _handler; private readonly ILogger _logger; public DiscordNewSessionCommand(DiscordNewSessionHandler handler, ILogger logger) { _handler = handler; _logger = logger; } [SlashCommand("newsession", "Create a new game session")] public async Task ExecuteAsync( [SlashCommandParameter(Name = "title", Description = "Game title")] string title, [SlashCommandParameter(Name = "time", Description = "Session time (YYYY-MM-DD HH:mm or DD.MM.YYYY HH:mm)")] string time, [SlashCommandParameter(Name = "seats", Description = "Maximum number of players")] long? seats = null, [SlashCommandParameter(Name = "link", Description = "Join link")] string? link = null) { _logger.LogInformation( "newsession called by user {UserId} ({UserType}) in guild {GuildId}, channel {ChannelId}", Context.User.Id, Context.User.GetType().Name, Context.Interaction.GuildId, Context.Channel?.Id); var guildId = Context.Interaction.GuildId ?? throw new InvalidOperationException("This command can only be used in a guild."); var member = Context.User as GuildInteractionUser; if (member is null) { _logger.LogError("Context.User is not GuildInteractionUser. Actual type: {ActualType}", Context.User.GetType().Name); throw new InvalidOperationException("Guild member data not available in interaction."); } var resolvedPermissions = (ulong)member.Permissions; _logger.LogInformation("Resolved permissions for user {UserId}: {Permissions}", Context.User.Id, resolvedPermissions); ulong guildOwnerId = 0; var guildName = guildId.ToString(); try { var guild = await Context.Client.Rest.GetGuildAsync(guildId); guildOwnerId = guild.OwnerId; guildName = guild.Name; _logger.LogInformation("Guild owner id: {OwnerId}", guildOwnerId); } catch (RestException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) { _logger.LogWarning( ex, "Bot is not a REST member of guild {GuildId}; using resolved permissions from interaction payload", guildId); } catch (Exception ex) { _logger.LogError(ex, "Unexpected error fetching guild {GuildId}", guildId); } var timeResult = DiscordNewSessionHandler.ParseTimeInput(time); if (!timeResult.IsSuccess) { await Context.Interaction.SendResponseAsync( InteractionCallback.Message($"X {timeResult.Error}")); return; } // Defer the response to avoid Discord 3-second interaction timeout await Context.Interaction.SendResponseAsync(InteractionCallback.DeferredMessage()); try { _logger.LogInformation("Creating session for guild {GuildId}, user {UserId}", guildId, Context.User.Id); var view = await _handler.HandleAsync( guildId: guildId.ToString(), channelId: Context.Channel!.Id.ToString(), groupName: guildName, userId: Context.User.Id, userDisplayName: Context.User.GlobalName ?? Context.User.Username, resolvedPermissions: resolvedPermissions, guildOwnerId: guildOwnerId, title: title, scheduledAt: timeResult.Value, maxPlayers: seats is null ? null : (int)seats.Value, joinLink: link, CancellationToken.None); _logger.LogInformation("Session created successfully. Building render."); var (embeds, actionRows) = DiscordSessionBatchRenderer.Render(view); _logger.LogInformation("Sending success response."); await Context.Interaction.ModifyResponseAsync(message => { message.Content = ":white_check_mark: **Session created successfully!**"; message.Embeds = embeds; message.Components = actionRows; }); _logger.LogInformation("Success response sent."); } catch (UnauthorizedAccessException ex) { _logger.LogWarning(ex, "Unauthorized session creation attempt by user {UserId}", Context.User.Id); await Context.Interaction.ModifyResponseAsync(message => { message.Content = $":no_entry: {ex.Message}"; }); } catch (Exception ex) { _logger.LogError(ex, "Failed to create session for user {UserId} in guild {GuildId}", Context.User.Id, guildId); await Context.Interaction.ModifyResponseAsync(message => { message.Content = ":boom: An error occurred while creating the session."; }); } } }