private void RegisterCommands(Type t, ulong?guildid) { CommandMethod[] InternalCommandMethods = Array.Empty <CommandMethod>(); GroupCommand[] InternalGroupCommands = Array.Empty <GroupCommand>(); SubGroupCommand[] InternalSubGroupCommands = Array.Empty <SubGroupCommand>(); Client.Ready += (s, e) => { _ = Task.Run(async() => { try { List <CommandCreatePayload> ToUpdate = new List <CommandCreatePayload>(); 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 CommandCreatePayload { Name = groupatt.Name, Description = 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 subpayload = new DiscordApplicationCommandOption(commandattribute.Name, commandattribute.Description); 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(); foreach (var parameter in parameters) { var optionattribute = parameter.GetCustomAttribute <OptionAttribute>(); if (optionattribute == null) { throw new ArgumentException("Arguments must have the Option attribute!"); } var type = parameter.ParameterType; ApplicationCommandOptionType parametertype; if (ReferenceEquals(type, typeof(string))) { parametertype = ApplicationCommandOptionType.String; } else if (ReferenceEquals(type, typeof(long))) { parametertype = ApplicationCommandOptionType.Integer; } else if (ReferenceEquals(type, typeof(bool))) { parametertype = ApplicationCommandOptionType.Boolean; } else if (ReferenceEquals(type, typeof(DiscordChannel))) { parametertype = ApplicationCommandOptionType.Channel; } else if (ReferenceEquals(type, typeof(DiscordUser))) { parametertype = ApplicationCommandOptionType.User; } else if (ReferenceEquals(type, typeof(DiscordRole))) { parametertype = ApplicationCommandOptionType.Role; } else { throw new ArgumentException("Cannot convert type! Argument types must be string, long, bool, DiscordChannel, DiscordUser or DiscordRole."); } DiscordApplicationCommandOptionChoice[] choices = null; var choiceattributes = parameter.GetCustomAttributes <ChoiceAttribute>(); if (choiceattributes.Any()) { choices = Array.Empty <DiscordApplicationCommandOptionChoice>(); foreach (var att in choiceattributes) { choices = choices.Append(new DiscordApplicationCommandOptionChoice(att.Name, att.Value)).ToArray(); } } var list = subpayload.Options?.ToList() ?? new List <DiscordApplicationCommandOption>(); list.Add(new DiscordApplicationCommandOption(optionattribute.Name, optionattribute.Description) { Required = !parameter.IsOptional, Type = parametertype, Choices = choices }); subpayload.Options = list; } commandmethods.Add(commandattribute.Name, submethod); payload.Options.Add(new DiscordApplicationCommandOption(commandattribute.Name, commandattribute.Description) { Required = null, Options = subpayload.Options, Type = ApplicationCommandOptionType.SubCommand }); 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 subpayload = new DiscordApplicationCommandOption(subgroupatt.Name, subgroupatt.Description) { Required = null, Type = ApplicationCommandOptionType.SubCommandGroup, Options = new List <DiscordApplicationCommandOption>() }; foreach (var subsubmethod in subsubmethods) { var commatt = subsubmethod.GetCustomAttribute <SlashCommandAttribute>(); var subsubpayload = new DiscordApplicationCommandOption(commatt.Name, commatt.Description) { Type = ApplicationCommandOptionType.SubCommand, }; 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(); foreach (var parameter in parameters) { var optionattribute = parameter.GetCustomAttribute <OptionAttribute>(); if (optionattribute == null) { throw new ArgumentException("Arguments must have the Option attribute!"); } var type = parameter.ParameterType; ApplicationCommandOptionType parametertype; if (ReferenceEquals(type, typeof(string))) { parametertype = ApplicationCommandOptionType.String; } else if (ReferenceEquals(type, typeof(long))) { parametertype = ApplicationCommandOptionType.Integer; } else if (ReferenceEquals(type, typeof(bool))) { parametertype = ApplicationCommandOptionType.Boolean; } else if (ReferenceEquals(type, typeof(DiscordChannel))) { parametertype = ApplicationCommandOptionType.Channel; } else if (ReferenceEquals(type, typeof(DiscordUser))) { parametertype = ApplicationCommandOptionType.User; } else if (ReferenceEquals(type, typeof(DiscordRole))) { parametertype = ApplicationCommandOptionType.Role; } else { throw new ArgumentException("Cannot convert type! Argument types must be string, long, bool, DiscordChannel, DiscordUser or DiscordRole."); } DiscordApplicationCommandOptionChoice[] choices = null; var choiceattributes = parameter.GetCustomAttributes <ChoiceAttribute>(); if (choiceattributes.Any()) { choices = Array.Empty <DiscordApplicationCommandOptionChoice>(); foreach (var att in choiceattributes) { choices = choices.Append(new DiscordApplicationCommandOptionChoice(att.Name, att.Value)).ToArray(); } } var list = subsubpayload.Options?.ToList() ?? new List <DiscordApplicationCommandOption>(); list.Add(new DiscordApplicationCommandOption(optionattribute.Name, optionattribute.Description) { Required = !parameter.IsOptional, Type = parametertype, Choices = choices }); subsubpayload.Options = list; } subpayload.Options = subpayload.Options.ToArray().Append(subsubpayload).ToList(); commandmethods.Add(commatt.Name, subsubmethod); } command.SubCommands.Add(new GroupCommand { Name = subgroupatt.Name, ParentClass = subclass, Methods = commandmethods }); InternalSubGroupCommands = InternalSubGroupCommands.Append(command).ToArray(); payload.Options.Add(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 payload = new CommandCreatePayload { Name = commandattribute.Name, Description = commandattribute.Description }; var parameters = method.GetParameters(); if (!ReferenceEquals(parameters.First().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; if (ReferenceEquals(type, typeof(string))) { parametertype = ApplicationCommandOptionType.String; } else if (ReferenceEquals(type, typeof(long))) { parametertype = ApplicationCommandOptionType.Integer; } else if (ReferenceEquals(type, typeof(bool))) { parametertype = ApplicationCommandOptionType.Boolean; } else if (ReferenceEquals(type, typeof(DiscordChannel))) { parametertype = ApplicationCommandOptionType.Channel; } else if (ReferenceEquals(type, typeof(DiscordUser))) { parametertype = ApplicationCommandOptionType.User; } else if (ReferenceEquals(type, typeof(DiscordRole))) { parametertype = ApplicationCommandOptionType.Role; } else { throw new ArgumentException($"Cannot convert type! Argument types must be string, long, bool, DiscordChannel, DiscordUser or DiscordRole."); } DiscordApplicationCommandOptionChoice[] choices = null; var choiceattributes = parameter.GetCustomAttributes <ChoiceAttribute>(); if (choiceattributes.Any()) { choices = Array.Empty <DiscordApplicationCommandOptionChoice>(); foreach (var att in choiceattributes) { choices = choices.Append(new DiscordApplicationCommandOptionChoice(att.Name, att.Value)).ToArray(); } } payload.Options.Add(new DiscordApplicationCommandOption(optionattribute.Name, optionattribute.Description) { Required = !parameter.IsOptional, Type = parametertype, Choices = choices }); } InternalCommandMethods = InternalCommandMethods.Append(new CommandMethod { Method = method, Name = commandattribute.Name, ParentClass = t }).ToArray(); ToUpdate.Add(payload); } var commands = await BulkCreateCommandsAsync(ToUpdate, guildid); 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); } }); return(Task.CompletedTask); }; }
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); } }); }