diff --git a/DisCatSharp.ApplicationCommands/Workers/ApplicationCommandWorker.cs b/DisCatSharp.ApplicationCommands/Workers/ApplicationCommandWorker.cs index 1f0b7d9a3..dd0ac9756 100644 --- a/DisCatSharp.ApplicationCommands/Workers/ApplicationCommandWorker.cs +++ b/DisCatSharp.ApplicationCommands/Workers/ApplicationCommandWorker.cs @@ -1,383 +1,383 @@ // This file is part of the DisCatSharp project, based off DSharpPlus. // // Copyright (c) 2021-2022 AITSYS // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Threading.Tasks; using DisCatSharp.Entities; using DisCatSharp.Enums; namespace DisCatSharp.ApplicationCommands { /// /// Represents a . /// internal class CommandWorker { /// /// Parses context menu application commands. /// /// The type. /// List of method infos. /// The optional command translations. /// Too much. internal static Task< ( List applicationCommands, List> commandTypeSources, List contextMenuCommands ) > ParseContextMenuCommands(Type type, IEnumerable methods, List translator = null) { List commands = new(); List> commandTypeSources = new(); List contextMenuCommands = new(); foreach (var contextMethod in methods) { var contextAttribute = contextMethod.GetCustomAttribute(); DiscordApplicationCommandLocalization nameLocalizations = null; var commandTranslation = translator?.Single(c => c.Name == contextAttribute.Name && c.Type == contextAttribute.Type); if (commandTranslation != null) nameLocalizations = commandTranslation.NameTranslations; var command = new DiscordApplicationCommand(contextAttribute.Name, null, null, contextAttribute.DefaultPermission, contextAttribute.Type, nameLocalizations); var parameters = contextMethod.GetParameters(); if (parameters.Length == 0 || parameters == null || !ReferenceEquals(parameters.FirstOrDefault()?.ParameterType, typeof(ContextMenuContext))) throw new ArgumentException($"The first argument must be a ContextMenuContext!"); if (parameters.Length > 1) throw new ArgumentException($"A context menu cannot have parameters!"); contextMenuCommands.Add(new ContextMenuCommand { Method = contextMethod, Name = contextAttribute.Name }); commands.Add(command); commandTypeSources.Add(new KeyValuePair(type, type)); } return Task.FromResult((commands, commandTypeSources, contextMenuCommands)); } /// /// Parses single application commands. /// /// The type. /// List of method infos. /// The optional guild id. /// The optional command translations. /// Too much. internal static async Task< ( List applicationCommands, List> commandTypeSources, List commandMethods ) > ParseBasicSlashCommandsAsync(Type type, IEnumerable methods, ulong? guildId = null, List translator = null) { List commands = new(); List> commandTypeSources = new(); List commandMethods = new(); foreach (var method in methods) { var commandattribute = method.GetCustomAttribute(); 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(); var options = await ApplicationCommandsExtension.ParseParametersAsync(parameters, guildId); commandMethods.Add(new CommandMethod { Method = method, Name = commandattribute.Name }); DiscordApplicationCommandLocalization nameLocalizations = null; DiscordApplicationCommandLocalization descriptionLocalizations = null; List localizisedOptions = null; var commandTranslation = translator?.Single(c => c.Name == commandattribute.Name && c.Type == ApplicationCommandType.ChatInput); if (commandTranslation != null && commandTranslation.Options != null) { localizisedOptions = new List(options.Count); foreach (var option in options) { - List choices = option.Choices != null ? new List(option.Choices.Count()) : null; + List choices = option.Choices != null ? new List(option.Choices.Count) : null; if (option.Choices != null) { foreach (var choice in option.Choices) { choices.Add(new DiscordApplicationCommandOptionChoice(choice.Name, choice.Value, commandTranslation.Options.Single(o => o.Name == option.Name).Choices.Single(c => c.Name == choice.Name).NameTranslations)); } } localizisedOptions.Add(new DiscordApplicationCommandOption(option.Name, option.Description, option.Type, option.Required, choices, option.Options, option.ChannelTypes, option.AutoComplete, option.MinimumValue, option.MaximumValue, commandTranslation.Options.Single(o => o.Name == option.Name).NameTranslations, commandTranslation.Options.Single(o => o.Name == option.Name).DescriptionTranslations )); } nameLocalizations = commandTranslation.NameTranslations; descriptionLocalizations = commandTranslation.DescriptionTranslations; } var payload = new DiscordApplicationCommand(commandattribute.Name, commandattribute.Description, localizisedOptions ?? options, commandattribute.DefaultPermission, ApplicationCommandType.ChatInput, nameLocalizations, descriptionLocalizations); commands.Add(payload); commandTypeSources.Add(new KeyValuePair(type, type)); } return (commands, commandTypeSources, commandMethods); } } /// /// Represents a . /// internal class NestedCommandWorker { /// /// Parses application command groups. /// /// The type. /// List of type infos. /// The optional guild id. /// The optional group translations. /// Too much. internal static async Task< ( List applicationCommands, List> commandTypeSources, List singletonModules, List groupCommands, List subGroupCommands ) > ParseSlashGroupsAsync(Type type, List types, ulong? guildId = null, List translator = null) { List commands = new(); List> commandTypeSources = new(); List groupCommands = new(); List subGroupCommands = new(); List singletonModules = new(); //Handles groups foreach (var subclassinfo in types) { //Gets the attribute and methods in the group var groupAttribute = subclassinfo.GetCustomAttribute(); var submethods = subclassinfo.DeclaredMethods.Where(x => x.GetCustomAttribute() != null).ToList(); var subclasses = subclassinfo.DeclaredNestedTypes.Where(x => x.GetCustomAttribute() != null).ToList(); if (subclasses.Any() && submethods.Any()) { throw new ArgumentException("Slash command groups cannot have both subcommands and subgroups!"); } DiscordApplicationCommandLocalization nameLocalizations = null; DiscordApplicationCommandLocalization descriptionLocalizations = null; if (translator != null) { var commandTranslation = translator.Single(c => c.Name == groupAttribute.Name); if (commandTranslation != null) { nameLocalizations = commandTranslation.NameTranslations; descriptionLocalizations = commandTranslation.DescriptionTranslations; } } //Initializes the command var payload = new DiscordApplicationCommand(groupAttribute.Name, groupAttribute.Description, defaultPermission: groupAttribute.DefaultPermission, nameLocalizations: nameLocalizations, descriptionLocalizations: descriptionLocalizations); commandTypeSources.Add(new KeyValuePair(type, type)); var commandmethods = new List>(); //Handles commands in the group foreach (var submethod in submethods) { var commandAttribute = submethod.GetCustomAttribute(); //Gets the paramaters and accounts for InteractionContext var parameters = submethod.GetParameters(); if (parameters.Length == 0 || parameters == null || !ReferenceEquals(parameters.First().ParameterType, typeof(InteractionContext))) throw new ArgumentException($"The first argument must be an InteractionContext!"); parameters = parameters.Skip(1).ToArray(); var options = await ApplicationCommandsExtension.ParseParametersAsync(parameters, guildId); DiscordApplicationCommandLocalization subNameLocalizations = null; DiscordApplicationCommandLocalization subDescriptionLocalizations = null; List localizisedOptions = null; var commandTranslation = translator?.Single(c => c.Name == payload.Name); if (commandTranslation?.Commands != null) { var subCommandTranslation = commandTranslation.Commands.Single(sc => sc.Name == commandAttribute.Name); if (subCommandTranslation.Options != null) { localizisedOptions = new List(options.Count); foreach (var option in options) { - List choices = option.Choices != null ? new List(option.Choices.Count()) : null; + List choices = option.Choices != null ? new List(option.Choices.Count) : null; if (option.Choices != null) { foreach (var choice in option.Choices) { choices.Add(new DiscordApplicationCommandOptionChoice(choice.Name, choice.Value, subCommandTranslation.Options.Single(o => o.Name == option.Name).Choices.Single(c => c.Name == choice.Name).NameTranslations)); } } localizisedOptions.Add(new DiscordApplicationCommandOption(option.Name, option.Description, option.Type, option.Required, choices, option.Options, option.ChannelTypes, option.AutoComplete, option.MinimumValue, option.MaximumValue, subCommandTranslation.Options.Single(o => o.Name == option.Name).NameTranslations, subCommandTranslation.Options.Single(o => o.Name == option.Name).DescriptionTranslations )); } } subNameLocalizations = subCommandTranslation.NameTranslations; subDescriptionLocalizations = subCommandTranslation.DescriptionTranslations; } //Creates the subcommand and adds it to the main command var subpayload = new DiscordApplicationCommandOption(commandAttribute.Name, commandAttribute.Description, ApplicationCommandOptionType.SubCommand, null, null, localizisedOptions ?? options, nameLocalizations: subNameLocalizations, descriptionLocalizations: subDescriptionLocalizations); payload = new DiscordApplicationCommand(payload.Name, payload.Description, payload.Options?.Append(subpayload) ?? new[] { subpayload }, payload.DefaultPermission, nameLocalizations: payload.NameLocalizations, descriptionLocalizations: payload.DescriptionLocalizations); commandTypeSources.Add(new KeyValuePair(subclassinfo, type)); //Adds it to the method lists commandmethods.Add(new KeyValuePair(commandAttribute.Name, submethod)); groupCommands.Add(new GroupCommand { Name = groupAttribute.Name, Methods = commandmethods }); } var command = new SubGroupCommand { Name = groupAttribute.Name }; //Handles subgroups foreach (var subclass in subclasses) { var subGroupAttribute = subclass.GetCustomAttribute(); var subsubmethods = subclass.DeclaredMethods.Where(x => x.GetCustomAttribute() != null); var options = new List(); var currentMethods = new List>(); DiscordApplicationCommandLocalization subNameLocalizations = null; DiscordApplicationCommandLocalization subDescriptionLocalizations = null; if (translator != null) { var commandTranslation = translator.Single(c => c.Name == payload.Name); if (commandTranslation != null && commandTranslation.SubGroups != null) { var subCommandTranslation = commandTranslation.SubGroups.Single(sc => sc.Name == subGroupAttribute.Name); if (subCommandTranslation != null) { subNameLocalizations = subCommandTranslation.NameTranslations; subDescriptionLocalizations = subCommandTranslation.DescriptionTranslations; } } } //Similar to the one for regular groups foreach (var subsubmethod in subsubmethods) { var suboptions = new List(); var commatt = subsubmethod.GetCustomAttribute(); var parameters = subsubmethod.GetParameters(); if (parameters.Length == 0 || parameters == null || !ReferenceEquals(parameters.First().ParameterType, typeof(InteractionContext))) throw new ArgumentException($"The first argument must be an InteractionContext!"); parameters = parameters.Skip(1).ToArray(); suboptions = suboptions.Concat(await ApplicationCommandsExtension.ParseParametersAsync(parameters, guildId)).ToList(); DiscordApplicationCommandLocalization subSubNameLocalizations = null; DiscordApplicationCommandLocalization subSubDescriptionLocalizations = null; List localizisedOptions = null; var commandTranslation = translator?.Single(c => c.Name == payload.Name); var subCommandTranslation = commandTranslation?.SubGroups?.Single(sc => sc.Name == commatt.Name); var subSubCommandTranslation = subCommandTranslation?.Commands.Single(sc => sc.Name == commatt.Name); if (subSubCommandTranslation != null && subSubCommandTranslation.Options != null) { localizisedOptions = new List(suboptions.Count); foreach (var option in suboptions) { List choices = option.Choices != null ? new List(option.Choices.Count) : null; if (option.Choices != null) { foreach (var choice in option.Choices) { choices.Add(new DiscordApplicationCommandOptionChoice(choice.Name, choice.Value, subSubCommandTranslation.Options.Single(o => o.Name == option.Name).Choices.Single(c => c.Name == choice.Name).NameTranslations)); } } localizisedOptions.Add(new DiscordApplicationCommandOption(option.Name, option.Description, option.Type, option.Required, choices, option.Options, option.ChannelTypes, option.AutoComplete, option.MinimumValue, option.MaximumValue, subSubCommandTranslation.Options.Single(o => o.Name == option.Name).NameTranslations, subSubCommandTranslation.Options.Single(o => o.Name == option.Name).DescriptionTranslations )); } subSubNameLocalizations = subSubCommandTranslation.NameTranslations; subSubDescriptionLocalizations = subSubCommandTranslation.DescriptionTranslations; } var subsubpayload = new DiscordApplicationCommandOption(commatt.Name, commatt.Description, ApplicationCommandOptionType.SubCommand, null, null, localizisedOptions ?? suboptions, nameLocalizations: subSubNameLocalizations, descriptionLocalizations: subSubDescriptionLocalizations); options.Add(subsubpayload); commandmethods.Add(new KeyValuePair(commatt.Name, subsubmethod)); currentMethods.Add(new KeyValuePair(commatt.Name, subsubmethod)); } //Adds the group to the command and method lists var subpayload = new DiscordApplicationCommandOption(subGroupAttribute.Name, subGroupAttribute.Description, ApplicationCommandOptionType.SubCommandGroup, null, null, options, nameLocalizations: subNameLocalizations, descriptionLocalizations: subDescriptionLocalizations); command.SubCommands.Add(new GroupCommand { Name = subGroupAttribute.Name, Methods = currentMethods }); payload = new DiscordApplicationCommand(payload.Name, payload.Description, payload.Options?.Append(subpayload) ?? new[] { subpayload }, payload.DefaultPermission, nameLocalizations: payload.NameLocalizations, descriptionLocalizations: payload.DescriptionLocalizations); commandTypeSources.Add(new KeyValuePair(subclass, type)); //Accounts for lifespans for the sub group if (subclass.GetCustomAttribute() != null && subclass.GetCustomAttribute().Lifespan == ApplicationCommandModuleLifespan.Singleton) { singletonModules.Add(ApplicationCommandsExtension.CreateInstance(subclass, ApplicationCommandsExtension.Configuration?.ServiceProvider)); } } if (command.SubCommands.Any()) subGroupCommands.Add(command); commands.Add(payload); //Accounts for lifespans if (subclassinfo.GetCustomAttribute() != null && subclassinfo.GetCustomAttribute().Lifespan == ApplicationCommandModuleLifespan.Singleton) { singletonModules.Add(ApplicationCommandsExtension.CreateInstance(subclassinfo, ApplicationCommandsExtension.Configuration?.ServiceProvider)); } } return (commands, commandTypeSources, singletonModules, groupCommands, subGroupCommands); } } } diff --git a/DisCatSharp.ApplicationCommands/Workers/RegistrationWorker.cs b/DisCatSharp.ApplicationCommands/Workers/RegistrationWorker.cs index db06c8652..f285db9ac 100644 --- a/DisCatSharp.ApplicationCommands/Workers/RegistrationWorker.cs +++ b/DisCatSharp.ApplicationCommands/Workers/RegistrationWorker.cs @@ -1,361 +1,361 @@ // This file is part of the DisCatSharp project, based off DSharpPlus. // // Copyright (c) 2021-2022 AITSYS // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using DisCatSharp.Common; using DisCatSharp.Entities; using DisCatSharp.Exceptions; using Microsoft.Extensions.Logging; namespace DisCatSharp.ApplicationCommands { /// /// Represents a . /// internal class RegistrationWorker { /// /// Registers the global commands. /// /// The command list. /// A list of registered commands. internal static async Task> RegisterGlobalCommandsAsync(List commands) { var globalCommandsOverwriteList = BuildGlobalOverwriteList(commands); var globalCommandsCreateList = BuildGlobalCreateList(commands); var globalCommandsDeleteList = BuildGlobalDeleteList(commands); if (globalCommandsCreateList.NotEmptyAndNotNull() && globalCommandsOverwriteList.EmptyOrNull()) { var cmds = await ApplicationCommandsExtension.ClientInternal.BulkOverwriteGlobalApplicationCommandsAsync(globalCommandsCreateList); commands.AddRange(cmds); } else if (globalCommandsCreateList.EmptyOrNull() && globalCommandsOverwriteList.NotEmptyAndNotNull()) { List overwriteList = new(); foreach (var overwrite in globalCommandsOverwriteList) { var cmd = overwrite.Value; cmd.Id = overwrite.Key; overwriteList.Add(cmd); } var discordBackendCommands = await ApplicationCommandsExtension.ClientInternal.BulkOverwriteGlobalApplicationCommandsAsync(overwriteList); commands.AddRange(discordBackendCommands); } else if (globalCommandsCreateList.NotEmptyAndNotNull() && globalCommandsOverwriteList.NotEmptyAndNotNull()) { foreach (var cmd in globalCommandsCreateList) { var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.CreateGlobalApplicationCommandAsync(cmd); commands.Add(discordBackendCommand); } foreach (var cmd in globalCommandsOverwriteList) { var command = cmd.Value; var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.EditGlobalApplicationCommandAsync(cmd.Key, action => { action.Name = command.Name; action.NameLocalizations = command.NameLocalizations; action.Description = command.Description; action.DescriptionLocalizations = command.DescriptionLocalizations; if(command.Options != null && command.Options.Any()) action.Options = Entities.Optional.FromValue(command.Options); action.DefaultPermission = command.DefaultPermission; }); commands.Add(discordBackendCommand); } } if (globalCommandsDeleteList.NotEmptyAndNotNull()) { foreach (var cmdId in globalCommandsDeleteList) { try { await ApplicationCommandsExtension.ClientInternal.DeleteGlobalApplicationCommandAsync(cmdId); } catch (NotFoundException) { ApplicationCommandsExtension.ClientInternal.Logger.LogError($"Could not delete global command {cmdId}. Please clean up manually"); } } } return commands.NotEmptyAndNotNull() ? commands : null; } /// /// Registers the guild commands. /// /// The target guild id. /// The command list. /// A list of registered commands. internal static async Task> RegisterGuilldCommandsAsync(ulong guildId, List commands) { var guildCommandsOverwriteList = BuildGuildOverwriteList(guildId, commands); var guildCommandsCreateList = BuildGuildCreateList(guildId, commands); var guildCommandsDeleteList = BuildGuildDeleteList(guildId, commands); if (guildCommandsCreateList.NotEmptyAndNotNull() && guildCommandsOverwriteList.EmptyOrNull()) { var cmds = await ApplicationCommandsExtension.ClientInternal.BulkOverwriteGuildApplicationCommandsAsync(guildId, guildCommandsCreateList); commands.AddRange(cmds); } else if (guildCommandsCreateList.EmptyOrNull() && guildCommandsOverwriteList.NotEmptyAndNotNull()) { List overwriteList = new(); foreach (var overwrite in guildCommandsOverwriteList) { var cmd = overwrite.Value; cmd.Id = overwrite.Key; overwriteList.Add(cmd); } var discordBackendCommands = await ApplicationCommandsExtension.ClientInternal.BulkOverwriteGuildApplicationCommandsAsync(guildId, overwriteList); commands.AddRange(discordBackendCommands); } else if (guildCommandsCreateList.NotEmptyAndNotNull() && guildCommandsOverwriteList.NotEmptyAndNotNull()) { foreach (var cmd in guildCommandsCreateList) { var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.CreateGuildApplicationCommandAsync(guildId, cmd); commands.Add(discordBackendCommand); } foreach (var cmd in guildCommandsOverwriteList) { var command = cmd.Value; var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.EditGuildApplicationCommandAsync(guildId, cmd.Key, action => { action.Name = command.Name; action.NameLocalizations = command.NameLocalizations; action.Description = command.Description; action.DescriptionLocalizations = command.DescriptionLocalizations; if(command.Options != null && command.Options.Any()) action.Options = Entities.Optional.FromValue(command.Options); action.DefaultPermission = command.DefaultPermission; }); commands.Add(discordBackendCommand); } } if (guildCommandsDeleteList.NotEmptyAndNotNull()) { foreach (var cmdId in guildCommandsDeleteList) { try { await ApplicationCommandsExtension.ClientInternal.DeleteGuildApplicationCommandAsync(guildId, cmdId); } catch (NotFoundException) { ApplicationCommandsExtension.ClientInternal.Logger.LogError($"Could not delete guild command {cmdId} in guild {guildId}. Please clean up manually"); } } } return commands.NotEmptyAndNotNull() ? commands : null; } /// /// Builds a list of guild command ids to be deleted on discords backend. /// /// The guild id these commands belong to. /// The command list. /// A list of command ids. private static List BuildGuildDeleteList(ulong guildId, List updateList) { List discord; if (ApplicationCommandsExtension.GuildDiscordCommands == null || !ApplicationCommandsExtension.GuildDiscordCommands.Any() || !ApplicationCommandsExtension.GuildDiscordCommands.GetFirstValueByKey(guildId, out discord) ) return null; List invalidCommandIds = new(); if (updateList == null) { foreach (var cmd in discord) { invalidCommandIds.Add(cmd.Id); } } else { foreach (var cmd in discord) { if (!updateList.Any(ul => ul.Name == cmd.Name)) invalidCommandIds.Add(cmd.Id); } } return invalidCommandIds; } /// /// Builds a list of guild commands to be created on discords backend. /// /// The guild id these commands belong to. /// The command list. /// private static List BuildGuildCreateList(ulong guildId, List updateList) { List discord; if (ApplicationCommandsExtension.GuildDiscordCommands == null || !ApplicationCommandsExtension.GuildDiscordCommands.Any() || updateList == null || !ApplicationCommandsExtension.GuildDiscordCommands.GetFirstValueByKey(guildId, out discord) ) return updateList; List newCommands = new(); foreach (var cmd in updateList) { if (!discord.Any(d => d.Name == cmd.Name)) { newCommands.Add(cmd); } } return newCommands; } /// /// Builds a list of guild commands to be overwritten on discords backend. /// /// The guild id these commands belong to. /// The command list. /// A dictionary of command id and command. private static Dictionary BuildGuildOverwriteList(ulong guildId, List updateList) { List discord; if (ApplicationCommandsExtension.GuildDiscordCommands == null || !ApplicationCommandsExtension.GuildDiscordCommands.Any() - || !ApplicationCommandsExtension.GuildDiscordCommands.Any(l => l.Key == guildId) || updateList == null + || ApplicationCommandsExtension.GuildDiscordCommands.All(l => l.Key != guildId) || updateList == null || !ApplicationCommandsExtension.GuildDiscordCommands.GetFirstValueByKey(guildId, out discord) ) return null; Dictionary updateCommands = new(); foreach (var cmd in updateList) { if (discord.GetFirstValueWhere(d => d.Name == cmd.Name, out var command)) updateCommands.Add(command.Id, cmd); } return updateCommands; } /// /// Builds a list of global command ids to be deleted on discords backend. /// /// The command list. /// A list of command ids. private static List BuildGlobalDeleteList(List updateList = null) { if (ApplicationCommandsExtension.GlobalDiscordCommands == null || !ApplicationCommandsExtension.GlobalDiscordCommands.Any() || ApplicationCommandsExtension.GlobalDiscordCommands == null ) return null; var discord = ApplicationCommandsExtension.GlobalDiscordCommands; List invalidCommandIds = new(); if (updateList == null) { foreach (var cmd in discord) { invalidCommandIds.Add(cmd.Id); } } else { foreach (var cmd in discord) { - if (!updateList.Any(ul => ul.Name == cmd.Name)) + if (updateList.All(ul => ul.Name != cmd.Name)) invalidCommandIds.Add(cmd.Id); } } return invalidCommandIds; } /// /// Builds a list of global commands to be created on discords backend. /// /// The command list. /// A list of commands. private static List BuildGlobalCreateList(List updateList) { if (ApplicationCommandsExtension.GlobalDiscordCommands == null || !ApplicationCommandsExtension.GlobalDiscordCommands.Any() || updateList == null) return updateList; var discord = ApplicationCommandsExtension.GlobalDiscordCommands; List newCommands = new(); foreach (var cmd in updateList) { if (discord.Any(d => d.Name == cmd.Name)) { newCommands.Add(cmd); } } return newCommands; } /// /// Builds a list of global commands to be overwritten on discords backend. /// /// The command list. /// A dictionary of command ids and commands. private static Dictionary BuildGlobalOverwriteList(List updateList) { if (ApplicationCommandsExtension.GlobalDiscordCommands == null || !ApplicationCommandsExtension.GlobalDiscordCommands.Any() || updateList == null || ApplicationCommandsExtension.GlobalDiscordCommands == null ) return null; var discord = ApplicationCommandsExtension.GlobalDiscordCommands; Dictionary updateCommands = new(); foreach (var cmd in updateList) { if (discord.GetFirstValueWhere(d => d.Name == cmd.Name, out var command)) updateCommands.Add(command.Id, cmd); } return updateCommands; } } }