using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; namespace Discord.Interactions { internal static class ApplicationCommandRestUtil { #region Parameters public static ApplicationCommandOptionProperties ToApplicationCommandOptionProps(this SlashCommandParameterInfo parameterInfo) { var localizationManager = parameterInfo.Command.Module.CommandService.LocalizationManager; var parameterPath = parameterInfo.GetParameterPath(); var props = new ApplicationCommandOptionProperties { Name = parameterInfo.Name, Description = parameterInfo.Description, Type = parameterInfo.DiscordOptionType.Value, IsRequired = parameterInfo.IsRequired, Choices = parameterInfo.Choices?.Select(x => new ApplicationCommandOptionChoiceProperties { Name = x.Name, Value = x.Value, NameLocalizations = localizationManager?.GetAllNames(parameterInfo.GetChoicePath(x), LocalizationTarget.Choice) ?? ImmutableDictionary.Empty })?.ToList(), ChannelTypes = parameterInfo.ChannelTypes?.ToList(), IsAutocomplete = parameterInfo.IsAutocomplete, MaxValue = parameterInfo.MaxValue, MinValue = parameterInfo.MinValue, NameLocalizations = localizationManager?.GetAllNames(parameterPath, LocalizationTarget.Parameter) ?? ImmutableDictionary.Empty, DescriptionLocalizations = localizationManager?.GetAllDescriptions(parameterPath, LocalizationTarget.Parameter) ?? ImmutableDictionary.Empty, MinLength = parameterInfo.MinLength, MaxLength = parameterInfo.MaxLength, }; parameterInfo.TypeConverter.Write(props, parameterInfo); return props; } #endregion #region Commands public static SlashCommandProperties ToApplicationCommandProps(this SlashCommandInfo commandInfo) { var commandPath = commandInfo.GetCommandPath(); var localizationManager = commandInfo.Module.CommandService.LocalizationManager; var props = new SlashCommandBuilder() { Name = commandInfo.Name, Description = commandInfo.Description, IsDefaultPermission = commandInfo.DefaultPermission, IsDMEnabled = commandInfo.IsEnabledInDm, IsNsfw = commandInfo.IsNsfw, DefaultMemberPermissions = ((commandInfo.DefaultMemberPermissions ?? 0) | (commandInfo.Module.DefaultMemberPermissions ?? 0)).SanitizeGuildPermissions(), }.WithNameLocalizations(localizationManager?.GetAllNames(commandPath, LocalizationTarget.Command) ?? ImmutableDictionary.Empty) .WithDescriptionLocalizations(localizationManager?.GetAllDescriptions(commandPath, LocalizationTarget.Command) ?? ImmutableDictionary.Empty) .Build(); if (commandInfo.Parameters.Count > SlashCommandBuilder.MaxOptionsCount) throw new InvalidOperationException($"Slash Commands cannot have more than {SlashCommandBuilder.MaxOptionsCount} command parameters"); props.Options = commandInfo.FlattenedParameters.Select(x => x.ToApplicationCommandOptionProps())?.ToList() ?? Optional>.Unspecified; return props; } public static ApplicationCommandOptionProperties ToApplicationCommandOptionProps(this SlashCommandInfo commandInfo) { var localizationManager = commandInfo.Module.CommandService.LocalizationManager; var commandPath = commandInfo.GetCommandPath(); return new ApplicationCommandOptionProperties { Name = commandInfo.Name, Description = commandInfo.Description, Type = ApplicationCommandOptionType.SubCommand, IsRequired = false, Options = commandInfo.FlattenedParameters?.Select(x => x.ToApplicationCommandOptionProps()) ?.ToList(), NameLocalizations = localizationManager?.GetAllNames(commandPath, LocalizationTarget.Command) ?? ImmutableDictionary.Empty, DescriptionLocalizations = localizationManager?.GetAllDescriptions(commandPath, LocalizationTarget.Command) ?? ImmutableDictionary.Empty }; } public static ApplicationCommandProperties ToApplicationCommandProps(this ContextCommandInfo commandInfo) { var localizationManager = commandInfo.Module.CommandService.LocalizationManager; var commandPath = commandInfo.GetCommandPath(); return commandInfo.CommandType switch { ApplicationCommandType.Message => new MessageCommandBuilder { Name = commandInfo.Name, IsDefaultPermission = commandInfo.DefaultPermission, DefaultMemberPermissions = ((commandInfo.DefaultMemberPermissions ?? 0) | (commandInfo.Module.DefaultMemberPermissions ?? 0)).SanitizeGuildPermissions(), IsDMEnabled = commandInfo.IsEnabledInDm, IsNsfw = commandInfo.IsNsfw, } .WithNameLocalizations(localizationManager?.GetAllNames(commandPath, LocalizationTarget.Command) ?? ImmutableDictionary.Empty) .Build(), ApplicationCommandType.User => new UserCommandBuilder { Name = commandInfo.Name, IsDefaultPermission = commandInfo.DefaultPermission, DefaultMemberPermissions = ((commandInfo.DefaultMemberPermissions ?? 0) | (commandInfo.Module.DefaultMemberPermissions ?? 0)).SanitizeGuildPermissions(), IsNsfw = commandInfo.IsNsfw, IsDMEnabled = commandInfo.IsEnabledInDm } .WithNameLocalizations(localizationManager?.GetAllNames(commandPath, LocalizationTarget.Command) ?? ImmutableDictionary.Empty) .Build(), _ => throw new InvalidOperationException($"{commandInfo.CommandType} isn't a supported command type.") }; } #endregion #region Modules public static IReadOnlyCollection ToApplicationCommandProps(this ModuleInfo moduleInfo, bool ignoreDontRegister = false) { var args = new List(); moduleInfo.ParseModuleModel(args, ignoreDontRegister); return args; } private static void ParseModuleModel(this ModuleInfo moduleInfo, List args, bool ignoreDontRegister) { if (moduleInfo.DontAutoRegister && !ignoreDontRegister) return; args.AddRange(moduleInfo.ContextCommands?.Select(x => x.ToApplicationCommandProps())); if (!moduleInfo.IsSlashGroup) { args.AddRange(moduleInfo.SlashCommands?.Select(x => x.ToApplicationCommandProps())); foreach (var submodule in moduleInfo.SubModules) submodule.ParseModuleModel(args, ignoreDontRegister); } else { var options = new List(); foreach (var command in moduleInfo.SlashCommands) { if (command.IgnoreGroupNames) args.Add(command.ToApplicationCommandProps()); else options.Add(command.ToApplicationCommandOptionProps()); } options.AddRange(moduleInfo.SubModules?.SelectMany(x => x.ParseSubModule(args, ignoreDontRegister))); var localizationManager = moduleInfo.CommandService.LocalizationManager; var modulePath = moduleInfo.GetModulePath(); var props = new SlashCommandBuilder { Name = moduleInfo.SlashGroupName, Description = moduleInfo.Description, IsDefaultPermission = moduleInfo.DefaultPermission, IsDMEnabled = moduleInfo.IsEnabledInDm, IsNsfw = moduleInfo.IsNsfw, DefaultMemberPermissions = moduleInfo.DefaultMemberPermissions } .WithNameLocalizations(localizationManager?.GetAllNames(modulePath, LocalizationTarget.Group) ?? ImmutableDictionary.Empty) .WithDescriptionLocalizations(localizationManager?.GetAllDescriptions(modulePath, LocalizationTarget.Group) ?? ImmutableDictionary.Empty) .Build(); if (options.Count > SlashCommandBuilder.MaxOptionsCount) throw new InvalidOperationException($"Slash Commands cannot have more than {SlashCommandBuilder.MaxOptionsCount} command parameters"); props.Options = options; args.Add(props); } } private static IReadOnlyCollection ParseSubModule(this ModuleInfo moduleInfo, List args, bool ignoreDontRegister) { if (moduleInfo.DontAutoRegister && !ignoreDontRegister) return Array.Empty(); args.AddRange(moduleInfo.ContextCommands?.Select(x => x.ToApplicationCommandProps())); var options = new List(); options.AddRange(moduleInfo.SubModules?.SelectMany(x => x.ParseSubModule(args, ignoreDontRegister))); foreach (var command in moduleInfo.SlashCommands) { if (command.IgnoreGroupNames) args.Add(command.ToApplicationCommandProps()); else options.Add(command.ToApplicationCommandOptionProps()); } if (!moduleInfo.IsSlashGroup) return options; else return new List() { new ApplicationCommandOptionProperties { Name = moduleInfo.SlashGroupName, Description = moduleInfo.Description, Type = ApplicationCommandOptionType.SubCommandGroup, Options = options, NameLocalizations = moduleInfo.CommandService.LocalizationManager?.GetAllNames(moduleInfo.GetModulePath(), LocalizationTarget.Group) ?? ImmutableDictionary.Empty, DescriptionLocalizations = moduleInfo.CommandService.LocalizationManager?.GetAllDescriptions(moduleInfo.GetModulePath(), LocalizationTarget.Group) ?? ImmutableDictionary.Empty, } }; } #endregion public static ApplicationCommandProperties ToApplicationCommandProps(this IApplicationCommand command) { return command.Type switch { ApplicationCommandType.Slash => new SlashCommandProperties { Name = command.Name, Description = command.Description, IsDefaultPermission = command.IsDefaultPermission, DefaultMemberPermissions = command.DefaultMemberPermissions.RawValue == 0 ? new Optional() : (GuildPermission)command.DefaultMemberPermissions.RawValue, IsDMEnabled = command.IsEnabledInDm, IsNsfw = command.IsNsfw, Options = command.Options?.Select(x => x.ToApplicationCommandOptionProps())?.ToList() ?? Optional>.Unspecified, NameLocalizations = command.NameLocalizations?.ToImmutableDictionary() ?? ImmutableDictionary.Empty, DescriptionLocalizations = command.DescriptionLocalizations?.ToImmutableDictionary() ?? ImmutableDictionary.Empty, }, ApplicationCommandType.User => new UserCommandProperties { Name = command.Name, IsDefaultPermission = command.IsDefaultPermission, DefaultMemberPermissions = command.DefaultMemberPermissions.RawValue == 0 ? new Optional() : (GuildPermission)command.DefaultMemberPermissions.RawValue, IsNsfw = command.IsNsfw, IsDMEnabled = command.IsEnabledInDm, NameLocalizations = command.NameLocalizations?.ToImmutableDictionary() ?? ImmutableDictionary.Empty, DescriptionLocalizations = command.DescriptionLocalizations?.ToImmutableDictionary() ?? ImmutableDictionary.Empty }, ApplicationCommandType.Message => new MessageCommandProperties { Name = command.Name, IsDefaultPermission = command.IsDefaultPermission, DefaultMemberPermissions = command.DefaultMemberPermissions.RawValue == 0 ? new Optional() : (GuildPermission)command.DefaultMemberPermissions.RawValue, IsNsfw = command.IsNsfw, IsDMEnabled = command.IsEnabledInDm, NameLocalizations = command.NameLocalizations?.ToImmutableDictionary() ?? ImmutableDictionary.Empty, DescriptionLocalizations = command.DescriptionLocalizations?.ToImmutableDictionary() ?? ImmutableDictionary.Empty }, _ => throw new InvalidOperationException($"Cannot create command properties for command type {command.Type}"), }; } public static ApplicationCommandOptionProperties ToApplicationCommandOptionProps(this IApplicationCommandOption commandOption) => new ApplicationCommandOptionProperties { Name = commandOption.Name, Description = commandOption.Description, Type = commandOption.Type, IsRequired = commandOption.IsRequired, ChannelTypes = commandOption.ChannelTypes?.ToList(), IsAutocomplete = commandOption.IsAutocomplete.GetValueOrDefault(), MinValue = commandOption.MinValue, MaxValue = commandOption.MaxValue, Choices = commandOption.Choices?.Select(x => new ApplicationCommandOptionChoiceProperties { Name = x.Name, Value = x.Value }).ToList(), Options = commandOption.Options?.Select(x => x.ToApplicationCommandOptionProps()).ToList(), NameLocalizations = commandOption.NameLocalizations?.ToImmutableDictionary(), DescriptionLocalizations = commandOption.DescriptionLocalizations?.ToImmutableDictionary(), MaxLength = commandOption.MaxLength, MinLength = commandOption.MinLength, }; public static Modal ToModal(this ModalInfo modalInfo, string customId, Action modifyModal = null) { var builder = new ModalBuilder(modalInfo.Title, customId); foreach (var input in modalInfo.Components) switch (input) { case TextInputComponentInfo textComponent: builder.AddTextInput(textComponent.Label, textComponent.CustomId, textComponent.Style, textComponent.Placeholder, textComponent.IsRequired ? textComponent.MinLength : null, textComponent.MaxLength, textComponent.IsRequired, textComponent.InitialValue); break; default: throw new InvalidOperationException($"{input.GetType().FullName} isn't a valid component info class"); } modifyModal?.Invoke(builder); return builder.Build(); } public static GuildPermission? SanitizeGuildPermissions(this GuildPermission permissions) => permissions == 0 ? null : permissions; } }