From 0a3b8ef1b25c4df1272284442941b9fd09e4781c Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 29 Jul 2023 00:11:54 +0200 Subject: [PATCH] aaaaaaaaaaa x2 & brainfuck --- .../ApplicationCommandsExtension.cs | 18 +- .../ApplicationCommandEqualityChecks.cs | 173 +++++------------ .../Workers/ApplicationCommandWorker.cs | 36 ++-- .../Workers/RegistrationWorker.cs | 178 +++++++----------- .../Application/DiscordApplicationCommand.cs | 21 ++- .../DiscordApplicationCommandOption.cs | 14 +- 6 files changed, 155 insertions(+), 285 deletions(-) diff --git a/DisCatSharp.ApplicationCommands/ApplicationCommandsExtension.cs b/DisCatSharp.ApplicationCommands/ApplicationCommandsExtension.cs index c7a97f9c27..2c2f97fa98 100644 --- a/DisCatSharp.ApplicationCommands/ApplicationCommandsExtension.cs +++ b/DisCatSharp.ApplicationCommands/ApplicationCommandsExtension.cs @@ -77,12 +77,12 @@ public sealed class ApplicationCommandsExtension : BaseExtension /// /// List of global commands on discords backend. /// - internal static List GlobalDiscordCommands { get; set; } + internal static List? GlobalDiscordCommands { get; set; } /// /// List of guild commands on discords backend. /// - internal static Dictionary> GuildDiscordCommands { get; set; } + internal static Dictionary>? GuildDiscordCommands { get; set; } /// /// Singleton modules. @@ -111,21 +111,21 @@ public sealed class ApplicationCommandsExtension : BaseExtension => s_registeredCommands.Select(guild => new KeyValuePair>(guild.Key, guild.Value .Select(parent => new RegisteredDiscordApplicationCommand(parent)).ToList())).ToList(); - private static List>> s_registeredCommands = new(); + private static readonly List>> s_registeredCommands = new(); /// /// Gets a list of registered global commands. /// public IReadOnlyList GlobalCommands => GlobalCommandsInternal; - internal static List GlobalCommandsInternal = new(); + internal static readonly List GlobalCommandsInternal = new(); /// /// Gets a list of registered guild commands mapped by guild id. /// public IReadOnlyDictionary> GuildCommands => GuildCommandsInternal; - internal static Dictionary> GuildCommandsInternal = new(); + internal static readonly Dictionary> GuildCommandsInternal = new(); /// /// Gets the registration count. @@ -163,7 +163,7 @@ internal static LogLevel ApplicationCommandsLogLevel internal static bool ManOr { get; set; } /// - /// Gets whether interactions should be automatically deffered. + /// Gets whether interactions should be automatically deferred. /// internal static bool AutoDeferEnabled { get; set; } @@ -182,7 +182,7 @@ public IServiceProvider? Services /// /// Gets a list of handled interactions. Fix for double interaction execution bug. /// - internal static List HandledInteractions = new(); + internal static readonly List HandledInteractions = new(); /// /// Initializes a new instance of the class. @@ -863,7 +863,7 @@ private async void CheckRegistrationStartup(bool man = false, IReadOnlyCollectio await fs.DisposeAsync().ConfigureAwait(false); ms.Close(); await ms.DisposeAsync().ConfigureAwait(false); - this.Client.Logger.LogInformation("Exported base translation to {exppath}", fileName); + this.Client.Logger.LogInformation("Exported base translation to {exportPath}", fileName); } if (groupTranslation != null && groupTranslation.Any()) @@ -881,7 +881,7 @@ private async void CheckRegistrationStartup(bool man = false, IReadOnlyCollectio await fs.DisposeAsync().ConfigureAwait(false); ms.Close(); await ms.DisposeAsync().ConfigureAwait(false); - this.Client.Logger.LogInformation("Exported base translation to {exppath}", fileName); + this.Client.Logger.LogInformation("Exported base translation to {exportPath}", fileName); } } catch (Exception ex) diff --git a/DisCatSharp.ApplicationCommands/Checks/ApplicationCommandEqualityChecks.cs b/DisCatSharp.ApplicationCommands/Checks/ApplicationCommandEqualityChecks.cs index c742fd603a..6b782541fb 100644 --- a/DisCatSharp.ApplicationCommands/Checks/ApplicationCommandEqualityChecks.cs +++ b/DisCatSharp.ApplicationCommands/Checks/ApplicationCommandEqualityChecks.cs @@ -41,7 +41,7 @@ internal static class ApplicationCommandEqualityChecks /// Command to check against. /// The discord client. /// Whether the equal check is performed for a guild command. - internal static bool IsEqualTo(this DiscordApplicationCommand ac1, DiscordApplicationCommand targetApplicationCommand, DiscordClient client, bool isGuild) + internal static bool IsEqualTo(this DiscordApplicationCommand? ac1, DiscordApplicationCommand? targetApplicationCommand, DiscordClient client, bool isGuild) { if (targetApplicationCommand is null || ac1 is null) return false; @@ -80,11 +80,26 @@ internal static bool SoftEqual(this DiscordApplicationCommand source, DiscordApp { bool? sDmPerm = source.DmPermission ?? true; bool? tDmPerm = target.DmPermission ?? true; - if (guild) - { - sDmPerm = null; - tDmPerm = null; - } + if (!guild) + return localizationEnabled + ? type switch + { + ApplicationCommandType.ChatInput => DeepEqual(source, target, true, sDmPerm, tDmPerm), + _ => source.Name == target.Name + && source.Type == target.Type && source.NameLocalizations == target.NameLocalizations + && source.DefaultMemberPermissions == target.DefaultMemberPermissions + && sDmPerm == tDmPerm && source.IsNsfw == target.IsNsfw + } + : type switch + { + ApplicationCommandType.ChatInput => DeepEqual(source, target, false, sDmPerm, tDmPerm), + _ => source.Name == target.Name + && source.Type == target.Type + && source.DefaultMemberPermissions == target.DefaultMemberPermissions + && sDmPerm == tDmPerm && source.IsNsfw == target.IsNsfw + }; + sDmPerm = null; + tDmPerm = null; return localizationEnabled ? type switch { @@ -135,45 +150,19 @@ internal static bool DeepEqual(DiscordApplicationCommand source, DiscordApplicat target.Options.Any(o => o.Type == ApplicationCommandOptionType.SubCommandGroup)) { List minimalSourceOptions = new(); - List minimalTargetOptions = new(); foreach (var option in - source.Options.Where(x => x.Type == ApplicationCommandOptionType.SubCommandGroup)) + source.Options.Where(x => x.Type == ApplicationCommandOptionType.SubCommandGroup)) { List minimalSubSourceOptions = new(); if (option.Options != null) { - foreach (var subOption in option.Options) - { - List minimalSubSubSourceOptions = null; - - if (subOption.Options != null) - { - minimalSubSubSourceOptions = new(); - - foreach (var subSubOption in subOption.Options) - minimalSubSubSourceOptions.Add(new( - subSubOption.Name, subSubOption.Description, subSubOption.Type, - subSubOption.Required, - subSubOption.Choices, null, subSubOption.ChannelTypes?.OrderBy(x => x), - subSubOption.AutoComplete, - subSubOption.MinimumValue, subSubOption.MaximumValue, - localizationEnabled ? subSubOption.NameLocalizations : null, - localizationEnabled ? subSubOption.DescriptionLocalizations : null, - subSubOption.MinimumLength, subSubOption.MaximumLength - )); - - minimalSubSourceOptions.Add(new( - subOption.Name, subOption.Description, subOption.Type, - options: minimalSubSubSourceOptions, - nameLocalizations: localizationEnabled ? subOption.NameLocalizations : null, - descriptionLocalizations: localizationEnabled - ? subOption.DescriptionLocalizations - : null - )); - } - } - + minimalSubSourceOptions.AddRange(from subOption in option.Options + where subOption.Options != null + let minimalSubSubSourceOptions = subOption.Options.Select(subSubOption => new DiscordApplicationCommandOption(subSubOption.Name, subSubOption.Description, subSubOption.Type, subSubOption.Required, subSubOption.Choices, null, subSubOption.ChannelTypes?.OrderBy(x => x), subSubOption.AutoComplete, subSubOption.MinimumValue, subSubOption.MaximumValue, localizationEnabled ? subSubOption.NameLocalizations : null, localizationEnabled ? subSubOption.DescriptionLocalizations : null, subSubOption.MinimumLength, subSubOption.MaximumLength)).ToList() + select new DiscordApplicationCommandOption(subOption.Name, subOption.Description, subOption.Type, options: minimalSubSubSourceOptions, nameLocalizations: localizationEnabled ? subOption.NameLocalizations : null, descriptionLocalizations: localizationEnabled + ? subOption.DescriptionLocalizations + : null)); } minimalSourceOptions.Add(new( @@ -184,48 +173,14 @@ internal static bool DeepEqual(DiscordApplicationCommand source, DiscordApplicat )); } - foreach (var option in - target.Options.Where(x => x.Type == ApplicationCommandOptionType.SubCommandGroup)) - { - List minimalSubTargetOptions = new(); - - foreach (var subOption in option.Options) - { - List minimalSubSubTargetOptions = null; - - if (subOption.Options != null && subOption.Options.Any()) - { - minimalSubSubTargetOptions = new(); - - foreach (var subSubOption in subOption.Options) - minimalSubSubTargetOptions.Add(new( - subSubOption.Name, subSubOption.Description, subSubOption.Type, - subSubOption.Required, - subSubOption.Choices, null, subSubOption.ChannelTypes?.OrderBy(x => x), subSubOption.AutoComplete, - subSubOption.MinimumValue, subSubOption.MaximumValue, - localizationEnabled ? subSubOption.NameLocalizations : null, - localizationEnabled ? subSubOption.DescriptionLocalizations : null, - subSubOption.MinimumLength, subSubOption.MaximumLength - )); - - minimalSubTargetOptions.Add(new( - subOption.Name, subOption.Description, subOption.Type, - options: minimalSubSubTargetOptions, - nameLocalizations: localizationEnabled ? subOption.NameLocalizations : null, - descriptionLocalizations: localizationEnabled - ? subOption.DescriptionLocalizations - : null - )); - } - } - - minimalTargetOptions.Add(new( - option.Name, option.Description, option.Type, - options: minimalSubTargetOptions, - nameLocalizations: localizationEnabled ? option.NameLocalizations : null, - descriptionLocalizations: localizationEnabled ? option.DescriptionLocalizations : null - )); - } + var minimalTargetOptions = (from option in target.Options.Where(x => x.Type == ApplicationCommandOptionType.SubCommandGroup) + let minimalSubTargetOptions = (from subOption in option.Options + where subOption.Options != null && subOption.Options.Any() + let minimalSubSubTargetOptions = subOption.Options.Select(subSubOption => new DiscordApplicationCommandOption(subSubOption.Name, subSubOption.Description, subSubOption.Type, subSubOption.Required, subSubOption.Choices, null, subSubOption.ChannelTypes?.OrderBy(x => x), subSubOption.AutoComplete, subSubOption.MinimumValue, subSubOption.MaximumValue, localizationEnabled ? subSubOption.NameLocalizations : null, localizationEnabled ? subSubOption.DescriptionLocalizations : null, subSubOption.MinimumLength, subSubOption.MaximumLength)).ToList() + select new DiscordApplicationCommandOption(subOption.Name, subOption.Description, subOption.Type, options: minimalSubSubTargetOptions, nameLocalizations: localizationEnabled ? subOption.NameLocalizations : null, descriptionLocalizations: localizationEnabled + ? subOption.DescriptionLocalizations + : null)).ToList() + select new DiscordApplicationCommandOption(option.Name, option.Description, option.Type, options: minimalSubTargetOptions, nameLocalizations: localizationEnabled ? option.NameLocalizations : null, descriptionLocalizations: localizationEnabled ? option.DescriptionLocalizations : null)).ToList(); var sOpt = JsonConvert.SerializeObject(minimalSourceOptions, Formatting.None); var tOpt = JsonConvert.SerializeObject(minimalTargetOptions, Formatting.None); @@ -233,8 +188,8 @@ internal static bool DeepEqual(DiscordApplicationCommand source, DiscordApplicat eqCheck1 = rootCheck && sOpt == tOpt; } - if (source.Options.Any(o => o.Type == ApplicationCommandOptionType.SubCommand) && - target.Options.Any(o => o.Type == ApplicationCommandOptionType.SubCommand)) + if (source.Options.All(o => o.Type != ApplicationCommandOptionType.SubCommand) || target.Options.All(o => o.Type != ApplicationCommandOptionType.SubCommand)) + return eqCheck1 && eqCheck2; { List minimalSourceOptions = new(); List minimalTargetOptions = new(); @@ -244,19 +199,7 @@ internal static bool DeepEqual(DiscordApplicationCommand source, DiscordApplicat List minimalSubSourceOptions = null; if (option.Options != null) - { - minimalSubSourceOptions = new(); - - foreach (var subOption in option.Options) - minimalSubSourceOptions.Add(new( - subOption.Name, subOption.Description, subOption.Type, subOption.Required, - subOption.Choices, null, subOption.ChannelTypes?.OrderBy(x => x), subOption.AutoComplete, - subOption.MinimumValue, subOption.MaximumValue, - localizationEnabled ? subOption.NameLocalizations : null, - localizationEnabled ? subOption.DescriptionLocalizations : null, - subOption.MinimumLength, subOption.MaximumLength - )); - } + minimalSubSourceOptions = option.Options.Select(subOption => new DiscordApplicationCommandOption(subOption.Name, subOption.Description, subOption.Type, subOption.Required, subOption.Choices, null, subOption.ChannelTypes?.OrderBy(x => x), subOption.AutoComplete, subOption.MinimumValue, subOption.MaximumValue, localizationEnabled ? subOption.NameLocalizations : null, localizationEnabled ? subOption.DescriptionLocalizations : null, subOption.MinimumLength, subOption.MaximumLength)).ToList(); minimalSourceOptions.Add(new( option.Name, option.Description, option.Type, @@ -271,19 +214,7 @@ internal static bool DeepEqual(DiscordApplicationCommand source, DiscordApplicat List minimalSubTargetOptions = null; if (option.Options != null && option.Options.Any()) - { - minimalSubTargetOptions = new(); - - foreach (var subOption in option.Options) - minimalSubTargetOptions.Add(new( - subOption.Name, subOption.Description, subOption.Type, subOption.Required, - subOption.Choices, null, subOption.ChannelTypes?.OrderBy(x => x), subOption.AutoComplete, - subOption.MinimumValue, subOption.MaximumValue, - localizationEnabled ? subOption.NameLocalizations : null, - localizationEnabled ? subOption.DescriptionLocalizations : null, - subOption.MinimumLength, subOption.MaximumLength - )); - } + minimalSubTargetOptions = option.Options.Select(subOption => new DiscordApplicationCommandOption(subOption.Name, subOption.Description, subOption.Type, subOption.Required, subOption.Choices, null, subOption.ChannelTypes?.OrderBy(x => x), subOption.AutoComplete, subOption.MinimumValue, subOption.MaximumValue, localizationEnabled ? subOption.NameLocalizations : null, localizationEnabled ? subOption.DescriptionLocalizations : null, subOption.MinimumLength, subOption.MaximumLength)).ToList(); minimalTargetOptions.Add(new( option.Name, option.Description, option.Type, @@ -301,28 +232,12 @@ internal static bool DeepEqual(DiscordApplicationCommand source, DiscordApplicat return eqCheck1 && eqCheck2; } + // ReSharper disable once RedundantIfElseBlock else { - List minimalSourceOptions = new(); - List minimalTargetOptions = new(); - - foreach (var option in source.Options) - minimalSourceOptions.Add(new( - option.Name, option.Description, option.Type, option.Required, - option.Choices, null, option.ChannelTypes?.OrderBy(x => x), option.AutoComplete, option.MinimumValue, option.MaximumValue, - localizationEnabled ? option.NameLocalizations : null, - localizationEnabled ? option.DescriptionLocalizations : null, - option.MinimumLength, option.MaximumLength - )); - - foreach (var option in target.Options) - minimalTargetOptions.Add(new( - option.Name, option.Description, option.Type, option.Required, - option.Choices, null, option.ChannelTypes?.OrderBy(x => x), option.AutoComplete, option.MinimumValue, option.MaximumValue, - localizationEnabled ? option.NameLocalizations : null, - localizationEnabled ? option.DescriptionLocalizations : null, - option.MinimumLength, option.MaximumLength - )); + var minimalSourceOptions = source.Options.Select(option => new DiscordApplicationCommandOption(option.Name, option.Description, option.Type, option.Required, option.Choices, null, option.ChannelTypes?.OrderBy(x => x), option.AutoComplete, option.MinimumValue, option.MaximumValue, localizationEnabled ? option.NameLocalizations : null, localizationEnabled ? option.DescriptionLocalizations : null, option.MinimumLength, option.MaximumLength)).ToList(); + + var minimalTargetOptions = target.Options.Select(option => new DiscordApplicationCommandOption(option.Name, option.Description, option.Type, option.Required, option.Choices, null, option.ChannelTypes?.OrderBy(x => x), option.AutoComplete, option.MinimumValue, option.MaximumValue, localizationEnabled ? option.NameLocalizations : null, localizationEnabled ? option.DescriptionLocalizations : null, option.MinimumLength, option.MaximumLength)).ToList(); var sOpt = JsonConvert.SerializeObject(minimalSourceOptions, Formatting.None); var tOpt = JsonConvert.SerializeObject(minimalTargetOptions, Formatting.None); diff --git a/DisCatSharp.ApplicationCommands/Workers/ApplicationCommandWorker.cs b/DisCatSharp.ApplicationCommands/Workers/ApplicationCommandWorker.cs index 4b5618c3b7..4f9601175a 100644 --- a/DisCatSharp.ApplicationCommands/Workers/ApplicationCommandWorker.cs +++ b/DisCatSharp.ApplicationCommands/Workers/ApplicationCommandWorker.cs @@ -38,7 +38,7 @@ namespace DisCatSharp.ApplicationCommands.Workers; /// /// Represents a . /// -internal class CommandWorker +internal static class CommandWorker { /// /// Parses context menu application commands. @@ -54,7 +54,7 @@ internal static Task< List contextMenuCommands, bool withLocalization ) - > ParseContextMenuCommands(Type type, IEnumerable methods, List translator = null) + > ParseContextMenuCommands(Type type, IEnumerable methods, List? translator = null) { List commands = new(); List> commandTypeSources = new(); @@ -103,7 +103,7 @@ internal static async Task< List commandMethods, bool withLocalization ) - > ParseBasicSlashCommandsAsync(Type type, IEnumerable methods, ulong? guildId = null, List translator = null) + > ParseBasicSlashCommandsAsync(Type type, IEnumerable methods, ulong? guildId = null, List? translator = null) { List commands = new(); List> commandTypeSources = new(); @@ -128,7 +128,7 @@ bool withLocalization var commandTranslation = translator?.Single(c => c.Name == commandAttribute.Name && c.Type == ApplicationCommandType.ChatInput); - if (commandTranslation != null && commandTranslation.Options != null) + if (commandTranslation is { Options: not null }) { localizedOptions = new(options.Count); foreach (var option in options) @@ -180,7 +180,7 @@ bool withLocalization /// /// Represents a . /// -internal class NestedCommandWorker +internal static class NestedCommandWorker { /// /// Parses application command groups. @@ -199,7 +199,7 @@ internal static async Task< List subGroupCommands, bool withLocalization ) - > ParseSlashGroupsAsync(Type type, List types, ulong? guildId = null, List translator = null) + > ParseSlashGroupsAsync(Type type, List types, ulong? guildId = null, List? translator = null) { List commands = new(); List> commandTypeSources = new(); @@ -253,7 +253,6 @@ bool withLocalization if (commandTranslation?.Commands != null) { - var subCommandTranslation = commandTranslation.Commands.Single(sc => sc.Name == commandAttribute.Name); if (subCommandTranslation.Options != null) { @@ -262,8 +261,7 @@ bool withLocalization { var choices = option.Choices != null ? new List(option.Choices.Count) : null; if (option.Choices != null) - foreach (var choice in option.Choices) - choices.Add(new(choice.Name, choice.Value, subCommandTranslation.Options.Single(o => o.Name == option.Name).Choices.Single(c => c.Name == choice.Name).NameTranslations)); + choices.AddRange(option.Choices.Select(choice => new DiscordApplicationCommandOptionChoice(choice.Name, choice.Value, subCommandTranslation.Options.Single(o => o.Name == option.Name).Choices.Single(c => c.Name == choice.Name).NameTranslations))); localizedOptions.Add(new(option.Name, option.Description, option.Type, option.Required, choices, option.Options, option.ChannelTypes, option.AutoComplete, option.MinimumValue, option.MaximumValue, @@ -306,9 +304,8 @@ bool withLocalization if (translator != null) { var commandTranslation = translator.Single(c => c.Name == payload.Name); - if (commandTranslation != null && commandTranslation.SubGroups != null) + if (commandTranslation is { SubGroups: not null }) { - var subCommandTranslation = commandTranslation.SubGroups.Single(sc => sc.Name == subgroupAttribute.Name); if (subCommandTranslation != null) @@ -323,7 +320,7 @@ bool withLocalization foreach (var subsubmethod in subsubmethods) { var suboptions = new List(); - var commatt = subsubmethod.GetCustomAttribute(); + var slashCommandAttribute = subsubmethod.GetCustomAttribute(); var parameters = subsubmethod.GetParameters(); if (parameters.Length == 0 || parameters == null || !ReferenceEquals(parameters.First().ParameterType, typeof(InteractionContext))) throw new ArgumentException($"The first argument of the command '{subgroupAttribute.Name}' has to be an InteractionContext!"); @@ -336,19 +333,18 @@ bool withLocalization var commandTranslation = translator?.Single(c => c.Name == payload.Name); - var subCommandTranslation = commandTranslation?.SubGroups?.Single(sc => sc.Name == commatt.Name); + var subCommandTranslation = commandTranslation?.SubGroups?.Single(sc => sc.Name == slashCommandAttribute.Name); - var subSubCommandTranslation = subCommandTranslation?.Commands.Single(sc => sc.Name == commatt.Name); + var subSubCommandTranslation = subCommandTranslation?.Commands.Single(sc => sc.Name == slashCommandAttribute.Name); - if (subSubCommandTranslation != null && subSubCommandTranslation.Options != null) + if (subSubCommandTranslation is { Options: not null }) { localizedOptions = new(suboptions.Count); foreach (var option in suboptions) { var choices = option.Choices != null ? new List(option.Choices.Count) : null; if (option.Choices != null) - foreach (var choice in option.Choices) - choices.Add(new(choice.Name, choice.Value, subSubCommandTranslation.Options.Single(o => o.Name == option.Name).Choices.Single(c => c.Name == choice.Name).NameTranslations)); + choices.AddRange(option.Choices.Select(choice => new DiscordApplicationCommandOptionChoice(choice.Name, choice.Value, subSubCommandTranslation.Options.Single(o => o.Name == option.Name).Choices.Single(c => c.Name == choice.Name).NameTranslations))); localizedOptions.Add(new(option.Name, option.Description, option.Type, option.Required, choices, option.Options, option.ChannelTypes, option.AutoComplete, option.MinimumValue, option.MaximumValue, @@ -361,10 +357,10 @@ bool withLocalization subSubDescriptionLocalizations = subSubCommandTranslation.DescriptionTranslations; } - var subsubpayload = new DiscordApplicationCommandOption(commatt.Name, commatt.Description, ApplicationCommandOptionType.SubCommand, false, null, (localizedOptions != null && localizedOptions.Any() ? localizedOptions : null) ?? (suboptions != null && suboptions.Any() ? suboptions : null), nameLocalizations: subSubNameLocalizations, descriptionLocalizations: subSubDescriptionLocalizations); + var subsubpayload = new DiscordApplicationCommandOption(slashCommandAttribute.Name, slashCommandAttribute.Description, ApplicationCommandOptionType.SubCommand, false, null, (localizedOptions != null && localizedOptions.Any() ? localizedOptions : null) ?? (suboptions != null && suboptions.Any() ? suboptions : null), nameLocalizations: subSubNameLocalizations, descriptionLocalizations: subSubDescriptionLocalizations); options.Add(subsubpayload); - commandMethods.Add(new(commatt.Name, subsubmethod)); - currentMethods.Add(new(commatt.Name, subsubmethod)); + commandMethods.Add(new(slashCommandAttribute.Name, subsubmethod)); + currentMethods.Add(new(slashCommandAttribute.Name, subsubmethod)); } //Adds the group to the command and method lists diff --git a/DisCatSharp.ApplicationCommands/Workers/RegistrationWorker.cs b/DisCatSharp.ApplicationCommands/Workers/RegistrationWorker.cs index 1ae1b5ff4a..28f838af89 100644 --- a/DisCatSharp.ApplicationCommands/Workers/RegistrationWorker.cs +++ b/DisCatSharp.ApplicationCommands/Workers/RegistrationWorker.cs @@ -37,7 +37,7 @@ namespace DisCatSharp.ApplicationCommands.Workers; /// /// Represents a . /// -internal class RegistrationWorker +internal static class RegistrationWorker { /// /// Registers the global commands. @@ -45,11 +45,11 @@ internal class RegistrationWorker /// The discord client. /// The command list. /// A list of registered commands. - internal static async Task> RegisterGlobalCommandsAsync(DiscordClient client, List commands) + internal static async Task?> RegisterGlobalCommandsAsync(DiscordClient client, List commands) { var (changedCommands, unchangedCommands) = BuildGlobalOverwriteList(client, commands); - var globalCommandsCreateList = BuildGlobalCreateList(client, commands); - var globalCommandsDeleteList = BuildGlobalDeleteList(client, commands); + var globalCommandsCreateList = BuildGlobalCreateList(commands); + var globalCommandsDeleteList = BuildGlobalDeleteList(commands); if (globalCommandsCreateList.NotEmptyAndNotNull() && unchangedCommands.NotEmptyAndNotNull() && changedCommands.NotEmptyAndNotNull()) { @@ -61,10 +61,9 @@ internal static async Task> RegisterGlobalComman commands.Add(discordBackendCommand); } - foreach (var cmd in changedCommands) + foreach (var (key, command) in changedCommands) { - var command = cmd.Value; - var discordBackendCommand = await client.EditGlobalApplicationCommandAsync(cmd.Key, action => + var discordBackendCommand = await client.EditGlobalApplicationCommandAsync(key, action => { action.Name = command.Name; action.NameLocalizations = command.NameLocalizations; @@ -93,10 +92,9 @@ internal static async Task> RegisterGlobalComman } if (changedCommands.NotEmptyAndNotNull()) - foreach (var cmd in changedCommands) + foreach (var (key, command) in changedCommands) { - var command = cmd.Value; - var discordBackendCommand = await client.EditGlobalApplicationCommandAsync(cmd.Key, action => + var discordBackendCommand = await client.EditGlobalApplicationCommandAsync(key, action => { action.Name = command.Name; action.NameLocalizations = command.NameLocalizations; @@ -119,10 +117,9 @@ internal static async Task> RegisterGlobalComman { client.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Editing & re-using application commands."); - foreach (var cmd in changedCommands) + foreach (var (key, command) in changedCommands) { - var command = cmd.Value; - var discordBackendCommand = await client.EditGlobalApplicationCommandAsync(cmd.Key, action => + var discordBackendCommand = await client.EditGlobalApplicationCommandAsync(key, action => { action.Name = command.Name; action.NameLocalizations = command.NameLocalizations; @@ -145,10 +142,9 @@ internal static async Task> RegisterGlobalComman client.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Overwriting all application commands."); List overwriteList = new(); - foreach (var overwrite in changedCommands) + foreach (var (key, cmd) in changedCommands) { - var cmd = overwrite.Value; - cmd.Id = overwrite.Key; + cmd.Id = key; overwriteList.Add(cmd); } var discordBackendCommands = await client.BulkOverwriteGlobalApplicationCommandsAsync(overwriteList).ConfigureAwait(false); @@ -158,7 +154,7 @@ internal static async Task> RegisterGlobalComman { client.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Creating all application commands."); - var cmds = await client.BulkOverwriteGlobalApplicationCommandsAsync(globalCommandsCreateList).ConfigureAwait(false); + var cmds = await client.BulkOverwriteGlobalApplicationCommandsAsync(globalCommandsCreateList!).ConfigureAwait(false); commands.AddRange(cmds); } else if (globalCommandsCreateList.EmptyOrNull() && changedCommands.EmptyOrNull() && unchangedCommands.NotEmptyAndNotNull()) @@ -168,20 +164,19 @@ internal static async Task> RegisterGlobalComman commands.AddRange(unchangedCommands); } - if (globalCommandsDeleteList.NotEmptyAndNotNull()) - { - client.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Deleting missing application commands."); + if (!globalCommandsDeleteList.NotEmptyAndNotNull()) + return commands.NotEmptyAndNotNull() ? commands : null; + client.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Deleting missing application commands."); - foreach (var cmdId in globalCommandsDeleteList) - try - { - await client.DeleteGlobalApplicationCommandAsync(cmdId).ConfigureAwait(false); - } - catch (NotFoundException) - { - client.Logger.LogError(@"Could not delete global command {cmd}. Please clean up manually", cmdId); - } - } + foreach (var cmdId in globalCommandsDeleteList) + try + { + await client.DeleteGlobalApplicationCommandAsync(cmdId).ConfigureAwait(false); + } + catch (NotFoundException) + { + client.Logger.LogError(@"Could not delete global command {cmd}. Please clean up manually", cmdId); + } return commands.NotEmptyAndNotNull() ? commands : null; } @@ -193,26 +188,25 @@ internal static async Task> RegisterGlobalComman /// The target guild id. /// The command list. /// A list of registered commands. - internal static async Task> RegisterGuildCommandsAsync(DiscordClient client, ulong guildId, List commands) + internal static async Task?> RegisterGuildCommandsAsync(DiscordClient client, ulong guildId, List commands) { var (changedCommands, unchangedCommands) = BuildGuildOverwriteList(client, guildId, commands); - var guildCommandsCreateList = BuildGuildCreateList(client, guildId, commands); - var guildCommandsDeleteList = BuildGuildDeleteList(client, guildId, commands); + var guildCommandsCreateList = BuildGuildCreateList(guildId, commands); + var guildCommandsDeleteList = BuildGuildDeleteList(guildId, commands); if (guildCommandsCreateList.NotEmptyAndNotNull() && unchangedCommands.NotEmptyAndNotNull() && changedCommands.NotEmptyAndNotNull()) { client.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, @"[AC GUILD] Creating, re-using and overwriting application commands. Guild ID: {guild}", guildId); - foreach (var cmd in guildCommandsCreateList) + foreach (var cmd in guildCommandsCreateList!) { var discordBackendCommand = await client.CreateGuildApplicationCommandAsync(guildId, cmd).ConfigureAwait(false); commands.Add(discordBackendCommand); } - foreach (var cmd in changedCommands) + foreach (var (key, command) in changedCommands) { - var command = cmd.Value; - var discordBackendCommand = await client.EditGuildApplicationCommandAsync(guildId, cmd.Key, action => + var discordBackendCommand = await client.EditGuildApplicationCommandAsync(guildId, key, action => { action.Name = command.Name; action.NameLocalizations = command.NameLocalizations; @@ -234,17 +228,16 @@ internal static async Task> RegisterGuildCommand { client.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, @"[AC GUILD] Creating, re-using and overwriting application commands. Guild ID: {guild}", guildId); - foreach (var cmd in guildCommandsCreateList) + foreach (var cmd in guildCommandsCreateList!) { var discordBackendCommand = await client.CreateGuildApplicationCommandAsync(guildId, cmd).ConfigureAwait(false); commands.Add(discordBackendCommand); } if (changedCommands.NotEmptyAndNotNull()) - foreach (var cmd in changedCommands) + foreach (var (key, command) in changedCommands) { - var command = cmd.Value; - var discordBackendCommand = await client.EditGuildApplicationCommandAsync(guildId, cmd.Key, action => + var discordBackendCommand = await client.EditGuildApplicationCommandAsync(guildId, key, action => { action.Name = command.Name; action.NameLocalizations = command.NameLocalizations; @@ -267,10 +260,9 @@ internal static async Task> RegisterGuildCommand { client.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, @"[AC GUILD] Editing & re-using application commands. Guild ID: {guild}", guildId); - foreach (var cmd in changedCommands) + foreach (var (key, command) in changedCommands) { - var command = cmd.Value; - var discordBackendCommand = await client.EditGuildApplicationCommandAsync(guildId, cmd.Key, action => + var discordBackendCommand = await client.EditGuildApplicationCommandAsync(guildId, key, action => { action.Name = command.Name; action.NameLocalizations = command.NameLocalizations; @@ -295,10 +287,9 @@ internal static async Task> RegisterGuildCommand client.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, @"[AC GUILD] Overwriting all application commands. Guild ID: {guild}", guildId); List overwriteList = new(); - foreach (var overwrite in changedCommands) + foreach (var (key, cmd) in changedCommands) { - var cmd = overwrite.Value; - cmd.Id = overwrite.Key; + cmd.Id = key; overwriteList.Add(cmd); } var discordBackendCommands = await client.BulkOverwriteGuildApplicationCommandsAsync(guildId, overwriteList).ConfigureAwait(false); @@ -318,19 +309,20 @@ internal static async Task> RegisterGuildCommand commands.AddRange(unchangedCommands); } - if (guildCommandsDeleteList.NotEmptyAndNotNull()) - foreach (var cmdId in guildCommandsDeleteList) + if (!guildCommandsDeleteList.NotEmptyAndNotNull()) + return commands.NotEmptyAndNotNull() ? commands : null; + foreach (var cmdId in guildCommandsDeleteList!) + { + client.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, @"[AC GUILD] Deleting missing application commands. Guild ID: {guild}", guildId); + try { - client.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, @"[AC GUILD] Deleting missing application commands. Guild ID: {guild}", guildId); - try - { - await client.DeleteGuildApplicationCommandAsync(guildId, cmdId).ConfigureAwait(false); - } - catch (NotFoundException) - { - client.Logger.LogError(@"Could not delete guild command {cmd} in guild {guild}. Please clean up manually", cmdId, guildId); - } + await client.DeleteGuildApplicationCommandAsync(guildId, cmdId).ConfigureAwait(false); + } + catch (NotFoundException) + { + client.Logger.LogError(@"Could not delete guild command {cmd} in guild {guild}. Please clean up manually", cmdId, guildId); } + } return commands.NotEmptyAndNotNull() ? commands : null; } @@ -338,11 +330,10 @@ internal static async Task> RegisterGuildCommand /// /// Builds a list of guild command ids to be deleted on discords backend. /// - /// The discord client. /// The guild id these commands belong to. /// The command list. /// A list of command ids. - private static List BuildGuildDeleteList(DiscordClient client, ulong guildId, List updateList) + private static List? BuildGuildDeleteList(ulong guildId, IReadOnlyCollection? updateList) { if (ApplicationCommandsExtension.GuildDiscordCommands == null || !ApplicationCommandsExtension.GuildDiscordCommands.Any() @@ -356,12 +347,9 @@ private static List BuildGuildDeleteList(DiscordClient client, ulong guil return null; if (updateList == null) - foreach (var cmd in discord) - invalidCommandIds.Add(cmd.Id); + invalidCommandIds.AddRange(discord.Select(cmd => cmd.Id)); else - foreach (var cmd in discord) - if (!updateList.Any(ul => ul.Name == cmd.Name)) - invalidCommandIds.Add(cmd.Id); + invalidCommandIds.AddRange(from cmd in discord where updateList.All(ul => ul.Name != cmd.Name) select cmd.Id); return invalidCommandIds; } @@ -369,29 +357,13 @@ private static List BuildGuildDeleteList(DiscordClient client, ulong guil /// /// Builds a list of guild commands to be created on discords backend. /// - /// The discord client. /// The guild id these commands belong to. /// The command list. /// - private static List BuildGuildCreateList(DiscordClient client, ulong guildId, List updateList) - { - - if (ApplicationCommandsExtension.GuildDiscordCommands == null || !ApplicationCommandsExtension.GuildDiscordCommands.Any() - || updateList == null || !ApplicationCommandsExtension.GuildDiscordCommands.TryGetFirstValueByKey(guildId, out var discord) - ) - return updateList; - - List newCommands = new(); - - if (discord == null) - return updateList; - - foreach (var cmd in updateList) - if (discord.All(d => d.Name != cmd.Name)) - newCommands.Add(cmd); - - return newCommands; - } + private static List? BuildGuildCreateList(ulong guildId, List? updateList) => + ApplicationCommandsExtension.GuildDiscordCommands == null || !ApplicationCommandsExtension.GuildDiscordCommands.Any() || updateList == null || !ApplicationCommandsExtension.GuildDiscordCommands.TryGetFirstValueByKey(guildId, out var discord) + ? updateList + : discord == null ? updateList : updateList.Where(cmd => discord.All(d => d.Name != cmd.Name)).ToList(); /// /// Builds a list of guild commands to be overwritten on discords backend. @@ -401,9 +373,9 @@ private static List BuildGuildCreateList(DiscordClien /// The command list. /// A dictionary of command id and command. private static ( - Dictionary changedCommands, - List unchangedCommands - ) BuildGuildOverwriteList(DiscordClient client, ulong guildId, List updateList) + Dictionary? changedCommands, + List? unchangedCommands + ) BuildGuildOverwriteList(DiscordClient client, ulong guildId, List? updateList) { if (ApplicationCommandsExtension.GuildDiscordCommands == null || !ApplicationCommandsExtension.GuildDiscordCommands.Any() @@ -442,10 +414,9 @@ List unchangedCommands /// /// Builds a list of global command ids to be deleted on discords backend. /// - /// The discord client. /// The command list. /// A list of command ids. - private static List BuildGlobalDeleteList(DiscordClient client, List updateList = null) + private static List? BuildGlobalDeleteList(IReadOnlyCollection? updateList = null) { if (ApplicationCommandsExtension.GlobalDiscordCommands == null || !ApplicationCommandsExtension.GlobalDiscordCommands.Any() || ApplicationCommandsExtension.GlobalDiscordCommands == null @@ -460,12 +431,9 @@ private static List BuildGlobalDeleteList(DiscordClient client, List cmd.Id)); else - foreach (var cmd in discord) - if (updateList.All(ul => ul.Name != cmd.Name)) - invalidCommandIds.Add(cmd.Id); + invalidCommandIds.AddRange(from cmd in discord where updateList.All(ul => ul.Name != cmd.Name) select cmd.Id); return invalidCommandIds; } @@ -473,26 +441,16 @@ private static List BuildGlobalDeleteList(DiscordClient client, List /// Builds a list of global commands to be created on discords backend. /// - /// The discord client. /// The command list. /// A list of commands. - private static List BuildGlobalCreateList(DiscordClient client, List updateList) + private static List? BuildGlobalCreateList(List? updateList) { if (ApplicationCommandsExtension.GlobalDiscordCommands == null || !ApplicationCommandsExtension.GlobalDiscordCommands.Any() || updateList == null) return updateList; var discord = ApplicationCommandsExtension.GlobalDiscordCommands; - List newCommands = new(); - - if (discord == null) - return updateList; - - foreach (var cmd in updateList) - if (discord.All(d => d.Name != cmd.Name)) - newCommands.Add(cmd); - - return newCommands; + return discord == null ? updateList : updateList.Where(cmd => discord.All(d => d.Name != cmd.Name)).ToList(); } /// @@ -502,9 +460,9 @@ private static List BuildGlobalCreateList(DiscordClie /// The command list. /// A dictionary of command ids and commands. private static ( - Dictionary changedCommands, - List unchangedCommands - ) BuildGlobalOverwriteList(DiscordClient client, List updateList) + Dictionary? changedCommands, + List? unchangedCommands + ) BuildGlobalOverwriteList(DiscordClient client, List? updateList) { if (ApplicationCommandsExtension.GlobalDiscordCommands == null || !ApplicationCommandsExtension.GlobalDiscordCommands.Any() || updateList == null || ApplicationCommandsExtension.GlobalDiscordCommands == null diff --git a/DisCatSharp/Entities/Application/DiscordApplicationCommand.cs b/DisCatSharp/Entities/Application/DiscordApplicationCommand.cs index 5a12cc884b..39963d4885 100644 --- a/DisCatSharp/Entities/Application/DiscordApplicationCommand.cs +++ b/DisCatSharp/Entities/Application/DiscordApplicationCommand.cs @@ -57,14 +57,14 @@ public class DiscordApplicationCommand : SnowflakeObject, IEquatable [JsonProperty("name_localizations", NullValueHandling = NullValueHandling.Ignore)] - internal Dictionary RawNameLocalizations { get; set; } + internal Dictionary? RawNameLocalizations { get; set; } /// /// Gets the name localizations. /// [JsonIgnore] - public DiscordApplicationCommandLocalization NameLocalizations - => new(this.RawNameLocalizations); + public DiscordApplicationCommandLocalization? NameLocalizations + => this.RawNameLocalizations != null ? new(this.RawNameLocalizations) : null; /// /// Gets the description of this command. @@ -76,14 +76,14 @@ public DiscordApplicationCommandLocalization NameLocalizations /// Sets the description localizations. /// [JsonProperty("description_localizations", NullValueHandling = NullValueHandling.Ignore)] - internal Dictionary RawDescriptionLocalizations { get; set; } + internal Dictionary? RawDescriptionLocalizations { get; set; } /// /// Gets the description localizations. /// [JsonIgnore] - public DiscordApplicationCommandLocalization DescriptionLocalizations - => new(this.RawDescriptionLocalizations); + public DiscordApplicationCommandLocalization? DescriptionLocalizations + => this.RawDescriptionLocalizations != null ? new (this.RawDescriptionLocalizations) : null; /// /// Gets the potential parameters for this command. @@ -125,7 +125,8 @@ public DiscordApplicationCommandLocalization DescriptionLocalizations /// Gets the name localizations. /// [JsonIgnore] - public string Mention => this.Type == ApplicationCommandType.ChatInput ? $"" : this.Name; + public string Mention + => this.Type == ApplicationCommandType.ChatInput ? $"" : this.Name; /// /// Creates a new instance of a . @@ -141,7 +142,7 @@ public DiscordApplicationCommandLocalization DescriptionLocalizations /// Whether this command is NSFW. // /// Where the command can be used. public DiscordApplicationCommand( - string name, string description, + string name, string? description, IEnumerable? options = null, ApplicationCommandType type = ApplicationCommandType.ChatInput, DiscordApplicationCommandLocalization? nameLocalizations = null, DiscordApplicationCommandLocalization? descriptionLocalizations = null, @@ -152,9 +153,9 @@ public DiscordApplicationCommand( { if (!Utilities.IsValidSlashCommandName(name)) throw new ArgumentException("Invalid slash command name specified. It must be below 32 characters and not contain any whitespace.", nameof(name)); - if (name.Any(ch => char.IsUpper(ch))) + if (name.Any(char.IsUpper)) throw new ArgumentException("Slash command name cannot have any upper case characters.", nameof(name)); - if (description.Length > 100) + if (description is { Length: > 100 }) throw new ArgumentException("Slash command description cannot exceed 100 characters.", nameof(description)); if (string.IsNullOrWhiteSpace(description)) throw new ArgumentException("Slash commands need a description.", nameof(description)); diff --git a/DisCatSharp/Entities/Application/DiscordApplicationCommandOption.cs b/DisCatSharp/Entities/Application/DiscordApplicationCommandOption.cs index ba5a6de57a..462845c9bf 100644 --- a/DisCatSharp/Entities/Application/DiscordApplicationCommandOption.cs +++ b/DisCatSharp/Entities/Application/DiscordApplicationCommandOption.cs @@ -51,14 +51,14 @@ public class DiscordApplicationCommandOption /// Sets the name localizations. /// [JsonProperty("name_localizations", NullValueHandling = NullValueHandling.Ignore)] - internal Dictionary RawNameLocalizations { get; set; } + internal Dictionary? RawNameLocalizations { get; set; } /// /// Gets the name localizations. /// [JsonIgnore] - public DiscordApplicationCommandLocalization NameLocalizations - => new(this.RawNameLocalizations); + public DiscordApplicationCommandLocalization? NameLocalizations + => this.RawNameLocalizations != null ? new (this.RawNameLocalizations) : null; /// /// Gets the description of this command parameter. @@ -70,14 +70,14 @@ public DiscordApplicationCommandLocalization NameLocalizations /// Sets the description localizations. /// [JsonProperty("description_localizations", NullValueHandling = NullValueHandling.Ignore)] - internal Dictionary RawDescriptionLocalizations { get; set; } + internal Dictionary? RawDescriptionLocalizations { get; set; } /// /// Gets the description localizations. /// [JsonIgnore] - public DiscordApplicationCommandLocalization DescriptionLocalizations - => new(this.RawDescriptionLocalizations); + public DiscordApplicationCommandLocalization? DescriptionLocalizations + => this.RawDescriptionLocalizations != null ? new (this.RawDescriptionLocalizations) : null; /// /// Gets whether this command parameter is required. @@ -151,7 +151,7 @@ public DiscordApplicationCommandLocalization DescriptionLocalizations /// The localizations of the parameter description. /// The minimum allowed length of the string. (Min 0) /// The maximum allowed length of the string. (Min 1) - public DiscordApplicationCommandOption(string name, string description, ApplicationCommandOptionType type, bool required = false, IEnumerable? choices = null, IEnumerable? options = null, IEnumerable? channelTypes = null, bool autocomplete = false, object minimumValue = null, object maximumValue = null, DiscordApplicationCommandLocalization nameLocalizations = null, DiscordApplicationCommandLocalization descriptionLocalizations = null, int? minimumLength = null, int? maximumLength = null) + public DiscordApplicationCommandOption(string name, string description, ApplicationCommandOptionType type, bool required = false, IEnumerable? choices = null, IEnumerable? options = null, IEnumerable? channelTypes = null, bool autocomplete = false, object? minimumValue = null, object? maximumValue = null, DiscordApplicationCommandLocalization? nameLocalizations = null, DiscordApplicationCommandLocalization? descriptionLocalizations = null, int? minimumLength = null, int? maximumLength = null) { if (!Utilities.IsValidSlashCommandName(name)) throw new ArgumentException("Invalid application command option name specified. It must be below 32 characters and not contain any whitespace.", nameof(name));