/// <summary> /// Creates a new instance of a <see cref="DiscordApplicationCommandOption"/>. /// </summary> /// <param name="name">The name of this parameter.</param> /// <param name="description">The description of the parameter.</param> /// <param name="type">The type of this parameter.</param> /// <param name="required">Whether the parameter is required.</param> /// <param name="choices">The optional choice selection for this parameter.</param> /// <param name="options">The optional subcommands for this parameter.</param> public DiscordApplicationCommandOption(string name, string description, ApplicationCommandOptionType type, bool?required = null, IEnumerable <DiscordApplicationCommandOptionChoice> choices = null, IEnumerable <DiscordApplicationCommandOption> options = null) { if (!Utilities.IsValidSlashCommandName(name)) { throw new ArgumentException("Invalid slash command option name specified. It must be below 32 characters and not contain any whitespace.", nameof(name)); } if (name.Any(ch => char.IsUpper(ch))) { throw new ArgumentException("Slash command option name cannot have any upper case characters.", nameof(name)); } if (description.Length > 100) { throw new ArgumentException("Slash command option description cannot exceed 100 characters.", nameof(description)); } var choiceList = choices != null ? new ReadOnlyCollection <DiscordApplicationCommandOptionChoice>(choices.ToList()) : null; var optionList = options != null ? new ReadOnlyCollection <DiscordApplicationCommandOption>(options.ToList()) : null; this.Name = name; this.Description = description; this.Type = type; this.Required = required; this.Choices = choiceList; this.Options = optionList; }
internal AutocompleteOption(ApplicationCommandOptionType type, string name, object value, bool focused) { Type = type; Name = name; Value = value; Focused = focused; }
/// <summary> /// Creates a new instance of a <see cref="DiscordApplicationCommandOption"/>. /// </summary> /// <param name="name">The name of this parameter.</param> /// <param name="description">The description of the parameter.</param> /// <param name="type">The type of this parameter.</param> /// <param name="required">Whether the parameter is required.</param> /// <param name="choices">The optional choice selection for this parameter.</param> /// <param name="options">The optional subcommands for this parameter.</param> public DiscordApplicationCommandOption(string name, string description, ApplicationCommandOptionType type, bool?required = null, IEnumerable <DiscordApplicationCommandOptionChoice> choices = null, IEnumerable <DiscordApplicationCommandOption> options = null) { var choiceList = choices != null ? new ReadOnlyCollection <DiscordApplicationCommandOptionChoice>(choices.ToList()) : null; var optionList = options != null ? new ReadOnlyCollection <DiscordApplicationCommandOption>(options.ToList()) : null; this.Name = name; this.Description = description; this.Type = type; this.Required = required; this.Choices = choiceList; this.Options = optionList; }
/// <summary> /// Adds an option to the current slash command. /// </summary> /// <param name="Name">The name of the option to add.</param> /// <param name="Type">The type of this option.</param> /// <param name="Description">The description of this option.</param> /// <param name="Required">If this option is required for this command.</param> /// <param name="Default">If this option is the default option.</param> /// <param name="Options">The options of the option to add.</param> /// <param name="Choices">The choices of this option.</param> /// <returns>The current builder.</returns> public SlashCommandBuilder AddOption(string Name, ApplicationCommandOptionType Type, string Description, bool Required = true, bool Default = false, List <SlashCommandOptionBuilder> Options = null, params ApplicationCommandOptionChoiceProperties[] Choices) { // Make sure the name matches the requirements from discord Preconditions.NotNullOrEmpty(Name, nameof(Name)); Preconditions.AtLeast(Name.Length, 3, nameof(Name)); Preconditions.AtMost(Name.Length, MaxNameLength, nameof(Name)); // Discord updated the docs, this regex prevents special characters like @!$%( and s p a c e s.. etc, // https://discord.com/developers/docs/interactions/slash-commands#applicationcommand if (!Regex.IsMatch(Name, @"^[\w-]{3,32}$")) { throw new ArgumentException("Command name cannot contian any special characters or whitespaces!", nameof(Name)); } // same with description Preconditions.NotNullOrEmpty(Description, nameof(Description)); Preconditions.AtLeast(Description.Length, 3, nameof(Description)); Preconditions.AtMost(Description.Length, MaxDescriptionLength, nameof(Description)); // make sure theres only one option with default set to true if (Default) { if (this.Options != null) { if (this.Options.Any(x => x.Default)) { throw new ArgumentException("There can only be one command option with default set to true!", nameof(Default)); } } } SlashCommandOptionBuilder option = new SlashCommandOptionBuilder(); option.Name = Name; option.Description = Description; option.Required = Required; option.Default = Default; option.Options = Options; option.Choices = Choices != null ? new List <ApplicationCommandOptionChoiceProperties>(Choices) : null; return(AddOption(option)); }
/// <summary> /// Adds an option to the current slash command. /// </summary> /// <param name="Name">The name of the option to add.</param> /// <param name="Type">The type of this option.</param> /// <param name="Description">The sescription of this option.</param> /// <returns>The current builder.</returns> public SlashCommandBuilder AddOption(string Name, ApplicationCommandOptionType Type, string Description) => AddOption(Name, Type, Description, Options: null, Choices: null);
/// <summary> /// Adds an option to the current slash command. /// </summary> /// <param name="Name">The name of the option to add.</param> /// <param name="Type">The type of this option.</param> /// <param name="Description">The description of this option.</param> /// <param name="Required">If this option is required for this command.</param> /// <param name="Default">If this option is the default option.</param> /// <param name="Choices">The choices of this option.</param> /// <returns>The current builder.</returns> public SlashCommandBuilder AddOption(string Name, ApplicationCommandOptionType Type, string Description, bool Required = true, bool Default = false, params ApplicationCommandOptionChoiceProperties[] Choices) => AddOption(Name, Type, Description, Required, Default, null, Choices);
private void RegisterCommands(Type[] types, ulong?guildid) { CommandMethod[] InternalCommandMethods = Array.Empty <CommandMethod>(); GroupCommand[] InternalGroupCommands = Array.Empty <GroupCommand>(); SubGroupCommand[] InternalSubGroupCommands = Array.Empty <SubGroupCommand>(); List <DiscordApplicationCommand> ToUpdate = new List <DiscordApplicationCommand>(); _ = Task.Run(async() => { foreach (var t in types) { try { var ti = t.GetTypeInfo(); var classes = ti.DeclaredNestedTypes; foreach (var tti in classes.Where(x => x.GetCustomAttribute <SlashCommandGroupAttribute>() != null)) { var groupatt = tti.GetCustomAttribute <SlashCommandGroupAttribute>(); var submethods = tti.DeclaredMethods; var subclasses = tti.DeclaredNestedTypes; if (subclasses.Any(x => x.GetCustomAttribute <SlashCommandGroupAttribute>() != null) && submethods.Any(x => x.GetCustomAttribute <SlashCommandAttribute>() != null)) { throw new ArgumentException("Slash command groups cannot have both subcommands and subgroups!"); } var payload = new DiscordApplicationCommand(groupatt.Name, groupatt.Description); var commandmethods = new Dictionary <string, MethodInfo>(); foreach (var submethod in submethods.Where(x => x.GetCustomAttribute <SlashCommandAttribute>() != null)) { var commandattribute = submethod.GetCustomAttribute <SlashCommandAttribute>(); var parameters = submethod.GetParameters(); if (!ReferenceEquals(parameters.First().ParameterType, typeof(InteractionContext))) { throw new ArgumentException($"The first argument must be an InteractionContext!"); } parameters = parameters.Skip(1).ToArray(); var options = ParseParameters(parameters); var subpayload = new DiscordApplicationCommandOption(commandattribute.Name, commandattribute.Description, ApplicationCommandOptionType.SubCommand, null, null, options); commandmethods.Add(commandattribute.Name, submethod); payload = new DiscordApplicationCommand(payload.Name, payload.Description, payload.Options != null ? payload.Options.ToArray().Append(subpayload) : new DiscordApplicationCommandOption[] { subpayload }); InternalGroupCommands = InternalGroupCommands.Append(new GroupCommand { Name = groupatt.Name, ParentClass = tti, Methods = commandmethods }).ToArray(); } foreach (var subclass in subclasses.Where(x => x.GetCustomAttribute <SlashCommandGroupAttribute>() != null)) { var subgroupatt = subclass.GetCustomAttribute <SlashCommandGroupAttribute>(); var subsubmethods = subclass.DeclaredMethods.Where(x => x.GetCustomAttribute <SlashCommandAttribute>() != null); var command = new SubGroupCommand { Name = groupatt.Name }; var options = new List <DiscordApplicationCommandOption>(); foreach (var subsubmethod in subsubmethods) { var suboptions = new List <DiscordApplicationCommandOption>(); var commatt = subsubmethod.GetCustomAttribute <SlashCommandAttribute>(); var parameters = subsubmethod.GetParameters(); if (!ReferenceEquals(parameters.First().ParameterType, typeof(InteractionContext))) { throw new ArgumentException($"The first argument must be an InteractionContext!"); } parameters = parameters.Skip(1).ToArray(); options = options.Concat(ParseParameters(parameters)).ToList(); var subsubpayload = new DiscordApplicationCommandOption(commatt.Name, commatt.Description, ApplicationCommandOptionType.SubCommand, null, null, suboptions); options.Add(subsubpayload); commandmethods.Add(commatt.Name, subsubmethod); } var subpayload = new DiscordApplicationCommandOption(subgroupatt.Name, subgroupatt.Description, ApplicationCommandOptionType.SubCommandGroup, null, null, options); command.SubCommands.Add(new GroupCommand { Name = subgroupatt.Name, ParentClass = subclass, Methods = commandmethods }); InternalSubGroupCommands = InternalSubGroupCommands.Append(command).ToArray(); payload = new DiscordApplicationCommand(payload.Name, payload.Description, payload.Options != null ? payload.Options.ToArray().Append(subpayload) : new DiscordApplicationCommandOption[] { subpayload }); } ToUpdate.Add(payload); } var methods = ti.DeclaredMethods; foreach (var method in methods.Where(x => x.GetCustomAttribute <SlashCommandAttribute>() != null)) { var commandattribute = method.GetCustomAttribute <SlashCommandAttribute>(); var options = new List <DiscordApplicationCommandOption>(); var parameters = method.GetParameters(); if (parameters.Length == 0 || parameters == null || !ReferenceEquals(parameters.FirstOrDefault()?.ParameterType, typeof(InteractionContext))) { throw new ArgumentException($"The first argument must be an InteractionContext!"); } parameters = parameters.Skip(1).ToArray(); foreach (var parameter in parameters) { var optionattribute = parameter.GetCustomAttribute <OptionAttribute>(); if (optionattribute == null) { throw new ArgumentException("Arguments must have the SlashOption attribute!"); } var type = parameter.ParameterType; ApplicationCommandOptionType parametertype = GetParameterType(type); DiscordApplicationCommandOptionChoice[] choices = GetChoiceAttributesFromParameter(parameter.GetCustomAttributes <ChoiceAttribute>()); options.Add(new DiscordApplicationCommandOption(optionattribute.Name, optionattribute.Description, parametertype, !parameter.IsOptional, choices)); } InternalCommandMethods = InternalCommandMethods.Append(new CommandMethod { Method = method, Name = commandattribute.Name, ParentClass = t }).ToArray(); var payload = new DiscordApplicationCommand(commandattribute.Name, commandattribute.Description, options); ToUpdate.Add(payload); } } catch (Exception ex) { Client.Logger.LogError(ex, $"There was an error registering slash commands"); Environment.Exit(-1); } } try { IEnumerable <DiscordApplicationCommand> commands; if (guildid == null) { commands = await Client.BulkOverwriteGlobalApplicationCommandsAsync(ToUpdate); } else { commands = await Client.BulkOverwriteGuildApplicationCommandsAsync(guildid.Value, ToUpdate); } foreach (var command in commands) { if (InternalCommandMethods.Any(x => x.Name == command.Name)) { InternalCommandMethods.First(x => x.Name == command.Name).Id = command.Id; } else if (InternalGroupCommands.Any(x => x.Name == command.Name)) { InternalGroupCommands.First(x => x.Name == command.Name).Id = command.Id; } else if (InternalSubGroupCommands.Any(x => x.Name == command.Name)) { InternalSubGroupCommands.First(x => x.Name == command.Name).Id = command.Id; } } CommandMethods.AddRange(InternalCommandMethods); GroupCommands.AddRange(InternalGroupCommands); SubGroupCommands.AddRange(InternalSubGroupCommands); } catch (Exception ex) { Client.Logger.LogError(ex, $"There was an error registering slash commands"); Environment.Exit(-1); } }); }
public ApplicationCommandOptionBuilder WithType(ApplicationCommandOptionType type) { Type = type; return(this); }