diff --git a/DisCatSharp.ApplicationCommands/ApplicationCommandsConfiguration.cs b/DisCatSharp.ApplicationCommands/ApplicationCommandsConfiguration.cs index d05bbc96c..09c277ac7 100644 --- a/DisCatSharp.ApplicationCommands/ApplicationCommandsConfiguration.cs +++ b/DisCatSharp.ApplicationCommands/ApplicationCommandsConfiguration.cs @@ -1,115 +1,114 @@ // 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 Microsoft.Extensions.DependencyInjection; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// A configuration for a +/// +public class ApplicationCommandsConfiguration { /// - /// A configuration for a + /// Sets the service provider. + /// Objects in this provider are used when instantiating application command modules. + /// This allows passing data around without resorting to static members. + /// Defaults to . /// - public class ApplicationCommandsConfiguration - { - /// - /// Sets the service provider. - /// Objects in this provider are used when instantiating application command modules. - /// This allows passing data around without resorting to static members. - /// Defaults to . - /// - public IServiceProvider ServiceProvider { internal get; set; } + public IServiceProvider ServiceProvider { internal get; set; } - /// - /// This option enables the default help command. - /// Disabling this will allow you to make your own help command. - /// Defaults to . - /// - public bool EnableDefaultHelp { internal get; set; } = true; + /// + /// This option enables the default help command. + /// Disabling this will allow you to make your own help command. + /// Defaults to . + /// + public bool EnableDefaultHelp { internal get; set; } = true; - /// - /// This option enables the localization feature. - /// Defaults to . - /// - public bool EnableLocalization { internal get; set; } = false; + /// + /// This option enables the localization feature. + /// Defaults to . + /// + public bool EnableLocalization { internal get; set; } = false; - /// - /// Automatically defer all responses. - /// If you enable this, you can't use CreateResponse. Use EditResponse instead. - /// Defaults to . - /// - public bool AutoDefer { internal get; set; } = false; + /// + /// Automatically defer all responses. + /// If you enable this, you can't use CreateResponse. Use EditResponse instead. + /// Defaults to . + /// + public bool AutoDefer { internal get; set; } = false; - /// - /// This option informs the module to check through all guilds whether the - /// application.commands scope is set. - /// This will take quite a while, when the bot is on more than 1k guilds. - /// Defaults to . - /// - public bool CheckAllGuilds { internal get; set; } = false; + /// + /// This option informs the module to check through all guilds whether the + /// application.commands scope is set. + /// This will take quite a while, when the bot is on more than 1k guilds. + /// Defaults to . + /// + public bool CheckAllGuilds { internal get; set; } = false; - /// - /// This option can override the default registration behavior of the module. - /// - /// It can lead to unexpected behavior of the application commands module. - /// Enable this option only if DisCatSharp support advises you to do so. - /// - /// Defaults to . - /// - public bool ManualOverride { internal get; set; } = false; + /// + /// This option can override the default registration behavior of the module. + /// + /// It can lead to unexpected behavior of the application commands module. + /// Enable this option only if DisCatSharp support advises you to do so. + /// + /// Defaults to . + /// + public bool ManualOverride { internal get; set; } = false; - /// - /// This option increases the debug output of the module. - /// - /// This is not recommended for production use. - /// Enable this option only if DisCatSharp support advises you to do so. - /// - /// Defaults to . - /// - public bool DebugStartup { internal get; set; } = false; + /// + /// This option increases the debug output of the module. + /// + /// This is not recommended for production use. + /// Enable this option only if DisCatSharp support advises you to do so. + /// + /// Defaults to . + /// + public bool DebugStartup { internal get; set; } = false; - /// - /// Initializes a new instance of the class. - /// - /// The service provider. - [ActivatorUtilitiesConstructor] - public ApplicationCommandsConfiguration(IServiceProvider provider = null) - { - this.ServiceProvider = provider; - } + /// + /// Initializes a new instance of the class. + /// + /// The service provider. + [ActivatorUtilitiesConstructor] + public ApplicationCommandsConfiguration(IServiceProvider provider = null) + { + this.ServiceProvider = provider; + } - /// - /// Creates a new instance of , copying the properties of another configuration. - /// - /// Configuration the properties of which are to be copied. - public ApplicationCommandsConfiguration(ApplicationCommandsConfiguration acc) - { - this.EnableDefaultHelp = acc.EnableDefaultHelp; - this.ServiceProvider = acc.ServiceProvider; - this.DebugStartup = acc.DebugStartup; - this.CheckAllGuilds = acc.CheckAllGuilds; - this.ManualOverride = acc.ManualOverride; - this.AutoDefer = acc.AutoDefer; - this.EnableLocalization = acc.EnableLocalization; - } + /// + /// Creates a new instance of , copying the properties of another configuration. + /// + /// Configuration the properties of which are to be copied. + public ApplicationCommandsConfiguration(ApplicationCommandsConfiguration acc) + { + this.EnableDefaultHelp = acc.EnableDefaultHelp; + this.ServiceProvider = acc.ServiceProvider; + this.DebugStartup = acc.DebugStartup; + this.CheckAllGuilds = acc.CheckAllGuilds; + this.ManualOverride = acc.ManualOverride; + this.AutoDefer = acc.AutoDefer; + this.EnableLocalization = acc.EnableLocalization; } } diff --git a/DisCatSharp.ApplicationCommands/ApplicationCommandsExtension.cs b/DisCatSharp.ApplicationCommands/ApplicationCommandsExtension.cs index ba2d4aff1..735e78a50 100644 --- a/DisCatSharp.ApplicationCommands/ApplicationCommandsExtension.cs +++ b/DisCatSharp.ApplicationCommands/ApplicationCommandsExtension.cs @@ -1,1820 +1,1819 @@ // 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.Text; using System.Threading.Tasks; using DisCatSharp.ApplicationCommands.Attributes; using DisCatSharp.ApplicationCommands.EventArgs; using DisCatSharp.Common; using DisCatSharp.Common.Utilities; using DisCatSharp.Entities; using DisCatSharp.Enums; using DisCatSharp.EventArgs; using DisCatSharp.Exceptions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Newtonsoft.Json; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// A class that handles slash commands for a client. +/// +public sealed class ApplicationCommandsExtension : BaseExtension { /// - /// A class that handles slash commands for a client. + /// A list of methods for top level commands. + /// + private static List s_commandMethods { get; set; } = new(); + + /// + /// List of groups. + /// + private static List s_groupCommands { get; set; } = new(); + + /// + /// List of groups with subgroups. + /// + private static List s_subGroupCommands { get; set; } = new(); + + /// + /// List of context menus. + /// + private static List s_contextMenuCommands { get; set; } = new(); + + /// + /// List of global commands on discords backend. + /// + internal static List GlobalDiscordCommands { get; set; } + + /// + /// List of guild commands on discords backend. + /// + internal static Dictionary> GuildDiscordCommands { get; set; } + + /// + /// Singleton modules. + /// + private static List s_singletonModules { get; set; } = new(); + + /// + /// List of modules to register. + /// + private readonly List> _updateList = new(); + + /// + /// Configuration for Discord. + /// + internal static ApplicationCommandsConfiguration Configuration; + + /// + /// Discord client. + /// + internal static DiscordClient ClientInternal; + + /// + /// Set to true if anything fails when registering. + /// + private static bool s_errored { get; set; } + + /// + /// Gets a list of registered commands. The key is the guild id (null if global). + /// + public IReadOnlyList>> RegisteredCommands + => s_registeredCommands; + private static readonly List>> s_registeredCommands = new(); + + /// + /// Gets a list of registered global commands. + /// + public IReadOnlyList GlobalCommands + => GlobalCommandsInternal; + internal static readonly List GlobalCommandsInternal = new(); + + /// + /// Gets a list of registered guild commands mapped by guild id. + /// + public IReadOnlyDictionary> GuildCommands + => GuildCommandsInternal; + internal static readonly Dictionary> GuildCommandsInternal = new(); + + /// + /// Gets the registration count. + /// + private static int s_registrationCount { get; set; } + + /// + /// Gets the expected count. + /// + private static int s_expectedCount { get; set; } + + /// + /// Gets the guild ids where the applications.commands scope is missing. + /// + private IReadOnlyList _missingScopeGuildIds; + + /// + /// Gets whether debug is enabled. + /// + internal static bool DebugEnabled { get; set; } + + internal static LogLevel ApplicationCommandsLogLevel + => DebugEnabled ? LogLevel.Debug : LogLevel.Trace; + + /// + /// Gets whether check through all guilds is enabled. + /// + internal static bool CheckAllGuilds { get; set; } + + /// + /// Gets whether the registration check should be manually overridden. + /// + internal static bool ManOr { get; set; } + + /// + /// Gets whether interactions should be automatically deffered. /// - public sealed class ApplicationCommandsExtension : BaseExtension + internal static bool AutoDeferEnabled { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The configuration. + internal ApplicationCommandsExtension(ApplicationCommandsConfiguration configuration = null) { - /// - /// A list of methods for top level commands. - /// - private static List s_commandMethods { get; set; } = new(); - - /// - /// List of groups. - /// - private static List s_groupCommands { get; set; } = new(); - - /// - /// List of groups with subgroups. - /// - private static List s_subGroupCommands { get; set; } = new(); - - /// - /// List of context menus. - /// - private static List s_contextMenuCommands { get; set; } = new(); - - /// - /// List of global commands on discords backend. - /// - internal static List GlobalDiscordCommands { get; set; } - - /// - /// List of guild commands on discords backend. - /// - internal static Dictionary> GuildDiscordCommands { get; set; } - - /// - /// Singleton modules. - /// - private static List s_singletonModules { get; set; } = new(); - - /// - /// List of modules to register. - /// - private readonly List> _updateList = new(); - - /// - /// Configuration for Discord. - /// - internal static ApplicationCommandsConfiguration Configuration; - - /// - /// Discord client. - /// - internal static DiscordClient ClientInternal; - - /// - /// Set to true if anything fails when registering. - /// - private static bool s_errored { get; set; } - - /// - /// Gets a list of registered commands. The key is the guild id (null if global). - /// - public IReadOnlyList>> RegisteredCommands - => s_registeredCommands; - private static readonly List>> s_registeredCommands = new(); - - /// - /// Gets a list of registered global commands. - /// - public IReadOnlyList GlobalCommands - => GlobalCommandsInternal; - internal static readonly List GlobalCommandsInternal = new(); - - /// - /// Gets a list of registered guild commands mapped by guild id. - /// - public IReadOnlyDictionary> GuildCommands - => GuildCommandsInternal; - internal static readonly Dictionary> GuildCommandsInternal = new(); - - /// - /// Gets the registration count. - /// - private static int s_registrationCount { get; set; } - - /// - /// Gets the expected count. - /// - private static int s_expectedCount { get; set; } - - /// - /// Gets the guild ids where the applications.commands scope is missing. - /// - private IReadOnlyList _missingScopeGuildIds; - - /// - /// Gets whether debug is enabled. - /// - internal static bool DebugEnabled { get; set; } - - internal static LogLevel ApplicationCommandsLogLevel - => DebugEnabled ? LogLevel.Debug : LogLevel.Trace; - - /// - /// Gets whether check through all guilds is enabled. - /// - internal static bool CheckAllGuilds { get; set; } - - /// - /// Gets whether the registration check should be manually overridden. - /// - internal static bool ManOr { get; set; } - - /// - /// Gets whether interactions should be automatically deffered. - /// - internal static bool AutoDeferEnabled { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// The configuration. - internal ApplicationCommandsExtension(ApplicationCommandsConfiguration configuration = null) - { - Configuration = configuration; - DebugEnabled = configuration?.DebugStartup ?? false; - CheckAllGuilds = configuration?.CheckAllGuilds ?? false; - ManOr = configuration?.ManualOverride ?? false; - AutoDeferEnabled = configuration?.AutoDefer ?? false; - } + Configuration = configuration; + DebugEnabled = configuration?.DebugStartup ?? false; + CheckAllGuilds = configuration?.CheckAllGuilds ?? false; + ManOr = configuration?.ManualOverride ?? false; + AutoDeferEnabled = configuration?.AutoDefer ?? false; + } - /// - /// Runs setup. - /// DO NOT RUN THIS MANUALLY. DO NOT DO ANYTHING WITH THIS. - /// - /// The client to setup on. - protected internal override void Setup(DiscordClient client) - { - if (this.Client != null) - throw new InvalidOperationException("What did I tell you?"); - - this.Client = client; - ClientInternal = client; - - this._slashError = new AsyncEvent("SLASHCOMMAND_ERRORED", TimeSpan.Zero, null); - this._slashExecuted = new AsyncEvent("SLASHCOMMAND_EXECUTED", TimeSpan.Zero, null); - this._contextMenuErrored = new AsyncEvent("CONTEXTMENU_ERRORED", TimeSpan.Zero, null); - this._contextMenuExecuted = new AsyncEvent("CONTEXTMENU_EXECUTED", TimeSpan.Zero, null); - this._applicationCommandsModuleReady = new AsyncEvent("APPLICATION_COMMANDS_MODULE_READY", TimeSpan.Zero, null); - this._applicationCommandsModuleStartupFinished = new AsyncEvent("APPLICATION_COMMANDS_MODULE_STARTUP_FINISHED", TimeSpan.Zero, null); - this._globalApplicationCommandsRegistered = new AsyncEvent("GLOBAL_COMMANDS_REGISTERED", TimeSpan.Zero, null); - this._guildApplicationCommandsRegistered = new AsyncEvent("GUILD_COMMANDS_REGISTERED", TimeSpan.Zero, null); - - this.Client.GuildDownloadCompleted += async (c, e) => await this.UpdateAsync(); - this.Client.InteractionCreated += this.CatchInteractionsOnStartup; - this.Client.ContextMenuInteractionCreated += this.CatchContextMenuInteractionsOnStartup; - } + /// + /// Runs setup. + /// DO NOT RUN THIS MANUALLY. DO NOT DO ANYTHING WITH THIS. + /// + /// The client to setup on. + protected internal override void Setup(DiscordClient client) + { + if (this.Client != null) + throw new InvalidOperationException("What did I tell you?"); + + this.Client = client; + ClientInternal = client; + + this._slashError = new AsyncEvent("SLASHCOMMAND_ERRORED", TimeSpan.Zero, null); + this._slashExecuted = new AsyncEvent("SLASHCOMMAND_EXECUTED", TimeSpan.Zero, null); + this._contextMenuErrored = new AsyncEvent("CONTEXTMENU_ERRORED", TimeSpan.Zero, null); + this._contextMenuExecuted = new AsyncEvent("CONTEXTMENU_EXECUTED", TimeSpan.Zero, null); + this._applicationCommandsModuleReady = new AsyncEvent("APPLICATION_COMMANDS_MODULE_READY", TimeSpan.Zero, null); + this._applicationCommandsModuleStartupFinished = new AsyncEvent("APPLICATION_COMMANDS_MODULE_STARTUP_FINISHED", TimeSpan.Zero, null); + this._globalApplicationCommandsRegistered = new AsyncEvent("GLOBAL_COMMANDS_REGISTERED", TimeSpan.Zero, null); + this._guildApplicationCommandsRegistered = new AsyncEvent("GUILD_COMMANDS_REGISTERED", TimeSpan.Zero, null); + + this.Client.GuildDownloadCompleted += async (c, e) => await this.UpdateAsync(); + this.Client.InteractionCreated += this.CatchInteractionsOnStartup; + this.Client.ContextMenuInteractionCreated += this.CatchContextMenuInteractionsOnStartup; + } - private async Task CatchInteractionsOnStartup(DiscordClient sender, InteractionCreateEventArgs e) - => await e.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral(true).WithContent("Attention: This application is still starting up. Application commands are unavailable for now.")); + private async Task CatchInteractionsOnStartup(DiscordClient sender, InteractionCreateEventArgs e) + => await e.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral(true).WithContent("Attention: This application is still starting up. Application commands are unavailable for now.")); - private async Task CatchContextMenuInteractionsOnStartup(DiscordClient sender, ContextMenuInteractionCreateEventArgs e) - => await e.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral(true).WithContent("Attention: This application is still starting up. Context menu commands are unavailable for now.")); + private async Task CatchContextMenuInteractionsOnStartup(DiscordClient sender, ContextMenuInteractionCreateEventArgs e) + => await e.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral(true).WithContent("Attention: This application is still starting up. Context menu commands are unavailable for now.")); - private void FinishedRegistration() - { - this.Client.InteractionCreated -= this.CatchInteractionsOnStartup; - this.Client.ContextMenuInteractionCreated -= this.CatchContextMenuInteractionsOnStartup; + private void FinishedRegistration() + { + this.Client.InteractionCreated -= this.CatchInteractionsOnStartup; + this.Client.ContextMenuInteractionCreated -= this.CatchContextMenuInteractionsOnStartup; - this.Client.InteractionCreated += this.InteractionHandler; - this.Client.ContextMenuInteractionCreated += this.ContextMenuHandler; - } - /// - /// Cleans the module for a new start of the bot. - /// DO NOT USE IF YOU DON'T KNOW WHAT IT DOES. - /// - public void CleanModule() - { - this._updateList.Clear(); - s_singletonModules.Clear(); - s_errored = false; - s_expectedCount = 0; - s_registrationCount = 0; - s_commandMethods.Clear(); - s_groupCommands.Clear(); - s_contextMenuCommands.Clear(); - s_subGroupCommands.Clear(); - s_singletonModules.Clear(); - s_registeredCommands.Clear(); - GlobalCommandsInternal.Clear(); - GuildCommandsInternal.Clear(); - } + this.Client.InteractionCreated += this.InteractionHandler; + this.Client.ContextMenuInteractionCreated += this.ContextMenuHandler; + } + /// + /// Cleans the module for a new start of the bot. + /// DO NOT USE IF YOU DON'T KNOW WHAT IT DOES. + /// + public void CleanModule() + { + this._updateList.Clear(); + s_singletonModules.Clear(); + s_errored = false; + s_expectedCount = 0; + s_registrationCount = 0; + s_commandMethods.Clear(); + s_groupCommands.Clear(); + s_contextMenuCommands.Clear(); + s_subGroupCommands.Clear(); + s_singletonModules.Clear(); + s_registeredCommands.Clear(); + GlobalCommandsInternal.Clear(); + GuildCommandsInternal.Clear(); + } - /// - /// Registers a command class. - /// - /// The command class to register. - public void RegisterGlobalCommands() where T : ApplicationCommandsModule - { - if (this.Client.ShardId == 0) - this._updateList.Add(new KeyValuePair(null, new ApplicationCommandsModuleConfiguration(typeof(T)))); - } - /// - /// Registers a command class. - /// - /// The of the command class to register. - public void RegisterGlobalCommands(Type type) - { - if (!typeof(ApplicationCommandsModule).IsAssignableFrom(type)) - throw new ArgumentException("Command classes have to inherit from ApplicationCommandsModule", nameof(type)); - //If sharding, only register for shard 0 - if (this.Client.ShardId == 0) - this._updateList.Add(new KeyValuePair(null, new ApplicationCommandsModuleConfiguration(type))); - } + /// + /// Registers a command class. + /// + /// The command class to register. + public void RegisterGlobalCommands() where T : ApplicationCommandsModule + { + if (this.Client.ShardId == 0) + this._updateList.Add(new KeyValuePair(null, new ApplicationCommandsModuleConfiguration(typeof(T)))); + } + /// + /// Registers a command class. + /// + /// The of the command class to register. + public void RegisterGlobalCommands(Type type) + { + if (!typeof(ApplicationCommandsModule).IsAssignableFrom(type)) + throw new ArgumentException("Command classes have to inherit from ApplicationCommandsModule", nameof(type)); + //If sharding, only register for shard 0 + if (this.Client.ShardId == 0) + this._updateList.Add(new KeyValuePair(null, new ApplicationCommandsModuleConfiguration(type))); + } - /// - /// Cleans all guild application commands. - /// You normally don't need to execute it. - /// - public async Task CleanGuildCommandsAsync() + /// + /// Cleans all guild application commands. + /// You normally don't need to execute it. + /// + public async Task CleanGuildCommandsAsync() + { + foreach (var guild in this.Client.Guilds.Values) { - foreach (var guild in this.Client.Guilds.Values) - { - await this.Client.BulkOverwriteGuildApplicationCommandsAsync(guild.Id, Array.Empty()); - } + await this.Client.BulkOverwriteGuildApplicationCommandsAsync(guild.Id, Array.Empty()); } + } - /// - /// Cleans the global application commands. - /// You normally don't need to execute it. - /// - public async Task CleanGlobalCommandsAsync() - => await this.Client.BulkOverwriteGlobalApplicationCommandsAsync(Array.Empty()); - - /// - /// Registers a command class with permission and translation setup. - /// - /// The command class to register. - /// The guild id to register it on. - /// A callback to setup translations with. - public void RegisterGuildCommands(ulong guildId, Action translationSetup = null) where T : ApplicationCommandsModule - { - if (this.Client.ShardId == 0) - this._updateList.Add(new KeyValuePair(guildId, new ApplicationCommandsModuleConfiguration(typeof(T), translationSetup))); - } + /// + /// Cleans the global application commands. + /// You normally don't need to execute it. + /// + public async Task CleanGlobalCommandsAsync() + => await this.Client.BulkOverwriteGlobalApplicationCommandsAsync(Array.Empty()); - /// - /// Registers a command class with permission and translation setup. - /// - /// The of the command class to register. - /// The guild id to register it on. - /// A callback to setup translations with. - public void RegisterGuildCommands(Type type, ulong guildId, Action translationSetup = null) - { - if (!typeof(ApplicationCommandsModule).IsAssignableFrom(type)) - throw new ArgumentException("Command classes have to inherit from ApplicationCommandsModule", nameof(type)); - //If sharding, only register for shard 0 - if (this.Client.ShardId == 0) - this._updateList.Add(new KeyValuePair(guildId, new ApplicationCommandsModuleConfiguration(type, translationSetup))); - } + /// + /// Registers a command class with permission and translation setup. + /// + /// The command class to register. + /// The guild id to register it on. + /// A callback to setup translations with. + public void RegisterGuildCommands(ulong guildId, Action translationSetup = null) where T : ApplicationCommandsModule + { + if (this.Client.ShardId == 0) + this._updateList.Add(new KeyValuePair(guildId, new ApplicationCommandsModuleConfiguration(typeof(T), translationSetup))); + } - /// - /// Registers a command class with permission setup but without a guild id. - /// - /// The command class to register. - /// A callback to setup translations with. - public void RegisterGlobalCommands(Action translationSetup = null) where T : ApplicationCommandsModule - { - if (this.Client.ShardId == 0) - this._updateList.Add(new KeyValuePair(null, new ApplicationCommandsModuleConfiguration(typeof(T), translationSetup))); - } + /// + /// Registers a command class with permission and translation setup. + /// + /// The of the command class to register. + /// The guild id to register it on. + /// A callback to setup translations with. + public void RegisterGuildCommands(Type type, ulong guildId, Action translationSetup = null) + { + if (!typeof(ApplicationCommandsModule).IsAssignableFrom(type)) + throw new ArgumentException("Command classes have to inherit from ApplicationCommandsModule", nameof(type)); + //If sharding, only register for shard 0 + if (this.Client.ShardId == 0) + this._updateList.Add(new KeyValuePair(guildId, new ApplicationCommandsModuleConfiguration(type, translationSetup))); + } - /// - /// Registers a command class with permission setup but without a guild id. - /// - /// The of the command class to register. - /// A callback to setup translations with. - public void RegisterGlobalCommands(Type type, Action translationSetup = null) - { - if (!typeof(ApplicationCommandsModule).IsAssignableFrom(type)) - throw new ArgumentException("Command classes have to inherit from ApplicationCommandsModule", nameof(type)); - //If sharding, only register for shard 0 - if (this.Client.ShardId == 0) - this._updateList.Add(new KeyValuePair(null, new ApplicationCommandsModuleConfiguration(type, translationSetup))); - } + /// + /// Registers a command class with permission setup but without a guild id. + /// + /// The command class to register. + /// A callback to setup translations with. + public void RegisterGlobalCommands(Action translationSetup = null) where T : ApplicationCommandsModule + { + if (this.Client.ShardId == 0) + this._updateList.Add(new KeyValuePair(null, new ApplicationCommandsModuleConfiguration(typeof(T), translationSetup))); + } - /// - /// Fired when the application commands module is ready. - /// - public event AsyncEventHandler ApplicationCommandsModuleReady - { - add => this._applicationCommandsModuleReady.Register(value); - remove => this._applicationCommandsModuleReady.Unregister(value); - } - private AsyncEvent _applicationCommandsModuleReady; + /// + /// Registers a command class with permission setup but without a guild id. + /// + /// The of the command class to register. + /// A callback to setup translations with. + public void RegisterGlobalCommands(Type type, Action translationSetup = null) + { + if (!typeof(ApplicationCommandsModule).IsAssignableFrom(type)) + throw new ArgumentException("Command classes have to inherit from ApplicationCommandsModule", nameof(type)); + //If sharding, only register for shard 0 + if (this.Client.ShardId == 0) + this._updateList.Add(new KeyValuePair(null, new ApplicationCommandsModuleConfiguration(type, translationSetup))); + } - /// - /// Fired when the application commands modules startup is finished. - /// - public event AsyncEventHandler ApplicationCommandsModuleStartupFinished - { - add => this._applicationCommandsModuleStartupFinished.Register(value); - remove => this._applicationCommandsModuleStartupFinished.Unregister(value); - } - private AsyncEvent _applicationCommandsModuleStartupFinished; + /// + /// Fired when the application commands module is ready. + /// + public event AsyncEventHandler ApplicationCommandsModuleReady + { + add => this._applicationCommandsModuleReady.Register(value); + remove => this._applicationCommandsModuleReady.Unregister(value); + } + private AsyncEvent _applicationCommandsModuleReady; + /// + /// Fired when the application commands modules startup is finished. + /// + public event AsyncEventHandler ApplicationCommandsModuleStartupFinished + { + add => this._applicationCommandsModuleStartupFinished.Register(value); + remove => this._applicationCommandsModuleStartupFinished.Unregister(value); + } + private AsyncEvent _applicationCommandsModuleStartupFinished; - /// - /// Fired when guild commands are registered on a guild. - /// - public event AsyncEventHandler GuildApplicationCommandsRegistered - { - add => this._guildApplicationCommandsRegistered.Register(value); - remove => this._guildApplicationCommandsRegistered.Unregister(value); - } - private AsyncEvent _guildApplicationCommandsRegistered; - /// - /// Fired when the global commands are registered. - /// - public event AsyncEventHandler GlobalApplicationCommandsRegistered - { - add => this._globalApplicationCommandsRegistered.Register(value); - remove => this._globalApplicationCommandsRegistered.Unregister(value); - } - private AsyncEvent _globalApplicationCommandsRegistered; + /// + /// Fired when guild commands are registered on a guild. + /// + public event AsyncEventHandler GuildApplicationCommandsRegistered + { + add => this._guildApplicationCommandsRegistered.Register(value); + remove => this._guildApplicationCommandsRegistered.Unregister(value); + } + private AsyncEvent _guildApplicationCommandsRegistered; - /// - /// Used for RegisterCommands and the event. - /// - internal async Task UpdateAsync() + /// + /// Fired when the global commands are registered. + /// + public event AsyncEventHandler GlobalApplicationCommandsRegistered + { + add => this._globalApplicationCommandsRegistered.Register(value); + remove => this._globalApplicationCommandsRegistered.Unregister(value); + } + private AsyncEvent _globalApplicationCommandsRegistered; + + /// + /// Used for RegisterCommands and the event. + /// + internal async Task UpdateAsync() + { + //Only update for shard 0 + if (this.Client.ShardId == 0) { - //Only update for shard 0 - if (this.Client.ShardId == 0) - { - GlobalDiscordCommands = new(); - GuildDiscordCommands = new(); + GlobalDiscordCommands = new(); + GuildDiscordCommands = new(); - var commandsPending = this._updateList.Select(x => x.Key).Distinct(); - s_expectedCount = commandsPending.Count(); + var commandsPending = this._updateList.Select(x => x.Key).Distinct(); + s_expectedCount = commandsPending.Count(); - this.Client.Logger.Log(ApplicationCommandsLogLevel, $"Expected Count: {s_expectedCount}"); + this.Client.Logger.Log(ApplicationCommandsLogLevel, $"Expected Count: {s_expectedCount}"); - List failedGuilds = new(); - IEnumerable globalCommands = null; - globalCommands = await this.Client.GetGlobalApplicationCommandsAsync(Configuration?.EnableLocalization ?? false) ?? null; - var guilds = CheckAllGuilds ? this.Client.Guilds?.Keys : this._updateList.Select(x => x.Key)?.Distinct().Where(x => x != null)?.Select(x => x.Value); + List failedGuilds = new(); + IEnumerable globalCommands = null; + globalCommands = await this.Client.GetGlobalApplicationCommandsAsync(Configuration?.EnableLocalization ?? false) ?? null; + var guilds = CheckAllGuilds ? this.Client.Guilds?.Keys : this._updateList.Select(x => x.Key)?.Distinct().Where(x => x != null)?.Select(x => x.Value); - foreach (var guild in guilds) + foreach (var guild in guilds) + { + IEnumerable commands = null; + var unauthorized = false; + try { - IEnumerable commands = null; - var unauthorized = false; - try - { - commands = await this.Client.GetGuildApplicationCommandsAsync(guild, Configuration?.EnableLocalization ?? false) ?? null; - } - catch (UnauthorizedException) - { - unauthorized = true; - } - finally - { - if (!unauthorized && commands != null && commands.Any()) - GuildDiscordCommands.Add(guild, commands.ToList()); - else if (!unauthorized) - GuildDiscordCommands.Add(guild, null); - else - failedGuilds.Add(guild); - } + commands = await this.Client.GetGuildApplicationCommandsAsync(guild, Configuration?.EnableLocalization ?? false) ?? null; } - - //Default should be to add the help and slash commands can be added without setting any configuration - //so this should still add the default help - if (Configuration is null || (Configuration is not null && Configuration.EnableDefaultHelp)) + catch (UnauthorizedException) { - this._updateList.Add(new KeyValuePair - (null, new ApplicationCommandsModuleConfiguration(typeof(DefaultHelpModule)))); - commandsPending = this._updateList.Select(x => x.Key).Distinct(); + unauthorized = true; } - - if (globalCommands != null && globalCommands.Any()) - GlobalDiscordCommands.AddRange(globalCommands); - - foreach (var key in commandsPending.ToList()) + finally { - this.Client.Logger.LogInformation(key.HasValue ? $"Registering commands in guild {key.Value}" : "Registering global commands."); - await this.RegisterCommands(this._updateList.Where(x => x.Key == key).Select(x => x.Value), key); + if (!unauthorized && commands != null && commands.Any()) + GuildDiscordCommands.Add(guild, commands.ToList()); + else if (!unauthorized) + GuildDiscordCommands.Add(guild, null); + else + failedGuilds.Add(guild); } + } + + //Default should be to add the help and slash commands can be added without setting any configuration + //so this should still add the default help + if (Configuration is null || (Configuration is not null && Configuration.EnableDefaultHelp)) + { + this._updateList.Add(new KeyValuePair + (null, new ApplicationCommandsModuleConfiguration(typeof(DefaultHelpModule)))); + commandsPending = this._updateList.Select(x => x.Key).Distinct(); + } - this._missingScopeGuildIds = failedGuilds; + if (globalCommands != null && globalCommands.Any()) + GlobalDiscordCommands.AddRange(globalCommands); - await this._applicationCommandsModuleReady.InvokeAsync(this, new ApplicationCommandsModuleReadyEventArgs(Configuration?.ServiceProvider) - { - Handled = true, - GuildsWithoutScope = failedGuilds - }); + foreach (var key in commandsPending.ToList()) + { + this.Client.Logger.LogInformation(key.HasValue ? $"Registering commands in guild {key.Value}" : "Registering global commands."); + await this.RegisterCommands(this._updateList.Where(x => x.Key == key).Select(x => x.Value), key); } + + this._missingScopeGuildIds = failedGuilds; + + await this._applicationCommandsModuleReady.InvokeAsync(this, new ApplicationCommandsModuleReadyEventArgs(Configuration?.ServiceProvider) + { + Handled = true, + GuildsWithoutScope = failedGuilds + }); } + } - /// - /// Method for registering commands for a target from modules. - /// - /// The types. - /// The optional guild id. - private async Task RegisterCommands(IEnumerable types, ulong? guildId) - { - //Initialize empty lists to be added to the global ones at the end - var commandMethods = new List(); - var groupCommands = new List(); - var subGroupCommands = new List(); - var contextMenuCommands = new List(); - var updateList = new List(); + /// + /// Method for registering commands for a target from modules. + /// + /// The types. + /// The optional guild id. + private async Task RegisterCommands(IEnumerable types, ulong? guildId) + { + //Initialize empty lists to be added to the global ones at the end + var commandMethods = new List(); + var groupCommands = new List(); + var subGroupCommands = new List(); + var contextMenuCommands = new List(); + var updateList = new List(); - var commandTypeSources = new List>(); + var commandTypeSources = new List>(); - //Iterates over all the modules - foreach (var config in types) + //Iterates over all the modules + foreach (var config in types) + { + var type = config.Type; + try { - var type = config.Type; - try - { - var module = type.GetTypeInfo(); - var classes = new List(); + var module = type.GetTypeInfo(); + var classes = new List(); - var ctx = new ApplicationCommandsTranslationContext(type, module.FullName); - config.Translations?.Invoke(ctx); + var ctx = new ApplicationCommandsTranslationContext(type, module.FullName); + config.Translations?.Invoke(ctx); - //Add module to classes list if it's a group - if (module.GetCustomAttribute() != null) - { - classes.Add(module); - } - else - { - //Otherwise add the nested groups - classes = module.DeclaredNestedTypes.Where(x => x.GetCustomAttribute() != null).ToList(); - } + //Add module to classes list if it's a group + if (module.GetCustomAttribute() != null) + { + classes.Add(module); + } + else + { + //Otherwise add the nested groups + classes = module.DeclaredNestedTypes.Where(x => x.GetCustomAttribute() != null).ToList(); + } - List groupTranslations = null; + List groupTranslations = null; - if (!string.IsNullOrEmpty(ctx.Translations)) - { - groupTranslations = JsonConvert.DeserializeObject>(ctx.Translations); - } + if (!string.IsNullOrEmpty(ctx.Translations)) + { + groupTranslations = JsonConvert.DeserializeObject>(ctx.Translations); + } - var slashGroupsTuple = NestedCommandWorker.ParseSlashGroupsAsync(type, classes, guildId, groupTranslations).Result; + var slashGroupsTuple = NestedCommandWorker.ParseSlashGroupsAsync(type, classes, guildId, groupTranslations).Result; - if (slashGroupsTuple.applicationCommands != null && slashGroupsTuple.applicationCommands.Any()) - updateList.AddRange(slashGroupsTuple.applicationCommands); + if (slashGroupsTuple.applicationCommands != null && slashGroupsTuple.applicationCommands.Any()) + updateList.AddRange(slashGroupsTuple.applicationCommands); - if (slashGroupsTuple.commandTypeSources != null && slashGroupsTuple.commandTypeSources.Any()) - commandTypeSources.AddRange(slashGroupsTuple.commandTypeSources); + if (slashGroupsTuple.commandTypeSources != null && slashGroupsTuple.commandTypeSources.Any()) + commandTypeSources.AddRange(slashGroupsTuple.commandTypeSources); - if (slashGroupsTuple.singletonModules != null && slashGroupsTuple.singletonModules.Any()) - s_singletonModules.AddRange(slashGroupsTuple.singletonModules); + if (slashGroupsTuple.singletonModules != null && slashGroupsTuple.singletonModules.Any()) + s_singletonModules.AddRange(slashGroupsTuple.singletonModules); - if (slashGroupsTuple.groupCommands != null && slashGroupsTuple.groupCommands.Any()) - groupCommands.AddRange(slashGroupsTuple.groupCommands); + if (slashGroupsTuple.groupCommands != null && slashGroupsTuple.groupCommands.Any()) + groupCommands.AddRange(slashGroupsTuple.groupCommands); - if (slashGroupsTuple.subGroupCommands != null && slashGroupsTuple.subGroupCommands.Any()) - subGroupCommands.AddRange(slashGroupsTuple.subGroupCommands); + if (slashGroupsTuple.subGroupCommands != null && slashGroupsTuple.subGroupCommands.Any()) + subGroupCommands.AddRange(slashGroupsTuple.subGroupCommands); - //Handles methods and context menus, only if the module isn't a group itself - if (module.GetCustomAttribute() == null) - { - List commandTranslations = null; + //Handles methods and context menus, only if the module isn't a group itself + if (module.GetCustomAttribute() == null) + { + List commandTranslations = null; - if (!string.IsNullOrEmpty(ctx.Translations)) - { - commandTranslations = JsonConvert.DeserializeObject>(ctx.Translations); - } + if (!string.IsNullOrEmpty(ctx.Translations)) + { + commandTranslations = JsonConvert.DeserializeObject>(ctx.Translations); + } - //Slash commands - var methods = module.DeclaredMethods.Where(x => x.GetCustomAttribute() != null); + //Slash commands + var methods = module.DeclaredMethods.Where(x => x.GetCustomAttribute() != null); - var slashCommands = CommandWorker.ParseBasicSlashCommandsAsync(type, methods, guildId, commandTranslations).Result; + var slashCommands = CommandWorker.ParseBasicSlashCommandsAsync(type, methods, guildId, commandTranslations).Result; - if (slashCommands.applicationCommands != null && slashCommands.applicationCommands.Any()) - updateList.AddRange(slashCommands.applicationCommands); + if (slashCommands.applicationCommands != null && slashCommands.applicationCommands.Any()) + updateList.AddRange(slashCommands.applicationCommands); - if (slashCommands.commandTypeSources != null && slashCommands.commandTypeSources.Any()) - commandTypeSources.AddRange(slashCommands.commandTypeSources); + if (slashCommands.commandTypeSources != null && slashCommands.commandTypeSources.Any()) + commandTypeSources.AddRange(slashCommands.commandTypeSources); - if (slashCommands.commandMethods != null && slashCommands.commandMethods.Any()) - commandMethods.AddRange(slashCommands.commandMethods); + if (slashCommands.commandMethods != null && slashCommands.commandMethods.Any()) + commandMethods.AddRange(slashCommands.commandMethods); - //Context Menus - var contextMethods = module.DeclaredMethods.Where(x => x.GetCustomAttribute() != null); + //Context Menus + var contextMethods = module.DeclaredMethods.Where(x => x.GetCustomAttribute() != null); - var contextCommands = CommandWorker.ParseContextMenuCommands(type, contextMethods, commandTranslations).Result; + var contextCommands = CommandWorker.ParseContextMenuCommands(type, contextMethods, commandTranslations).Result; - if (contextCommands.applicationCommands != null && contextCommands.applicationCommands.Any()) - updateList.AddRange(contextCommands.applicationCommands); + if (contextCommands.applicationCommands != null && contextCommands.applicationCommands.Any()) + updateList.AddRange(contextCommands.applicationCommands); - if (contextCommands.commandTypeSources != null && contextCommands.commandTypeSources.Any()) - commandTypeSources.AddRange(contextCommands.commandTypeSources); + if (contextCommands.commandTypeSources != null && contextCommands.commandTypeSources.Any()) + commandTypeSources.AddRange(contextCommands.commandTypeSources); - if (contextCommands.contextMenuCommands != null && contextCommands.contextMenuCommands.Any()) - contextMenuCommands.AddRange(contextCommands.contextMenuCommands); + if (contextCommands.contextMenuCommands != null && contextCommands.contextMenuCommands.Any()) + contextMenuCommands.AddRange(contextCommands.contextMenuCommands); - //Accounts for lifespans - if (module.GetCustomAttribute() != null && module.GetCustomAttribute().Lifespan == ApplicationCommandModuleLifespan.Singleton) - { - s_singletonModules.Add(CreateInstance(module, Configuration?.ServiceProvider)); - } + //Accounts for lifespans + if (module.GetCustomAttribute() != null && module.GetCustomAttribute().Lifespan == ApplicationCommandModuleLifespan.Singleton) + { + s_singletonModules.Add(CreateInstance(module, Configuration?.ServiceProvider)); } } - catch (Exception ex) - { - if (ex is BadRequestException brex) - this.Client.Logger.LogCritical(brex, $"There was an error registering application commands: {brex.JsonMessage}"); - else - this.Client.Logger.LogCritical(ex, $"There was an error parsing the application commands"); - s_errored = true; - } } - if (!s_errored) + catch (Exception ex) { + if (ex is BadRequestException brex) + this.Client.Logger.LogCritical(brex, $"There was an error registering application commands: {brex.JsonMessage}"); + else + this.Client.Logger.LogCritical(ex, $"There was an error parsing the application commands"); + s_errored = true; + } + } + if (!s_errored) + { + try + { + List commands = new(); + try { - List commands = new(); - - try + if (guildId == null) { - if (guildId == null) + if (updateList != null && updateList.Any()) { - if (updateList != null && updateList.Any()) - { - var regCommands = RegistrationWorker.RegisterGlobalCommandsAsync(updateList).Result; - var actualCommands = regCommands.Distinct().ToList(); - commands.AddRange(actualCommands); - GlobalCommandsInternal.AddRange(actualCommands); - } - else + var regCommands = RegistrationWorker.RegisterGlobalCommandsAsync(updateList).Result; + var actualCommands = regCommands.Distinct().ToList(); + commands.AddRange(actualCommands); + GlobalCommandsInternal.AddRange(actualCommands); + } + else + { + foreach (var cmd in GlobalDiscordCommands) { - foreach (var cmd in GlobalDiscordCommands) + try { - try - { - await this.Client.DeleteGlobalApplicationCommandAsync(cmd.Id); - } - catch (NotFoundException) - { - this.Client.Logger.Log(ApplicationCommandsLogLevel, $"Could not delete global command {cmd.Id}. Please clean up manually"); - } + await this.Client.DeleteGlobalApplicationCommandAsync(cmd.Id); } + catch (NotFoundException) + { + this.Client.Logger.Log(ApplicationCommandsLogLevel, $"Could not delete global command {cmd.Id}. Please clean up manually"); + } + } + } + } + else + { + if (updateList != null && updateList.Any()) + { + var regCommands = RegistrationWorker.RegisterGuilldCommandsAsync(guildId.Value, updateList).Result; + var actualCommands = regCommands.Distinct().ToList(); + commands.AddRange(actualCommands); + GuildCommandsInternal.Add(guildId.Value, actualCommands); + if (this.Client.Guilds.TryGetValue(guildId.Value, out var guild)) + { + guild.InternalRegisteredApplicationCommands = new(); + guild.InternalRegisteredApplicationCommands.AddRange(actualCommands); } + } else { - if (updateList != null && updateList.Any()) + foreach (var cmd in GuildDiscordCommands[guildId.Value]) { - var regCommands = RegistrationWorker.RegisterGuilldCommandsAsync(guildId.Value, updateList).Result; - var actualCommands = regCommands.Distinct().ToList(); - commands.AddRange(actualCommands); - GuildCommandsInternal.Add(guildId.Value, actualCommands); - if (this.Client.Guilds.TryGetValue(guildId.Value, out var guild)) + try { - guild.InternalRegisteredApplicationCommands = new(); - guild.InternalRegisteredApplicationCommands.AddRange(actualCommands); + await this.Client.DeleteGuildApplicationCommandAsync(guildId.Value, cmd.Id); } - - } - else - { - foreach (var cmd in GuildDiscordCommands[guildId.Value]) + catch (NotFoundException) { - try - { - await this.Client.DeleteGuildApplicationCommandAsync(guildId.Value, cmd.Id); - } - catch (NotFoundException) - { - this.Client.Logger.Log(ApplicationCommandsLogLevel, $"Could not delete guild command {cmd.Id} in guild {guildId.Value}. Please clean up manually"); - } + this.Client.Logger.Log(ApplicationCommandsLogLevel, $"Could not delete guild command {cmd.Id} in guild {guildId.Value}. Please clean up manually"); } } } } - catch (UnauthorizedException ex) - { - this.Client.Logger.LogError($"Could not register application commands for guild {guildId}.\nError: {ex.JsonMessage}"); - return; - } - //Creates a guild command if a guild id is specified, otherwise global - //Checks against the ids and adds them to the command method lists - foreach (var command in commands) - { - if (commandMethods.GetFirstValueWhere(x => x.Name == command.Name, out var com)) - com.CommandId = command.Id; - else if (groupCommands.GetFirstValueWhere(x => x.Name == command.Name, out var groupCom)) - groupCom.CommandId = command.Id; - else if (subGroupCommands.GetFirstValueWhere(x => x.Name == command.Name, out var subCom)) - subCom.CommandId = command.Id; - else if (contextMenuCommands.GetFirstValueWhere(x => x.Name == command.Name, out var cmCom)) - cmCom.CommandId = command.Id; - } + } + catch (UnauthorizedException ex) + { + this.Client.Logger.LogError($"Could not register application commands for guild {guildId}.\nError: {ex.JsonMessage}"); + return; + } + //Creates a guild command if a guild id is specified, otherwise global + //Checks against the ids and adds them to the command method lists + foreach (var command in commands) + { + if (commandMethods.GetFirstValueWhere(x => x.Name == command.Name, out var com)) + com.CommandId = command.Id; + else if (groupCommands.GetFirstValueWhere(x => x.Name == command.Name, out var groupCom)) + groupCom.CommandId = command.Id; + else if (subGroupCommands.GetFirstValueWhere(x => x.Name == command.Name, out var subCom)) + subCom.CommandId = command.Id; + else if (contextMenuCommands.GetFirstValueWhere(x => x.Name == command.Name, out var cmCom)) + cmCom.CommandId = command.Id; + } - //Adds to the global lists finally - s_commandMethods.AddRange(commandMethods); - s_groupCommands.AddRange(groupCommands); - s_subGroupCommands.AddRange(subGroupCommands); - s_contextMenuCommands.AddRange(contextMenuCommands); + //Adds to the global lists finally + s_commandMethods.AddRange(commandMethods); + s_groupCommands.AddRange(groupCommands); + s_subGroupCommands.AddRange(subGroupCommands); + s_contextMenuCommands.AddRange(contextMenuCommands); - s_registeredCommands.Add(new KeyValuePair>(guildId, commands.ToList())); + s_registeredCommands.Add(new KeyValuePair>(guildId, commands.ToList())); - foreach (var command in commandMethods) - { - var app = types.First(t => t.Type == command.Method.DeclaringType); - } + foreach (var command in commandMethods) + { + var app = types.First(t => t.Type == command.Method.DeclaringType); + } - this.Client.Logger.Log(ApplicationCommandsLogLevel, $"Expected Count: {s_expectedCount}\nCurrent Count: {s_registrationCount}"); + this.Client.Logger.Log(ApplicationCommandsLogLevel, $"Expected Count: {s_expectedCount}\nCurrent Count: {s_registrationCount}"); - if (guildId.HasValue) - { - await this._guildApplicationCommandsRegistered.InvokeAsync(this, new GuildApplicationCommandsRegisteredEventArgs(Configuration?.ServiceProvider) - { - Handled = true, - GuildId = guildId.Value, - RegisteredCommands = GuildCommandsInternal.Any(c => c.Key == guildId.Value) ? GuildCommandsInternal.FirstOrDefault(c => c.Key == guildId.Value).Value : null - }); - } - else + if (guildId.HasValue) + { + await this._guildApplicationCommandsRegistered.InvokeAsync(this, new GuildApplicationCommandsRegisteredEventArgs(Configuration?.ServiceProvider) { - await this._globalApplicationCommandsRegistered.InvokeAsync(this, new GlobalApplicationCommandsRegisteredEventArgs(Configuration?.ServiceProvider) - { - Handled = true, - RegisteredCommands = GlobalCommandsInternal - }); - } - - s_registrationCount++; - this.CheckRegistrationStartup(ManOr); + Handled = true, + GuildId = guildId.Value, + RegisteredCommands = GuildCommandsInternal.Any(c => c.Key == guildId.Value) ? GuildCommandsInternal.FirstOrDefault(c => c.Key == guildId.Value).Value : null + }); } - catch (Exception ex) + else { - if (ex is BadRequestException brex) - this.Client.Logger.LogCritical(brex, $"There was an error registering application commands: {brex.JsonMessage}"); - else - this.Client.Logger.LogCritical(ex, $"There was an general error registering application commands"); - s_errored = true; + await this._globalApplicationCommandsRegistered.InvokeAsync(this, new GlobalApplicationCommandsRegisteredEventArgs(Configuration?.ServiceProvider) + { + Handled = true, + RegisteredCommands = GlobalCommandsInternal + }); } + + s_registrationCount++; + this.CheckRegistrationStartup(ManOr); + } + catch (Exception ex) + { + if (ex is BadRequestException brex) + this.Client.Logger.LogCritical(brex, $"There was an error registering application commands: {brex.JsonMessage}"); + else + this.Client.Logger.LogCritical(ex, $"There was an general error registering application commands"); + s_errored = true; } } + } - private async void CheckRegistrationStartup(bool man = false) - { - this.Client.Logger.Log(ApplicationCommandsLogLevel, $"Checking counts...\n\nExpected Count: {s_expectedCount}\nCurrent Count: {s_registrationCount}"); + private async void CheckRegistrationStartup(bool man = false) + { + this.Client.Logger.Log(ApplicationCommandsLogLevel, $"Checking counts...\n\nExpected Count: {s_expectedCount}\nCurrent Count: {s_registrationCount}"); - if ((s_registrationCount == s_expectedCount) || man) + if ((s_registrationCount == s_expectedCount) || man) + { + await this._applicationCommandsModuleStartupFinished.InvokeAsync(this, new ApplicationCommandsModuleStartupFinishedEventArgs(Configuration?.ServiceProvider) { - await this._applicationCommandsModuleStartupFinished.InvokeAsync(this, new ApplicationCommandsModuleStartupFinishedEventArgs(Configuration?.ServiceProvider) - { - Handled = true, - RegisteredGlobalCommands = GlobalCommandsInternal, - RegisteredGuildCommands = GuildCommandsInternal, - GuildsWithoutScope = this._missingScopeGuildIds - }); + Handled = true, + RegisteredGlobalCommands = GlobalCommandsInternal, + RegisteredGuildCommands = GuildCommandsInternal, + GuildsWithoutScope = this._missingScopeGuildIds + }); - this.FinishedRegistration(); - } + this.FinishedRegistration(); } + } - /// - /// Interaction handler. - /// - /// The client. - /// The event args. - private Task InteractionHandler(DiscordClient client, InteractionCreateEventArgs e) + /// + /// Interaction handler. + /// + /// The client. + /// The event args. + private Task InteractionHandler(DiscordClient client, InteractionCreateEventArgs e) + { + _ = Task.Run(async () => { - _ = Task.Run(async () => + if (e.Interaction.Type == InteractionType.ApplicationCommand) { - if (e.Interaction.Type == InteractionType.ApplicationCommand) + //Creates the context + var context = new InteractionContext { - //Creates the context - var context = new InteractionContext - { - Interaction = e.Interaction, - Channel = e.Interaction.Channel, - Guild = e.Interaction.Guild, - User = e.Interaction.User, - Client = client, - ApplicationCommandsExtension = this, - CommandName = e.Interaction.Data.Name, - InteractionId = e.Interaction.Id, - Token = e.Interaction.Token, - Services = Configuration?.ServiceProvider, - ResolvedUserMentions = e.Interaction.Data.Resolved?.Users?.Values.ToList(), - ResolvedRoleMentions = e.Interaction.Data.Resolved?.Roles?.Values.ToList(), - ResolvedChannelMentions = e.Interaction.Data.Resolved?.Channels?.Values.ToList(), - ResolvedAttachments = e.Interaction.Data.Resolved?.Attachments?.Values.ToList(), - Type = ApplicationCommandType.ChatInput, - Locale = e.Interaction.Locale, - GuildLocale = e.Interaction.GuildLocale - }; - - try - { - if (s_errored) - throw new InvalidOperationException("Slash commands failed to register properly on startup."); - - var methods = s_commandMethods.Where(x => x.CommandId == e.Interaction.Data.Id); - var groups = s_groupCommands.Where(x => x.CommandId == e.Interaction.Data.Id); - var subgroups = s_subGroupCommands.Where(x => x.CommandId == e.Interaction.Data.Id); - if (!methods.Any() && !groups.Any() && !subgroups.Any()) - throw new InvalidOperationException("A slash command was executed, but no command was registered for it."); - - if (methods.Any()) - { - var method = methods.First().Method; - - var args = await this.ResolveInteractionCommandParameters(e, context, method, e.Interaction.Data.Options); - - await this.RunCommandAsync(context, method, args); - } - else if (groups.Any()) - { - var command = e.Interaction.Data.Options.First(); - var method = groups.First().Methods.First(x => x.Key == command.Name).Value; - - var args = await this.ResolveInteractionCommandParameters(e, context, method, e.Interaction.Data.Options.First().Options); - - await this.RunCommandAsync(context, method, args); - } - else if (subgroups.Any()) - { - var command = e.Interaction.Data.Options.First(); - var group = subgroups.First().SubCommands.First(x => x.Name == command.Name); - - var method = group.Methods.First(x => x.Key == command.Options.First().Name).Value; - - var args = await this.ResolveInteractionCommandParameters(e, context, method, e.Interaction.Data.Options.First().Options.First().Options); - - await this.RunCommandAsync(context, method, args); - } + Interaction = e.Interaction, + Channel = e.Interaction.Channel, + Guild = e.Interaction.Guild, + User = e.Interaction.User, + Client = client, + ApplicationCommandsExtension = this, + CommandName = e.Interaction.Data.Name, + InteractionId = e.Interaction.Id, + Token = e.Interaction.Token, + Services = Configuration?.ServiceProvider, + ResolvedUserMentions = e.Interaction.Data.Resolved?.Users?.Values.ToList(), + ResolvedRoleMentions = e.Interaction.Data.Resolved?.Roles?.Values.ToList(), + ResolvedChannelMentions = e.Interaction.Data.Resolved?.Channels?.Values.ToList(), + ResolvedAttachments = e.Interaction.Data.Resolved?.Attachments?.Values.ToList(), + Type = ApplicationCommandType.ChatInput, + Locale = e.Interaction.Locale, + GuildLocale = e.Interaction.GuildLocale + }; - await this._slashExecuted.InvokeAsync(this, new SlashCommandExecutedEventArgs(this.Client.ServiceProvider) { Context = context }); - } - catch (Exception ex) - { - await this._slashError.InvokeAsync(this, new SlashCommandErrorEventArgs(this.Client.ServiceProvider) { Context = context, Exception = ex }); - } - } - else if (e.Interaction.Type == InteractionType.AutoComplete) + try { if (s_errored) throw new InvalidOperationException("Slash commands failed to register properly on startup."); var methods = s_commandMethods.Where(x => x.CommandId == e.Interaction.Data.Id); var groups = s_groupCommands.Where(x => x.CommandId == e.Interaction.Data.Id); var subgroups = s_subGroupCommands.Where(x => x.CommandId == e.Interaction.Data.Id); if (!methods.Any() && !groups.Any() && !subgroups.Any()) - throw new InvalidOperationException("An autocomplete interaction was created, but no command was registered for it."); + throw new InvalidOperationException("A slash command was executed, but no command was registered for it."); - try + if (methods.Any()) { - if (methods.Any()) - { - var focusedOption = e.Interaction.Data.Options.First(o => o.Focused); - var method = methods.First().Method; + var method = methods.First().Method; - var option = method.GetParameters().Skip(1).First(p => p.GetCustomAttribute().Name == focusedOption.Name); - var provider = option.GetCustomAttribute().ProviderType; - var providerMethod = provider.GetMethod(nameof(IAutocompleteProvider.Provider)); - var providerInstance = Activator.CreateInstance(provider); + var args = await this.ResolveInteractionCommandParameters(e, context, method, e.Interaction.Data.Options); - var context = new AutocompleteContext - { - Interaction = e.Interaction, - Client = this.Client, - Services = Configuration?.ServiceProvider, - ApplicationCommandsExtension = this, - Guild = e.Interaction.Guild, - Channel = e.Interaction.Channel, - User = e.Interaction.User, - Options = e.Interaction.Data.Options.ToList(), - FocusedOption = focusedOption, - Locale = e.Interaction.Locale, - GuildLocale = e.Interaction.GuildLocale - }; - - var choices = await (Task>) providerMethod.Invoke(providerInstance, new[] { context }); - await e.Interaction.CreateResponseAsync(InteractionResponseType.AutoCompleteResult, new DiscordInteractionResponseBuilder().AddAutoCompleteChoices(choices)); - } - else if (groups.Any()) - { - var command = e.Interaction.Data.Options.First(); - var group = groups.First().Methods.First(x => x.Key == command.Name).Value; + await this.RunCommandAsync(context, method, args); + } + else if (groups.Any()) + { + var command = e.Interaction.Data.Options.First(); + var method = groups.First().Methods.First(x => x.Key == command.Name).Value; - var focusedOption = command.Options.First(o => o.Focused); - var option = group.GetParameters().Skip(1).First(p => p.GetCustomAttribute().Name == focusedOption.Name); - var provider = option.GetCustomAttribute().ProviderType; - var providerMethod = provider.GetMethod(nameof(IAutocompleteProvider.Provider)); - var providerInstance = Activator.CreateInstance(provider); + var args = await this.ResolveInteractionCommandParameters(e, context, method, e.Interaction.Data.Options.First().Options); - var context = new AutocompleteContext - { - Interaction = e.Interaction, - Services = Configuration?.ServiceProvider, - ApplicationCommandsExtension = this, - Guild = e.Interaction.Guild, - Channel = e.Interaction.Channel, - User = e.Interaction.User, - Options = command.Options.ToList(), - FocusedOption = focusedOption, - Locale = e.Interaction.Locale, - GuildLocale = e.Interaction.GuildLocale - }; - - var choices = await (Task>) providerMethod.Invoke(providerInstance, new[] { context }); - await e.Interaction.CreateResponseAsync(InteractionResponseType.AutoCompleteResult, new DiscordInteractionResponseBuilder().AddAutoCompleteChoices(choices)); - } - else if (subgroups.Any()) - { - var command = e.Interaction.Data.Options.First(); - var group = subgroups.First().SubCommands.First(x => x.Name == command.Name).Methods.First(x => x.Key == command.Options.First().Name).Value; + await this.RunCommandAsync(context, method, args); + } + else if (subgroups.Any()) + { + var command = e.Interaction.Data.Options.First(); + var group = subgroups.First().SubCommands.First(x => x.Name == command.Name); - var focusedOption = command.Options.First().Options.First(o => o.Focused); + var method = group.Methods.First(x => x.Key == command.Options.First().Name).Value; - var option = group.GetParameters().Skip(1).First(p => p.GetCustomAttribute().Name == focusedOption.Name); - var provider = option.GetCustomAttribute().ProviderType; - var providerMethod = provider.GetMethod(nameof(IAutocompleteProvider.Provider)); - var providerInstance = Activator.CreateInstance(provider); + var args = await this.ResolveInteractionCommandParameters(e, context, method, e.Interaction.Data.Options.First().Options.First().Options); - var context = new AutocompleteContext - { - Interaction = e.Interaction, - Services = Configuration?.ServiceProvider, - ApplicationCommandsExtension = this, - Guild = e.Interaction.Guild, - Channel = e.Interaction.Channel, - User = e.Interaction.User, - Options = command.Options.First().Options.ToList(), - FocusedOption = focusedOption, - Locale = e.Interaction.Locale, - GuildLocale = e.Interaction.GuildLocale - }; - - var choices = await (Task>) providerMethod.Invoke(providerInstance, new[] { context }); - await e.Interaction.CreateResponseAsync(InteractionResponseType.AutoCompleteResult, new DiscordInteractionResponseBuilder().AddAutoCompleteChoices(choices)); - } + await this.RunCommandAsync(context, method, args); } - catch (Exception ex) - { - this.Client.Logger.LogError(ex, "Error in autocomplete interaction"); - } - } - }); - return Task.CompletedTask; - } - /// - /// Context menu handler. - /// - /// The client. - /// The event args. - private Task ContextMenuHandler(DiscordClient client, ContextMenuInteractionCreateEventArgs e) - { - _ = Task.Run(async () => - { - //Creates the context - var context = new ContextMenuContext + await this._slashExecuted.InvokeAsync(this, new SlashCommandExecutedEventArgs(this.Client.ServiceProvider) { Context = context }); + } + catch (Exception ex) { - Interaction = e.Interaction, - Channel = e.Interaction.Channel, - Client = client, - Services = Configuration?.ServiceProvider, - CommandName = e.Interaction.Data.Name, - ApplicationCommandsExtension = this, - Guild = e.Interaction.Guild, - InteractionId = e.Interaction.Id, - User = e.Interaction.User, - Token = e.Interaction.Token, - TargetUser = e.TargetUser, - TargetMessage = e.TargetMessage, - Type = e.Type, - Locale = e.Interaction.Locale, - GuildLocale = e.Interaction.GuildLocale - }; + await this._slashError.InvokeAsync(this, new SlashCommandErrorEventArgs(this.Client.ServiceProvider) { Context = context, Exception = ex }); + } + } + else if (e.Interaction.Type == InteractionType.AutoComplete) + { + if (s_errored) + throw new InvalidOperationException("Slash commands failed to register properly on startup."); + + var methods = s_commandMethods.Where(x => x.CommandId == e.Interaction.Data.Id); + var groups = s_groupCommands.Where(x => x.CommandId == e.Interaction.Data.Id); + var subgroups = s_subGroupCommands.Where(x => x.CommandId == e.Interaction.Data.Id); + if (!methods.Any() && !groups.Any() && !subgroups.Any()) + throw new InvalidOperationException("An autocomplete interaction was created, but no command was registered for it."); try { - if (s_errored) - throw new InvalidOperationException("Context menus failed to register properly on startup."); + if (methods.Any()) + { + var focusedOption = e.Interaction.Data.Options.First(o => o.Focused); + var method = methods.First().Method; + + var option = method.GetParameters().Skip(1).First(p => p.GetCustomAttribute().Name == focusedOption.Name); + var provider = option.GetCustomAttribute().ProviderType; + var providerMethod = provider.GetMethod(nameof(IAutocompleteProvider.Provider)); + var providerInstance = Activator.CreateInstance(provider); + + var context = new AutocompleteContext + { + Interaction = e.Interaction, + Client = this.Client, + Services = Configuration?.ServiceProvider, + ApplicationCommandsExtension = this, + Guild = e.Interaction.Guild, + Channel = e.Interaction.Channel, + User = e.Interaction.User, + Options = e.Interaction.Data.Options.ToList(), + FocusedOption = focusedOption, + Locale = e.Interaction.Locale, + GuildLocale = e.Interaction.GuildLocale + }; + + var choices = await (Task>) providerMethod.Invoke(providerInstance, new[] { context }); + await e.Interaction.CreateResponseAsync(InteractionResponseType.AutoCompleteResult, new DiscordInteractionResponseBuilder().AddAutoCompleteChoices(choices)); + } + else if (groups.Any()) + { + var command = e.Interaction.Data.Options.First(); + var group = groups.First().Methods.First(x => x.Key == command.Name).Value; + + var focusedOption = command.Options.First(o => o.Focused); + var option = group.GetParameters().Skip(1).First(p => p.GetCustomAttribute().Name == focusedOption.Name); + var provider = option.GetCustomAttribute().ProviderType; + var providerMethod = provider.GetMethod(nameof(IAutocompleteProvider.Provider)); + var providerInstance = Activator.CreateInstance(provider); - //Gets the method for the command - var method = s_contextMenuCommands.FirstOrDefault(x => x.CommandId == e.Interaction.Data.Id); + var context = new AutocompleteContext + { + Interaction = e.Interaction, + Services = Configuration?.ServiceProvider, + ApplicationCommandsExtension = this, + Guild = e.Interaction.Guild, + Channel = e.Interaction.Channel, + User = e.Interaction.User, + Options = command.Options.ToList(), + FocusedOption = focusedOption, + Locale = e.Interaction.Locale, + GuildLocale = e.Interaction.GuildLocale + }; + + var choices = await (Task>) providerMethod.Invoke(providerInstance, new[] { context }); + await e.Interaction.CreateResponseAsync(InteractionResponseType.AutoCompleteResult, new DiscordInteractionResponseBuilder().AddAutoCompleteChoices(choices)); + } + else if (subgroups.Any()) + { + var command = e.Interaction.Data.Options.First(); + var group = subgroups.First().SubCommands.First(x => x.Name == command.Name).Methods.First(x => x.Key == command.Options.First().Name).Value; - if (method == null) - throw new InvalidOperationException("A context menu was executed, but no command was registered for it."); + var focusedOption = command.Options.First().Options.First(o => o.Focused); - await this.RunCommandAsync(context, method.Method, new[] { context }); + var option = group.GetParameters().Skip(1).First(p => p.GetCustomAttribute().Name == focusedOption.Name); + var provider = option.GetCustomAttribute().ProviderType; + var providerMethod = provider.GetMethod(nameof(IAutocompleteProvider.Provider)); + var providerInstance = Activator.CreateInstance(provider); - await this._contextMenuExecuted.InvokeAsync(this, new ContextMenuExecutedEventArgs(this.Client.ServiceProvider) { Context = context }); + var context = new AutocompleteContext + { + Interaction = e.Interaction, + Services = Configuration?.ServiceProvider, + ApplicationCommandsExtension = this, + Guild = e.Interaction.Guild, + Channel = e.Interaction.Channel, + User = e.Interaction.User, + Options = command.Options.First().Options.ToList(), + FocusedOption = focusedOption, + Locale = e.Interaction.Locale, + GuildLocale = e.Interaction.GuildLocale + }; + + var choices = await (Task>) providerMethod.Invoke(providerInstance, new[] { context }); + await e.Interaction.CreateResponseAsync(InteractionResponseType.AutoCompleteResult, new DiscordInteractionResponseBuilder().AddAutoCompleteChoices(choices)); + } } catch (Exception ex) { - await this._contextMenuErrored.InvokeAsync(this, new ContextMenuErrorEventArgs(this.Client.ServiceProvider) { Context = context, Exception = ex }); + this.Client.Logger.LogError(ex, "Error in autocomplete interaction"); } - }); - - return Task.CompletedTask; - } + } + }); + return Task.CompletedTask; + } - /// - /// Runs a command. - /// - /// The base context. - /// The method info. - /// The arguments. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0066:Convert switch statement to expression", Justification = "")] - internal async Task RunCommandAsync(BaseContext context, MethodInfo method, IEnumerable args) + /// + /// Context menu handler. + /// + /// The client. + /// The event args. + private Task ContextMenuHandler(DiscordClient client, ContextMenuInteractionCreateEventArgs e) + { + _ = Task.Run(async () => { - object classInstance; - - //Accounts for lifespans - var moduleLifespan = (method.DeclaringType.GetCustomAttribute() != null ? method.DeclaringType.GetCustomAttribute()?.Lifespan : ApplicationCommandModuleLifespan.Transient) ?? ApplicationCommandModuleLifespan.Transient; - switch (moduleLifespan) + //Creates the context + var context = new ContextMenuContext { - case ApplicationCommandModuleLifespan.Scoped: - //Accounts for static methods and adds DI - classInstance = method.IsStatic ? ActivatorUtilities.CreateInstance(Configuration?.ServiceProvider.CreateScope().ServiceProvider, method.DeclaringType) : CreateInstance(method.DeclaringType, Configuration?.ServiceProvider.CreateScope().ServiceProvider); - break; - - case ApplicationCommandModuleLifespan.Transient: - //Accounts for static methods and adds DI - classInstance = method.IsStatic ? ActivatorUtilities.CreateInstance(Configuration?.ServiceProvider, method.DeclaringType) : CreateInstance(method.DeclaringType, Configuration?.ServiceProvider); - break; - - //If singleton, gets it from the singleton list - case ApplicationCommandModuleLifespan.Singleton: - classInstance = s_singletonModules.First(x => ReferenceEquals(x.GetType(), method.DeclaringType)); - break; - - default: - throw new Exception($"An unknown {nameof(ApplicationCommandModuleLifespanAttribute)} scope was specified on command {context.CommandName}"); - } - - ApplicationCommandsModule module = null; - if (classInstance is ApplicationCommandsModule mod) - module = mod; - - // Slash commands - if (context is InteractionContext slashContext) + Interaction = e.Interaction, + Channel = e.Interaction.Channel, + Client = client, + Services = Configuration?.ServiceProvider, + CommandName = e.Interaction.Data.Name, + ApplicationCommandsExtension = this, + Guild = e.Interaction.Guild, + InteractionId = e.Interaction.Id, + User = e.Interaction.User, + Token = e.Interaction.Token, + TargetUser = e.TargetUser, + TargetMessage = e.TargetMessage, + Type = e.Type, + Locale = e.Interaction.Locale, + GuildLocale = e.Interaction.GuildLocale + }; + + try { - await this.RunPreexecutionChecksAsync(method, slashContext); + if (s_errored) + throw new InvalidOperationException("Context menus failed to register properly on startup."); - var shouldExecute = await (module?.BeforeSlashExecutionAsync(slashContext) ?? Task.FromResult(true)); + //Gets the method for the command + var method = s_contextMenuCommands.FirstOrDefault(x => x.CommandId == e.Interaction.Data.Id); - if (shouldExecute) - { - if (AutoDeferEnabled) - await context.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource); - await (Task)method.Invoke(classInstance, args.ToArray()); + if (method == null) + throw new InvalidOperationException("A context menu was executed, but no command was registered for it."); - await (module?.AfterSlashExecutionAsync(slashContext) ?? Task.CompletedTask); - } + await this.RunCommandAsync(context, method.Method, new[] { context }); + + await this._contextMenuExecuted.InvokeAsync(this, new ContextMenuExecutedEventArgs(this.Client.ServiceProvider) { Context = context }); } - // Context menus - if (context is ContextMenuContext contextMenuContext) + catch (Exception ex) { - await this.RunPreexecutionChecksAsync(method, contextMenuContext); + await this._contextMenuErrored.InvokeAsync(this, new ContextMenuErrorEventArgs(this.Client.ServiceProvider) { Context = context, Exception = ex }); + } + }); - var shouldExecute = await (module?.BeforeContextMenuExecutionAsync(contextMenuContext) ?? Task.FromResult(true)); + return Task.CompletedTask; + } - if (shouldExecute) - { - if (AutoDeferEnabled) - await context.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource); - await (Task)method.Invoke(classInstance, args.ToArray()); + /// + /// Runs a command. + /// + /// The base context. + /// The method info. + /// The arguments. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0066:Convert switch statement to expression", Justification = "")] + internal async Task RunCommandAsync(BaseContext context, MethodInfo method, IEnumerable args) + { + object classInstance; - await (module?.AfterContextMenuExecutionAsync(contextMenuContext) ?? Task.CompletedTask); - } - } + //Accounts for lifespans + var moduleLifespan = (method.DeclaringType.GetCustomAttribute() != null ? method.DeclaringType.GetCustomAttribute()?.Lifespan : ApplicationCommandModuleLifespan.Transient) ?? ApplicationCommandModuleLifespan.Transient; + switch (moduleLifespan) + { + case ApplicationCommandModuleLifespan.Scoped: + //Accounts for static methods and adds DI + classInstance = method.IsStatic ? ActivatorUtilities.CreateInstance(Configuration?.ServiceProvider.CreateScope().ServiceProvider, method.DeclaringType) : CreateInstance(method.DeclaringType, Configuration?.ServiceProvider.CreateScope().ServiceProvider); + break; + + case ApplicationCommandModuleLifespan.Transient: + //Accounts for static methods and adds DI + classInstance = method.IsStatic ? ActivatorUtilities.CreateInstance(Configuration?.ServiceProvider, method.DeclaringType) : CreateInstance(method.DeclaringType, Configuration?.ServiceProvider); + break; + + //If singleton, gets it from the singleton list + case ApplicationCommandModuleLifespan.Singleton: + classInstance = s_singletonModules.First(x => ReferenceEquals(x.GetType(), method.DeclaringType)); + break; + + default: + throw new Exception($"An unknown {nameof(ApplicationCommandModuleLifespanAttribute)} scope was specified on command {context.CommandName}"); } - /// - /// Property injection - /// - /// The type. - /// The services. - internal static object CreateInstance(Type t, IServiceProvider services) - { - var ti = t.GetTypeInfo(); - var constructors = ti.DeclaredConstructors - .Where(xci => xci.IsPublic) - .ToArray(); + ApplicationCommandsModule module = null; + if (classInstance is ApplicationCommandsModule mod) + module = mod; - if (constructors.Length != 1) - throw new ArgumentException("Specified type does not contain a public constructor or contains more than one public constructor."); + // Slash commands + if (context is InteractionContext slashContext) + { + await this.RunPreexecutionChecksAsync(method, slashContext); - var constructor = constructors[0]; - var constructorArgs = constructor.GetParameters(); - var args = new object[constructorArgs.Length]; + var shouldExecute = await (module?.BeforeSlashExecutionAsync(slashContext) ?? Task.FromResult(true)); - if (constructorArgs.Length != 0 && services == null) - throw new InvalidOperationException("Dependency collection needs to be specified for parameterized constructors."); + if (shouldExecute) + { + if (AutoDeferEnabled) + await context.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource); + await (Task)method.Invoke(classInstance, args.ToArray()); - // inject via constructor - if (constructorArgs.Length != 0) - for (var i = 0; i < args.Length; i++) - args[i] = services.GetRequiredService(constructorArgs[i].ParameterType); + await (module?.AfterSlashExecutionAsync(slashContext) ?? Task.CompletedTask); + } + } + // Context menus + if (context is ContextMenuContext contextMenuContext) + { + await this.RunPreexecutionChecksAsync(method, contextMenuContext); - var moduleInstance = Activator.CreateInstance(t, args); + var shouldExecute = await (module?.BeforeContextMenuExecutionAsync(contextMenuContext) ?? Task.FromResult(true)); - // inject into properties - var props = t.GetRuntimeProperties().Where(xp => xp.CanWrite && xp.SetMethod != null && !xp.SetMethod.IsStatic && xp.SetMethod.IsPublic); - foreach (var prop in props) + if (shouldExecute) { - if (prop.GetCustomAttribute() != null) - continue; - - var service = services.GetService(prop.PropertyType); - if (service == null) - continue; + if (AutoDeferEnabled) + await context.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource); + await (Task)method.Invoke(classInstance, args.ToArray()); - prop.SetValue(moduleInstance, service); + await (module?.AfterContextMenuExecutionAsync(contextMenuContext) ?? Task.CompletedTask); } + } + } - // inject into fields - var fields = t.GetRuntimeFields().Where(xf => !xf.IsInitOnly && !xf.IsStatic && xf.IsPublic); - foreach (var field in fields) - { - if (field.GetCustomAttribute() != null) - continue; + /// + /// Property injection + /// + /// The type. + /// The services. + internal static object CreateInstance(Type t, IServiceProvider services) + { + var ti = t.GetTypeInfo(); + var constructors = ti.DeclaredConstructors + .Where(xci => xci.IsPublic) + .ToArray(); - var service = services.GetService(field.FieldType); - if (service == null) - continue; + if (constructors.Length != 1) + throw new ArgumentException("Specified type does not contain a public constructor or contains more than one public constructor."); - field.SetValue(moduleInstance, service); - } + var constructor = constructors[0]; + var constructorArgs = constructor.GetParameters(); + var args = new object[constructorArgs.Length]; - return moduleInstance; - } + if (constructorArgs.Length != 0 && services == null) + throw new InvalidOperationException("Dependency collection needs to be specified for parameterized constructors."); + + // inject via constructor + if (constructorArgs.Length != 0) + for (var i = 0; i < args.Length; i++) + args[i] = services.GetRequiredService(constructorArgs[i].ParameterType); + + var moduleInstance = Activator.CreateInstance(t, args); - /// - /// Resolves the slash command parameters. - /// - /// The event arguments. - /// The interaction context. - /// The method info. - /// The options. - private async Task> ResolveInteractionCommandParameters(InteractionCreateEventArgs e, InteractionContext context, MethodInfo method, IEnumerable options) + // inject into properties + var props = t.GetRuntimeProperties().Where(xp => xp.CanWrite && xp.SetMethod != null && !xp.SetMethod.IsStatic && xp.SetMethod.IsPublic); + foreach (var prop in props) { - var args = new List { context }; - var parameters = method.GetParameters().Skip(1); + if (prop.GetCustomAttribute() != null) + continue; - foreach (var parameter in parameters) - { - //Accounts for optional arguments without values given - if (parameter.IsOptional && (options == null || (!options?.Any(x => x.Name == parameter.GetCustomAttribute().Name.ToLower()) ?? true))) - args.Add(parameter.DefaultValue); - else - { - var option = options.Single(x => x.Name == parameter.GetCustomAttribute().Name.ToLower()); - - if (parameter.ParameterType == typeof(string)) - args.Add(option.Value.ToString()); - else if (parameter.ParameterType.IsEnum) - args.Add(Enum.Parse(parameter.ParameterType, (string)option.Value)); - else if (parameter.ParameterType == typeof(long) || parameter.ParameterType == typeof(long?)) - args.Add((long?)option.Value); - else if (parameter.ParameterType == typeof(bool) || parameter.ParameterType == typeof(bool?)) - args.Add((bool?)option.Value); - else if (parameter.ParameterType == typeof(double) || parameter.ParameterType == typeof(double?)) - args.Add((double?)option.Value); - else if (parameter.ParameterType == typeof(int) || parameter.ParameterType == typeof(int?)) - args.Add((int?)option.Value); - else if (parameter.ParameterType == typeof(DiscordAttachment)) - { - //Checks through resolved - if (e.Interaction.Data.Resolved.Attachments != null && - e.Interaction.Data.Resolved.Attachments.TryGetValue((ulong)option.Value, out var attachment)) - args.Add(attachment); - else - args.Add(new DiscordAttachment() { Id = (ulong)option.Value, Discord = this.Client.ApiClient.Discord }); - } - else if (parameter.ParameterType == typeof(DiscordUser)) - { - //Checks through resolved - if (e.Interaction.Data.Resolved.Members != null && - e.Interaction.Data.Resolved.Members.TryGetValue((ulong)option.Value, out var member)) - args.Add(member); - else if (e.Interaction.Data.Resolved.Users != null && - e.Interaction.Data.Resolved.Users.TryGetValue((ulong)option.Value, out var user)) - args.Add(user); - else - args.Add(await this.Client.GetUserAsync((ulong)option.Value)); - } - else if (parameter.ParameterType == typeof(DiscordChannel)) - { - //Checks through resolved - if (e.Interaction.Data.Resolved.Channels != null && - e.Interaction.Data.Resolved.Channels.TryGetValue((ulong)option.Value, out var channel)) - args.Add(channel); - else - args.Add(e.Interaction.Guild.GetChannel((ulong)option.Value)); - } - else if (parameter.ParameterType == typeof(DiscordRole)) - { - //Checks through resolved - if (e.Interaction.Data.Resolved.Roles != null && - e.Interaction.Data.Resolved.Roles.TryGetValue((ulong)option.Value, out var role)) - args.Add(role); - else - args.Add(e.Interaction.Guild.GetRole((ulong)option.Value)); - } - else if (parameter.ParameterType == typeof(SnowflakeObject)) - { - //Checks through resolved - if (e.Interaction.Data.Resolved.Roles != null && e.Interaction.Data.Resolved.Roles.TryGetValue((ulong)option.Value, out var role)) - args.Add(role); - else if (e.Interaction.Data.Resolved.Members != null && e.Interaction.Data.Resolved.Members.TryGetValue((ulong)option.Value, out var member)) - args.Add(member); - else if (e.Interaction.Data.Resolved.Users != null && e.Interaction.Data.Resolved.Users.TryGetValue((ulong)option.Value, out var user)) - args.Add(user); - else - throw new ArgumentException("Error resolving mentionable option."); - } - else - throw new ArgumentException($"Error resolving interaction."); - } - } + var service = services.GetService(prop.PropertyType); + if (service == null) + continue; + + prop.SetValue(moduleInstance, service); + } - return args; + // inject into fields + var fields = t.GetRuntimeFields().Where(xf => !xf.IsInitOnly && !xf.IsStatic && xf.IsPublic); + foreach (var field in fields) + { + if (field.GetCustomAttribute() != null) + continue; + + var service = services.GetService(field.FieldType); + if (service == null) + continue; + + field.SetValue(moduleInstance, service); } - /// - /// Runs the pre-execution checks. - /// - /// The method info. - /// The base context. - private async Task RunPreexecutionChecksAsync(MethodInfo method, BaseContext context) + return moduleInstance; + } + + /// + /// Resolves the slash command parameters. + /// + /// The event arguments. + /// The interaction context. + /// The method info. + /// The options. + private async Task> ResolveInteractionCommandParameters(InteractionCreateEventArgs e, InteractionContext context, MethodInfo method, IEnumerable options) + { + var args = new List { context }; + var parameters = method.GetParameters().Skip(1); + + foreach (var parameter in parameters) { - if (context is InteractionContext ctx) + //Accounts for optional arguments without values given + if (parameter.IsOptional && (options == null || (!options?.Any(x => x.Name == parameter.GetCustomAttribute().Name.ToLower()) ?? true))) + args.Add(parameter.DefaultValue); + else { - //Gets all attributes from parent classes as well and stuff - var attributes = new List(); - attributes.AddRange(method.GetCustomAttributes(true)); - attributes.AddRange(method.DeclaringType.GetCustomAttributes()); - if (method.DeclaringType.DeclaringType != null) + var option = options.Single(x => x.Name == parameter.GetCustomAttribute().Name.ToLower()); + + if (parameter.ParameterType == typeof(string)) + args.Add(option.Value.ToString()); + else if (parameter.ParameterType.IsEnum) + args.Add(Enum.Parse(parameter.ParameterType, (string)option.Value)); + else if (parameter.ParameterType == typeof(long) || parameter.ParameterType == typeof(long?)) + args.Add((long?)option.Value); + else if (parameter.ParameterType == typeof(bool) || parameter.ParameterType == typeof(bool?)) + args.Add((bool?)option.Value); + else if (parameter.ParameterType == typeof(double) || parameter.ParameterType == typeof(double?)) + args.Add((double?)option.Value); + else if (parameter.ParameterType == typeof(int) || parameter.ParameterType == typeof(int?)) + args.Add((int?)option.Value); + else if (parameter.ParameterType == typeof(DiscordAttachment)) { - attributes.AddRange(method.DeclaringType.DeclaringType.GetCustomAttributes()); - if (method.DeclaringType.DeclaringType.DeclaringType != null) - { - attributes.AddRange(method.DeclaringType.DeclaringType.DeclaringType.GetCustomAttributes()); - } + //Checks through resolved + if (e.Interaction.Data.Resolved.Attachments != null && + e.Interaction.Data.Resolved.Attachments.TryGetValue((ulong)option.Value, out var attachment)) + args.Add(attachment); + else + args.Add(new DiscordAttachment() { Id = (ulong)option.Value, Discord = this.Client.ApiClient.Discord }); } - - var dict = new Dictionary(); - foreach (var att in attributes) + else if (parameter.ParameterType == typeof(DiscordUser)) { - //Runs the check and adds the result to a list - var result = await att.ExecuteChecksAsync(ctx); - dict.Add(att, result); + //Checks through resolved + if (e.Interaction.Data.Resolved.Members != null && + e.Interaction.Data.Resolved.Members.TryGetValue((ulong)option.Value, out var member)) + args.Add(member); + else if (e.Interaction.Data.Resolved.Users != null && + e.Interaction.Data.Resolved.Users.TryGetValue((ulong)option.Value, out var user)) + args.Add(user); + else + args.Add(await this.Client.GetUserAsync((ulong)option.Value)); } - - //Checks if any failed, and throws an exception - if (dict.Any(x => x.Value == false)) - throw new SlashExecutionChecksFailedException { FailedChecks = dict.Where(x => x.Value == false).Select(x => x.Key).ToList() }; - } - if (context is ContextMenuContext cMctx) - { - var attributes = new List(); - attributes.AddRange(method.GetCustomAttributes(true)); - attributes.AddRange(method.DeclaringType.GetCustomAttributes()); - if (method.DeclaringType.DeclaringType != null) + else if (parameter.ParameterType == typeof(DiscordChannel)) { - attributes.AddRange(method.DeclaringType.DeclaringType.GetCustomAttributes()); - if (method.DeclaringType.DeclaringType.DeclaringType != null) - { - attributes.AddRange(method.DeclaringType.DeclaringType.DeclaringType.GetCustomAttributes()); - } + //Checks through resolved + if (e.Interaction.Data.Resolved.Channels != null && + e.Interaction.Data.Resolved.Channels.TryGetValue((ulong)option.Value, out var channel)) + args.Add(channel); + else + args.Add(e.Interaction.Guild.GetChannel((ulong)option.Value)); } - - var dict = new Dictionary(); - foreach (var att in attributes) + else if (parameter.ParameterType == typeof(DiscordRole)) { - //Runs the check and adds the result to a list - var result = await att.ExecuteChecksAsync(cMctx); - dict.Add(att, result); + //Checks through resolved + if (e.Interaction.Data.Resolved.Roles != null && + e.Interaction.Data.Resolved.Roles.TryGetValue((ulong)option.Value, out var role)) + args.Add(role); + else + args.Add(e.Interaction.Guild.GetRole((ulong)option.Value)); } - - //Checks if any failed, and throws an exception - if (dict.Any(x => x.Value == false)) - throw new ContextMenuExecutionChecksFailedException { FailedChecks = dict.Where(x => x.Value == false).Select(x => x.Key).ToList() }; + else if (parameter.ParameterType == typeof(SnowflakeObject)) + { + //Checks through resolved + if (e.Interaction.Data.Resolved.Roles != null && e.Interaction.Data.Resolved.Roles.TryGetValue((ulong)option.Value, out var role)) + args.Add(role); + else if (e.Interaction.Data.Resolved.Members != null && e.Interaction.Data.Resolved.Members.TryGetValue((ulong)option.Value, out var member)) + args.Add(member); + else if (e.Interaction.Data.Resolved.Users != null && e.Interaction.Data.Resolved.Users.TryGetValue((ulong)option.Value, out var user)) + args.Add(user); + else + throw new ArgumentException("Error resolving mentionable option."); + } + else + throw new ArgumentException($"Error resolving interaction."); } } - /// - /// Gets the choice attributes from choice provider. - /// - /// The custom attributes. - /// The optional guild id - private static async Task> GetChoiceAttributesFromProvider(IEnumerable customAttributes, ulong? guildId = null) + return args; + } + + /// + /// Runs the pre-execution checks. + /// + /// The method info. + /// The base context. + private async Task RunPreexecutionChecksAsync(MethodInfo method, BaseContext context) + { + if (context is InteractionContext ctx) { - var choices = new List(); - foreach (var choiceProviderAttribute in customAttributes) + //Gets all attributes from parent classes as well and stuff + var attributes = new List(); + attributes.AddRange(method.GetCustomAttributes(true)); + attributes.AddRange(method.DeclaringType.GetCustomAttributes()); + if (method.DeclaringType.DeclaringType != null) { - var method = choiceProviderAttribute.ProviderType.GetMethod(nameof(IChoiceProvider.Provider)); - - if (method == null) - throw new ArgumentException("ChoiceProviders must inherit from IChoiceProvider."); - else + attributes.AddRange(method.DeclaringType.DeclaringType.GetCustomAttributes()); + if (method.DeclaringType.DeclaringType.DeclaringType != null) { - var instance = Activator.CreateInstance(choiceProviderAttribute.ProviderType); - - // Abstract class offers more properties that can be set - if (choiceProviderAttribute.ProviderType.IsSubclassOf(typeof(ChoiceProvider))) - { - choiceProviderAttribute.ProviderType.GetProperty(nameof(ChoiceProvider.GuildId)) - ?.SetValue(instance, guildId); - - choiceProviderAttribute.ProviderType.GetProperty(nameof(ChoiceProvider.Services)) - ?.SetValue(instance, Configuration.ServiceProvider); - } - - //Gets the choices from the method - var result = await (Task>)method.Invoke(instance, null); - - if (result.Any()) - { - choices.AddRange(result); - } + attributes.AddRange(method.DeclaringType.DeclaringType.DeclaringType.GetCustomAttributes()); } } - return choices; - } - - /// - /// Gets the choice attributes from enum parameter. - /// - /// The enum parameter. - private static List GetChoiceAttributesFromEnumParameter(Type enumParam) - { - var choices = new List(); - foreach (Enum enumValue in Enum.GetValues(enumParam)) + var dict = new Dictionary(); + foreach (var att in attributes) { - choices.Add(new DiscordApplicationCommandOptionChoice(enumValue.GetName(), enumValue.ToString())); + //Runs the check and adds the result to a list + var result = await att.ExecuteChecksAsync(ctx); + dict.Add(att, result); } - return choices; - } - /// - /// Gets the parameter type. - /// - /// The type. - private static ApplicationCommandOptionType GetParameterType(Type type) - { - var parameterType = type == typeof(string) - ? ApplicationCommandOptionType.String - : type == typeof(long) || type == typeof(long?) || type == typeof(int) || type == typeof(int?) - ? ApplicationCommandOptionType.Integer - : type == typeof(bool) || type == typeof(bool?) - ? ApplicationCommandOptionType.Boolean - : type == typeof(double) || type == typeof(double?) - ? ApplicationCommandOptionType.Number - : type == typeof(DiscordAttachment) - ? ApplicationCommandOptionType.Attachment - : type == typeof(DiscordChannel) - ? ApplicationCommandOptionType.Channel - : type == typeof(DiscordUser) - ? ApplicationCommandOptionType.User - : type == typeof(DiscordRole) - ? ApplicationCommandOptionType.Role - : type == typeof(SnowflakeObject) - ? ApplicationCommandOptionType.Mentionable - : type.IsEnum - ? ApplicationCommandOptionType.String - : throw new ArgumentException("Cannot convert type! Argument types must be string, int, long, bool, double, DiscordChannel, DiscordUser, DiscordRole, SnowflakeObject, DiscordAttachment or an Enum."); - return parameterType; + //Checks if any failed, and throws an exception + if (dict.Any(x => x.Value == false)) + throw new SlashExecutionChecksFailedException { FailedChecks = dict.Where(x => x.Value == false).Select(x => x.Key).ToList() }; } - - /// - /// Gets the choice attributes from parameter. - /// - /// The choice attributes. - private static List GetChoiceAttributesFromParameter(IEnumerable choiceAttributes) => - !choiceAttributes.Any() - ? null - : choiceAttributes.Select(att => new DiscordApplicationCommandOptionChoice(att.Name, att.Value)).ToList(); - - /// - /// Parses the parameters. - /// - /// The parameters. - /// The optional guild id. - internal static async Task> ParseParametersAsync(IEnumerable parameters, ulong? guildId) + if (context is ContextMenuContext cMctx) { - var options = new List(); - foreach (var parameter in parameters) + var attributes = new List(); + attributes.AddRange(method.GetCustomAttributes(true)); + attributes.AddRange(method.DeclaringType.GetCustomAttributes()); + if (method.DeclaringType.DeclaringType != null) { - //Gets the attribute - var optionAttribute = parameter.GetCustomAttribute(); - if (optionAttribute == null) - throw new ArgumentException("Arguments must have the Option attribute!"); - - var minimumValue = parameter.GetCustomAttribute()?.Value ?? null; - var maximumValue = parameter.GetCustomAttribute()?.Value ?? null; - - - var autocompleteAttribute = parameter.GetCustomAttribute(); - if (optionAttribute.Autocomplete && autocompleteAttribute == null) - throw new ArgumentException("Autocomplete options must have the Autocomplete attribute!"); - if (!optionAttribute.Autocomplete && autocompleteAttribute != null) - throw new ArgumentException("Setting an autocomplete provider requires the option to have autocomplete set to true!"); - - //Sets the type - var type = parameter.ParameterType; - var parameterType = GetParameterType(type); - - //Handles choices - //From attributes - var choices = GetChoiceAttributesFromParameter(parameter.GetCustomAttributes()); - //From enums - if (parameter.ParameterType.IsEnum) - { - choices = GetChoiceAttributesFromEnumParameter(parameter.ParameterType); - } - //From choice provider - var choiceProviders = parameter.GetCustomAttributes(); - if (choiceProviders.Any()) + attributes.AddRange(method.DeclaringType.DeclaringType.GetCustomAttributes()); + if (method.DeclaringType.DeclaringType.DeclaringType != null) { - choices = await GetChoiceAttributesFromProvider(choiceProviders, guildId); + attributes.AddRange(method.DeclaringType.DeclaringType.DeclaringType.GetCustomAttributes()); } + } - var channelTypes = parameter.GetCustomAttribute()?.ChannelTypes ?? null; - - options.Add(new DiscordApplicationCommandOption(optionAttribute.Name, optionAttribute.Description, parameterType, !parameter.IsOptional, choices, null, channelTypes, optionAttribute.Autocomplete, minimumValue, maximumValue)); + var dict = new Dictionary(); + foreach (var att in attributes) + { + //Runs the check and adds the result to a list + var result = await att.ExecuteChecksAsync(cMctx); + dict.Add(att, result); } - return options; + //Checks if any failed, and throws an exception + if (dict.Any(x => x.Value == false)) + throw new ContextMenuExecutionChecksFailedException { FailedChecks = dict.Where(x => x.Value == false).Select(x => x.Key).ToList() }; } + } - /// - /// Refreshes your commands, used for refreshing choice providers or applying commands registered after the ready event on the discord client. - /// Should only be run on the slash command extension linked to shard 0 if sharding. - /// Not recommended and should be avoided since it can make slash commands be unresponsive for a while. - /// - public async Task RefreshCommandsAsync() + /// + /// Gets the choice attributes from choice provider. + /// + /// The custom attributes. + /// The optional guild id + private static async Task> GetChoiceAttributesFromProvider(IEnumerable customAttributes, ulong? guildId = null) + { + var choices = new List(); + foreach (var choiceProviderAttribute in customAttributes) { - s_commandMethods.Clear(); - s_groupCommands.Clear(); - s_subGroupCommands.Clear(); - s_registeredCommands.Clear(); - s_contextMenuCommands.Clear(); - GlobalDiscordCommands.Clear(); - GuildDiscordCommands.Clear(); - GuildCommandsInternal.Clear(); - GlobalCommandsInternal.Clear(); - GlobalDiscordCommands = null; - GuildDiscordCommands = null; - s_errored = false; - - if (Configuration != null && Configuration.EnableDefaultHelp) + var method = choiceProviderAttribute.ProviderType.GetMethod(nameof(IChoiceProvider.Provider)); + + if (method == null) + throw new ArgumentException("ChoiceProviders must inherit from IChoiceProvider."); + else { - this._updateList.RemoveAll(x => x.Value.Type == typeof(DefaultHelpModule)); - } + var instance = Activator.CreateInstance(choiceProviderAttribute.ProviderType); - await this.UpdateAsync(); - } + // Abstract class offers more properties that can be set + if (choiceProviderAttribute.ProviderType.IsSubclassOf(typeof(ChoiceProvider))) + { + choiceProviderAttribute.ProviderType.GetProperty(nameof(ChoiceProvider.GuildId)) + ?.SetValue(instance, guildId); - /// - /// Fires when the execution of a slash command fails. - /// - public event AsyncEventHandler SlashCommandErrored - { - add => this._slashError.Register(value); - remove => this._slashError.Unregister(value); - } - private AsyncEvent _slashError; + choiceProviderAttribute.ProviderType.GetProperty(nameof(ChoiceProvider.Services)) + ?.SetValue(instance, Configuration.ServiceProvider); + } - /// - /// Fires when the execution of a slash command is successful. - /// - public event AsyncEventHandler SlashCommandExecuted - { - add => this._slashExecuted.Register(value); - remove => this._slashExecuted.Unregister(value); + //Gets the choices from the method + var result = await (Task>)method.Invoke(instance, null); + + if (result.Any()) + { + choices.AddRange(result); + } + } } - private AsyncEvent _slashExecuted; - /// - /// Fires when the execution of a context menu fails. - /// - public event AsyncEventHandler ContextMenuErrored + return choices; + } + + /// + /// Gets the choice attributes from enum parameter. + /// + /// The enum parameter. + private static List GetChoiceAttributesFromEnumParameter(Type enumParam) + { + var choices = new List(); + foreach (Enum enumValue in Enum.GetValues(enumParam)) { - add => this._contextMenuErrored.Register(value); - remove => this._contextMenuErrored.Unregister(value); + choices.Add(new DiscordApplicationCommandOptionChoice(enumValue.GetName(), enumValue.ToString())); } - private AsyncEvent _contextMenuErrored; + return choices; + } + + /// + /// Gets the parameter type. + /// + /// The type. + private static ApplicationCommandOptionType GetParameterType(Type type) + { + var parameterType = type == typeof(string) + ? ApplicationCommandOptionType.String + : type == typeof(long) || type == typeof(long?) || type == typeof(int) || type == typeof(int?) + ? ApplicationCommandOptionType.Integer + : type == typeof(bool) || type == typeof(bool?) + ? ApplicationCommandOptionType.Boolean + : type == typeof(double) || type == typeof(double?) + ? ApplicationCommandOptionType.Number + : type == typeof(DiscordAttachment) + ? ApplicationCommandOptionType.Attachment + : type == typeof(DiscordChannel) + ? ApplicationCommandOptionType.Channel + : type == typeof(DiscordUser) + ? ApplicationCommandOptionType.User + : type == typeof(DiscordRole) + ? ApplicationCommandOptionType.Role + : type == typeof(SnowflakeObject) + ? ApplicationCommandOptionType.Mentionable + : type.IsEnum + ? ApplicationCommandOptionType.String + : throw new ArgumentException("Cannot convert type! Argument types must be string, int, long, bool, double, DiscordChannel, DiscordUser, DiscordRole, SnowflakeObject, DiscordAttachment or an Enum."); + return parameterType; + } + + /// + /// Gets the choice attributes from parameter. + /// + /// The choice attributes. + private static List GetChoiceAttributesFromParameter(IEnumerable choiceAttributes) => + !choiceAttributes.Any() + ? null + : choiceAttributes.Select(att => new DiscordApplicationCommandOptionChoice(att.Name, att.Value)).ToList(); - /// - /// Fire when the execution of a context menu is successful. - /// - public event AsyncEventHandler ContextMenuExecuted + /// + /// Parses the parameters. + /// + /// The parameters. + /// The optional guild id. + internal static async Task> ParseParametersAsync(IEnumerable parameters, ulong? guildId) + { + var options = new List(); + foreach (var parameter in parameters) { - add => this._contextMenuExecuted.Register(value); - remove => this._contextMenuExecuted.Unregister(value); + //Gets the attribute + var optionAttribute = parameter.GetCustomAttribute(); + if (optionAttribute == null) + throw new ArgumentException("Arguments must have the Option attribute!"); + + var minimumValue = parameter.GetCustomAttribute()?.Value ?? null; + var maximumValue = parameter.GetCustomAttribute()?.Value ?? null; + + + var autocompleteAttribute = parameter.GetCustomAttribute(); + if (optionAttribute.Autocomplete && autocompleteAttribute == null) + throw new ArgumentException("Autocomplete options must have the Autocomplete attribute!"); + if (!optionAttribute.Autocomplete && autocompleteAttribute != null) + throw new ArgumentException("Setting an autocomplete provider requires the option to have autocomplete set to true!"); + + //Sets the type + var type = parameter.ParameterType; + var parameterType = GetParameterType(type); + + //Handles choices + //From attributes + var choices = GetChoiceAttributesFromParameter(parameter.GetCustomAttributes()); + //From enums + if (parameter.ParameterType.IsEnum) + { + choices = GetChoiceAttributesFromEnumParameter(parameter.ParameterType); + } + //From choice provider + var choiceProviders = parameter.GetCustomAttributes(); + if (choiceProviders.Any()) + { + choices = await GetChoiceAttributesFromProvider(choiceProviders, guildId); + } + + var channelTypes = parameter.GetCustomAttribute()?.ChannelTypes ?? null; + + options.Add(new DiscordApplicationCommandOption(optionAttribute.Name, optionAttribute.Description, parameterType, !parameter.IsOptional, choices, null, channelTypes, optionAttribute.Autocomplete, minimumValue, maximumValue)); } - private AsyncEvent _contextMenuExecuted; + + return options; } /// - /// Holds configuration data for setting up an application command. + /// Refreshes your commands, used for refreshing choice providers or applying commands registered after the ready event on the discord client. + /// Should only be run on the slash command extension linked to shard 0 if sharding. + /// Not recommended and should be avoided since it can make slash commands be unresponsive for a while. /// - internal class ApplicationCommandsModuleConfiguration + public async Task RefreshCommandsAsync() { - /// - /// The type of the command module. - /// - public Type Type { get; } - - /// - /// The translation setup. - /// - public Action Translations { get; } - - /// - /// Creates a new command configuration. - /// - /// The type of the command module. - /// The translation setup callback. - public ApplicationCommandsModuleConfiguration(Type type, Action translations = null) + s_commandMethods.Clear(); + s_groupCommands.Clear(); + s_subGroupCommands.Clear(); + s_registeredCommands.Clear(); + s_contextMenuCommands.Clear(); + GlobalDiscordCommands.Clear(); + GuildDiscordCommands.Clear(); + GuildCommandsInternal.Clear(); + GlobalCommandsInternal.Clear(); + GlobalDiscordCommands = null; + GuildDiscordCommands = null; + s_errored = false; + + if (Configuration != null && Configuration.EnableDefaultHelp) { - this.Type = type; - this.Translations = translations; + this._updateList.RemoveAll(x => x.Value.Type == typeof(DefaultHelpModule)); } + + await this.UpdateAsync(); } /// - /// Links a command to its original command module. + /// Fires when the execution of a slash command fails. /// - internal class ApplicationCommandSourceLink + public event AsyncEventHandler SlashCommandErrored { - /// - /// The command. - /// - public DiscordApplicationCommand ApplicationCommand { get; set; } - - /// - /// The base/root module the command is contained in. - /// - public Type RootCommandContainerType { get; set; } - - /// - /// The direct group the command is contained in. - /// - public Type CommandContainerType { get; set; } + add => this._slashError.Register(value); + remove => this._slashError.Unregister(value); } + private AsyncEvent _slashError; /// - /// The command method. + /// Fires when the execution of a slash command is successful. /// - internal class CommandMethod + public event AsyncEventHandler SlashCommandExecuted { - /// - /// Gets or sets the command id. - /// - public ulong CommandId { get; set; } - - /// - /// Gets or sets the name. - /// - public string Name { get; set; } - - /// - /// Gets or sets the method. - /// - public MethodInfo Method { get; set; } + add => this._slashExecuted.Register(value); + remove => this._slashExecuted.Unregister(value); } + private AsyncEvent _slashExecuted; /// - /// The group command. + /// Fires when the execution of a context menu fails. /// - internal class GroupCommand + public event AsyncEventHandler ContextMenuErrored { - /// - /// Gets or sets the command id. - /// - public ulong CommandId { get; set; } - - /// - /// Gets or sets the name. - /// - public string Name { get; set; } - - /// - /// Gets or sets the methods. - /// - public List> Methods { get; set; } = null; + add => this._contextMenuErrored.Register(value); + remove => this._contextMenuErrored.Unregister(value); } + private AsyncEvent _contextMenuErrored; /// - /// The sub group command. + /// Fire when the execution of a context menu is successful. /// - internal class SubGroupCommand + public event AsyncEventHandler ContextMenuExecuted { - /// - /// Gets or sets the command id. - /// - public ulong CommandId { get; set; } - - /// - /// Gets or sets the name. - /// - public string Name { get; set; } - - /// - /// Gets or sets the sub commands. - /// - public List SubCommands { get; set; } = new(); + add => this._contextMenuExecuted.Register(value); + remove => this._contextMenuExecuted.Unregister(value); } + private AsyncEvent _contextMenuExecuted; +} + +/// +/// Holds configuration data for setting up an application command. +/// +internal class ApplicationCommandsModuleConfiguration +{ + /// + /// The type of the command module. + /// + public Type Type { get; } + + /// + /// The translation setup. + /// + public Action Translations { get; } /// - /// The context menu command. + /// Creates a new command configuration. /// - internal class ContextMenuCommand + /// The type of the command module. + /// The translation setup callback. + public ApplicationCommandsModuleConfiguration(Type type, Action translations = null) { - /// - /// Gets or sets the command id. - /// - public ulong CommandId { get; set; } - - /// - /// Gets or sets the name. - /// - public string Name { get; set; } - - /// - /// Gets or sets the method. - /// - public MethodInfo Method { get; set; } + this.Type = type; + this.Translations = translations; } +} + +/// +/// Links a command to its original command module. +/// +internal class ApplicationCommandSourceLink +{ + /// + /// The command. + /// + public DiscordApplicationCommand ApplicationCommand { get; set; } + + /// + /// The base/root module the command is contained in. + /// + public Type RootCommandContainerType { get; set; } + + /// + /// The direct group the command is contained in. + /// + public Type CommandContainerType { get; set; } +} + +/// +/// The command method. +/// +internal class CommandMethod +{ + /// + /// Gets or sets the command id. + /// + public ulong CommandId { get; set; } + + /// + /// Gets or sets the name. + /// + public string Name { get; set; } + + /// + /// Gets or sets the method. + /// + public MethodInfo Method { get; set; } +} + +/// +/// The group command. +/// +internal class GroupCommand +{ + /// + /// Gets or sets the command id. + /// + public ulong CommandId { get; set; } + + /// + /// Gets or sets the name. + /// + public string Name { get; set; } + + /// + /// Gets or sets the methods. + /// + public List> Methods { get; set; } = null; +} + +/// +/// The sub group command. +/// +internal class SubGroupCommand +{ + /// + /// Gets or sets the command id. + /// + public ulong CommandId { get; set; } + + /// + /// Gets or sets the name. + /// + public string Name { get; set; } + + /// + /// Gets or sets the sub commands. + /// + public List SubCommands { get; set; } = new(); +} + +/// +/// The context menu command. +/// +internal class ContextMenuCommand +{ + /// + /// Gets or sets the command id. + /// + public ulong CommandId { get; set; } + + /// + /// Gets or sets the name. + /// + public string Name { get; set; } - #region Default Help /// - /// Represents the default help module. + /// Gets or sets the method. /// - internal class DefaultHelpModule : ApplicationCommandsModule + public MethodInfo Method { get; set; } +} + +#region Default Help +/// +/// Represents the default help module. +/// +internal class DefaultHelpModule : ApplicationCommandsModule +{ + public class DefaultHelpAutoCompleteProvider : IAutocompleteProvider { - public class DefaultHelpAutoCompleteProvider : IAutocompleteProvider + public async Task> Provider(AutocompleteContext context) { - public async Task> Provider(AutocompleteContext context) - { - var options = new List(); + var options = new List(); - IEnumerable slashCommands = null; - var globalCommandsTask = context.Client.GetGlobalApplicationCommandsAsync(); - if (context.Guild != null) - { - var guildCommandsTask = context.Client.GetGuildApplicationCommandsAsync(context.Guild.Id); - await Task.WhenAll(globalCommandsTask, guildCommandsTask); - slashCommands = globalCommandsTask.Result.Concat(guildCommandsTask.Result) - .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase)) - .GroupBy(ac => ac.Name).Select(x => x.First()) - .Where(ac => ac.Name.StartsWith(context.Options[0].Value.ToString(), StringComparison.OrdinalIgnoreCase)) - .ToList(); - } - else - { - await Task.WhenAll(globalCommandsTask); - slashCommands = globalCommandsTask.Result - .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase)) - .GroupBy(ac => ac.Name).Select(x => x.First()) - .Where(ac => ac.Name.StartsWith(context.Options[0].Value.ToString(), StringComparison.OrdinalIgnoreCase)) - .ToList(); - } + IEnumerable slashCommands = null; + var globalCommandsTask = context.Client.GetGlobalApplicationCommandsAsync(); + if (context.Guild != null) + { + var guildCommandsTask = context.Client.GetGuildApplicationCommandsAsync(context.Guild.Id); + await Task.WhenAll(globalCommandsTask, guildCommandsTask); + slashCommands = globalCommandsTask.Result.Concat(guildCommandsTask.Result) + .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase)) + .GroupBy(ac => ac.Name).Select(x => x.First()) + .Where(ac => ac.Name.StartsWith(context.Options[0].Value.ToString(), StringComparison.OrdinalIgnoreCase)) + .ToList(); + } + else + { + await Task.WhenAll(globalCommandsTask); + slashCommands = globalCommandsTask.Result + .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase)) + .GroupBy(ac => ac.Name).Select(x => x.First()) + .Where(ac => ac.Name.StartsWith(context.Options[0].Value.ToString(), StringComparison.OrdinalIgnoreCase)) + .ToList(); + } - foreach (var sc in slashCommands.Take(25)) - { - options.Add(new DiscordApplicationCommandAutocompleteChoice(sc.Name, sc.Name.Trim())); - } - return options.AsEnumerable(); + foreach (var sc in slashCommands.Take(25)) + { + options.Add(new DiscordApplicationCommandAutocompleteChoice(sc.Name, sc.Name.Trim())); } + return options.AsEnumerable(); } + } - public class DefaultHelpAutoCompleteLevelOneProvider : IAutocompleteProvider + public class DefaultHelpAutoCompleteLevelOneProvider : IAutocompleteProvider + { + public async Task> Provider(AutocompleteContext context) { - public async Task> Provider(AutocompleteContext context) + var options = new List(); + IEnumerable slashCommands = null; + var globalCommandsTask = context.Client.GetGlobalApplicationCommandsAsync(); + if (context.Guild != null) { - var options = new List(); - IEnumerable slashCommands = null; - var globalCommandsTask = context.Client.GetGlobalApplicationCommandsAsync(); - if (context.Guild != null) - { - var guildCommandsTask = context.Client.GetGuildApplicationCommandsAsync(context.Guild.Id); - await Task.WhenAll(globalCommandsTask, guildCommandsTask); - slashCommands = globalCommandsTask.Result.Concat(guildCommandsTask.Result) - .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase)) - .GroupBy(ac => ac.Name).Select(x => x.First()); - } - else - { - await Task.WhenAll(globalCommandsTask); - slashCommands = globalCommandsTask.Result - .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase)) - .GroupBy(ac => ac.Name).Select(x => x.First()); - } + var guildCommandsTask = context.Client.GetGuildApplicationCommandsAsync(context.Guild.Id); + await Task.WhenAll(globalCommandsTask, guildCommandsTask); + slashCommands = globalCommandsTask.Result.Concat(guildCommandsTask.Result) + .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase)) + .GroupBy(ac => ac.Name).Select(x => x.First()); + } + else + { + await Task.WhenAll(globalCommandsTask); + slashCommands = globalCommandsTask.Result + .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase)) + .GroupBy(ac => ac.Name).Select(x => x.First()); + } - var command = slashCommands.FirstOrDefault(ac => - ac.Name.Equals(context.Options[0].Value.ToString().Trim(),StringComparison.OrdinalIgnoreCase)); - if (command is null || command.Options is null) - { - options.Add(new DiscordApplicationCommandAutocompleteChoice("no_options_for_this_command", "no_options_for_this_command")); - } - else + var command = slashCommands.FirstOrDefault(ac => + ac.Name.Equals(context.Options[0].Value.ToString().Trim(),StringComparison.OrdinalIgnoreCase)); + if (command is null || command.Options is null) + { + options.Add(new DiscordApplicationCommandAutocompleteChoice("no_options_for_this_command", "no_options_for_this_command")); + } + else + { + var opt = command.Options.Where(c => c.Type is ApplicationCommandOptionType.SubCommandGroup or ApplicationCommandOptionType.SubCommand + && c.Name.StartsWith(context.Options[1].Value.ToString(), StringComparison.InvariantCultureIgnoreCase)).ToList(); + foreach (var option in opt.Take(25)) { - var opt = command.Options.Where(c => c.Type is ApplicationCommandOptionType.SubCommandGroup or ApplicationCommandOptionType.SubCommand - && c.Name.StartsWith(context.Options[1].Value.ToString(), StringComparison.InvariantCultureIgnoreCase)).ToList(); - foreach (var option in opt.Take(25)) - { - options.Add(new DiscordApplicationCommandAutocompleteChoice(option.Name, option.Name.Trim())); - } + options.Add(new DiscordApplicationCommandAutocompleteChoice(option.Name, option.Name.Trim())); } - return options.AsEnumerable(); } + return options.AsEnumerable(); } + } - public class DefaultHelpAutoCompleteLevelTwoProvider : IAutocompleteProvider + public class DefaultHelpAutoCompleteLevelTwoProvider : IAutocompleteProvider + { + public async Task> Provider(AutocompleteContext context) { - public async Task> Provider(AutocompleteContext context) + var options = new List(); + IEnumerable slashCommands = null; + var globalCommandsTask = context.Client.GetGlobalApplicationCommandsAsync(); + if (context.Guild != null) { - var options = new List(); - IEnumerable slashCommands = null; - var globalCommandsTask = context.Client.GetGlobalApplicationCommandsAsync(); - if (context.Guild != null) - { - var guildCommandsTask = context.Client.GetGuildApplicationCommandsAsync(context.Guild.Id); - await Task.WhenAll(globalCommandsTask, guildCommandsTask); - slashCommands = globalCommandsTask.Result.Concat(guildCommandsTask.Result) - .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase)) - .GroupBy(ac => ac.Name).Select(x => x.First()); - } - else - { - await Task.WhenAll(globalCommandsTask); - slashCommands = globalCommandsTask.Result - .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase)) - .GroupBy(ac => ac.Name).Select(x => x.First()); - } + var guildCommandsTask = context.Client.GetGuildApplicationCommandsAsync(context.Guild.Id); + await Task.WhenAll(globalCommandsTask, guildCommandsTask); + slashCommands = globalCommandsTask.Result.Concat(guildCommandsTask.Result) + .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase)) + .GroupBy(ac => ac.Name).Select(x => x.First()); + } + else + { + await Task.WhenAll(globalCommandsTask); + slashCommands = globalCommandsTask.Result + .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase)) + .GroupBy(ac => ac.Name).Select(x => x.First()); + } - var command = slashCommands.FirstOrDefault(ac => - ac.Name.Equals(context.Options[0].Value.ToString().Trim(), StringComparison.OrdinalIgnoreCase)); - if (command.Options is null) - { - options.Add(new DiscordApplicationCommandAutocompleteChoice("no_options_for_this_command", "no_options_for_this_command")); - return options.AsEnumerable(); - } - var foundCommand = command.Options.FirstOrDefault(op => op.Name.Equals(context.Options[1].Value.ToString().Trim(), StringComparison.OrdinalIgnoreCase)); - if (foundCommand is null || foundCommand.Options is null) - { - options.Add(new DiscordApplicationCommandAutocompleteChoice("no_options_for_this_command", "no_options_for_this_command")); - } - else + var command = slashCommands.FirstOrDefault(ac => + ac.Name.Equals(context.Options[0].Value.ToString().Trim(), StringComparison.OrdinalIgnoreCase)); + if (command.Options is null) + { + options.Add(new DiscordApplicationCommandAutocompleteChoice("no_options_for_this_command", "no_options_for_this_command")); + return options.AsEnumerable(); + } + var foundCommand = command.Options.FirstOrDefault(op => op.Name.Equals(context.Options[1].Value.ToString().Trim(), StringComparison.OrdinalIgnoreCase)); + if (foundCommand is null || foundCommand.Options is null) + { + options.Add(new DiscordApplicationCommandAutocompleteChoice("no_options_for_this_command", "no_options_for_this_command")); + } + else + { + var opt = foundCommand.Options.Where(x => x.Type == ApplicationCommandOptionType.SubCommand && + x.Name.StartsWith(context.Options[2].Value.ToString(), StringComparison.OrdinalIgnoreCase)).ToList(); + foreach (var option in opt.Take(25)) { - var opt = foundCommand.Options.Where(x => x.Type == ApplicationCommandOptionType.SubCommand && - x.Name.StartsWith(context.Options[2].Value.ToString(), StringComparison.OrdinalIgnoreCase)).ToList(); - foreach (var option in opt.Take(25)) - { - options.Add(new DiscordApplicationCommandAutocompleteChoice(option.Name, option.Name.Trim())); - } + options.Add(new DiscordApplicationCommandAutocompleteChoice(option.Name, option.Name.Trim())); } - return options.AsEnumerable(); } + return options.AsEnumerable(); } + } - [SlashCommand("help", "Displays command help")] - internal async Task DefaultHelpAsync(InteractionContext ctx, - [Autocomplete(typeof(DefaultHelpAutoCompleteProvider))] - [Option("option_one", "top level command to provide help for", true)] string commandName, - [Autocomplete(typeof(DefaultHelpAutoCompleteLevelOneProvider))] - [Option("option_two", "subgroup or command to provide help for", true)] string commandOneName = null, - [Autocomplete(typeof(DefaultHelpAutoCompleteLevelTwoProvider))] - [Option("option_three", "command to provide help for", true)] string commandTwoName = null) - { - var globalCommandsTask = ctx.Client.GetGlobalApplicationCommandsAsync(); - var guildCommandsTask= ctx.Client.GetGuildApplicationCommandsAsync(ctx.Guild.Id); + [SlashCommand("help", "Displays command help")] + internal async Task DefaultHelpAsync(InteractionContext ctx, + [Autocomplete(typeof(DefaultHelpAutoCompleteProvider))] + [Option("option_one", "top level command to provide help for", true)] string commandName, + [Autocomplete(typeof(DefaultHelpAutoCompleteLevelOneProvider))] + [Option("option_two", "subgroup or command to provide help for", true)] string commandOneName = null, + [Autocomplete(typeof(DefaultHelpAutoCompleteLevelTwoProvider))] + [Option("option_three", "command to provide help for", true)] string commandTwoName = null) + { + var globalCommandsTask = ctx.Client.GetGlobalApplicationCommandsAsync(); + var guildCommandsTask= ctx.Client.GetGuildApplicationCommandsAsync(ctx.Guild.Id); - await Task.WhenAll(globalCommandsTask, guildCommandsTask); + await Task.WhenAll(globalCommandsTask, guildCommandsTask); - var applicationCommands = globalCommandsTask.Result.Concat(guildCommandsTask.Result) - .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase)) - .GroupBy(ac => ac.Name).Select(x => x.First()) - .ToList(); - if (applicationCommands.Count < 1) + var applicationCommands = globalCommandsTask.Result.Concat(guildCommandsTask.Result) + .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase)) + .GroupBy(ac => ac.Name).Select(x => x.First()) + .ToList(); + if (applicationCommands.Count < 1) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder() + .WithContent($"There are no slash commands for guild {ctx.Guild.Name}").AsEphemeral(true)); + return; + } + if (commandTwoName is not null && !commandTwoName.Equals("no_options_for_this_command")) + { + var commandsWithSubCommands = applicationCommands.FindAll(ac => ac.Options is not null && ac.Options.Any(op => op.Type == ApplicationCommandOptionType.SubCommandGroup)); + var cmdParent = commandsWithSubCommands.FirstOrDefault(cm => cm.Options.Any(op => op.Name.Equals(commandOneName))).Options + .FirstOrDefault(opt => opt.Name.Equals(commandOneName,StringComparison.OrdinalIgnoreCase)); + var cmd = cmdParent.Options.FirstOrDefault(op => op.Name.Equals(commandTwoName,StringComparison.OrdinalIgnoreCase)); + var discordEmbed = new DiscordEmbedBuilder { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder() - .WithContent($"There are no slash commands for guild {ctx.Guild.Name}").AsEphemeral(true)); - return; - } - if (commandTwoName is not null && !commandTwoName.Equals("no_options_for_this_command")) + Title = "Help", + Description = $"{Formatter.InlineCode(cmd.Name)}: {cmd.Description ?? "No description provided."}" + }; + if (cmd.Options is not null) { - var commandsWithSubCommands = applicationCommands.FindAll(ac => ac.Options is not null && ac.Options.Any(op => op.Type == ApplicationCommandOptionType.SubCommandGroup)); - var cmdParent = commandsWithSubCommands.FirstOrDefault(cm => cm.Options.Any(op => op.Name.Equals(commandOneName))).Options - .FirstOrDefault(opt => opt.Name.Equals(commandOneName,StringComparison.OrdinalIgnoreCase)); - var cmd = cmdParent.Options.FirstOrDefault(op => op.Name.Equals(commandTwoName,StringComparison.OrdinalIgnoreCase)); - var discordEmbed = new DiscordEmbedBuilder - { - Title = "Help", - Description = $"{Formatter.InlineCode(cmd.Name)}: {cmd.Description ?? "No description provided."}" - }; - if (cmd.Options is not null) - { - var commandOptions = cmd.Options.ToList(); - var sb = new StringBuilder(); + var commandOptions = cmd.Options.ToList(); + var sb = new StringBuilder(); - foreach (var option in commandOptions) - sb.Append('`').Append(option.Name).Append(" (").Append(")`: ").Append(option.Description ?? "No description provided.").Append('\n'); + foreach (var option in commandOptions) + sb.Append('`').Append(option.Name).Append(" (").Append(")`: ").Append(option.Description ?? "No description provided.").Append('\n'); - sb.Append('\n'); - discordEmbed.AddField(new DiscordEmbedField("Arguments", sb.ToString().Trim())); - } - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, - new DiscordInteractionResponseBuilder().AddEmbed(discordEmbed).AsEphemeral(true)); + sb.Append('\n'); + discordEmbed.AddField(new DiscordEmbedField("Arguments", sb.ToString().Trim())); } - else if (commandOneName is not null && commandTwoName is null && !commandOneName.Equals("no_options_for_this_command")) + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, + new DiscordInteractionResponseBuilder().AddEmbed(discordEmbed).AsEphemeral(true)); + } + else if (commandOneName is not null && commandTwoName is null && !commandOneName.Equals("no_options_for_this_command")) + { + var commandsWithOptions = applicationCommands.FindAll(ac => ac.Options is not null && ac.Options.All(op => op.Type == ApplicationCommandOptionType.SubCommand)); + var subCommandParent = commandsWithOptions.FirstOrDefault(cm => cm.Name.Equals(commandName,StringComparison.OrdinalIgnoreCase)); + var subCommand = subCommandParent.Options.FirstOrDefault(op => op.Name.Equals(commandOneName,StringComparison.OrdinalIgnoreCase)); + var discordEmbed = new DiscordEmbedBuilder { - var commandsWithOptions = applicationCommands.FindAll(ac => ac.Options is not null && ac.Options.All(op => op.Type == ApplicationCommandOptionType.SubCommand)); - var subCommandParent = commandsWithOptions.FirstOrDefault(cm => cm.Name.Equals(commandName,StringComparison.OrdinalIgnoreCase)); - var subCommand = subCommandParent.Options.FirstOrDefault(op => op.Name.Equals(commandOneName,StringComparison.OrdinalIgnoreCase)); - var discordEmbed = new DiscordEmbedBuilder - { - Title = "Help", - Description = $"{Formatter.InlineCode(subCommand.Name)}: {subCommand.Description ?? "No description provided."}" - }; - if (subCommand.Options is not null) - { - var commandOptions = subCommand.Options.ToList(); - var sb = new StringBuilder(); + Title = "Help", + Description = $"{Formatter.InlineCode(subCommand.Name)}: {subCommand.Description ?? "No description provided."}" + }; + if (subCommand.Options is not null) + { + var commandOptions = subCommand.Options.ToList(); + var sb = new StringBuilder(); - foreach (var option in commandOptions) - sb.Append('`').Append(option.Name).Append(" (").Append(")`: ").Append(option.Description ?? "No description provided.").Append('\n'); + foreach (var option in commandOptions) + sb.Append('`').Append(option.Name).Append(" (").Append(")`: ").Append(option.Description ?? "No description provided.").Append('\n'); - sb.Append('\n'); - discordEmbed.AddField(new DiscordEmbedField("Arguments", sb.ToString().Trim())); - } - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, - new DiscordInteractionResponseBuilder().AddEmbed(discordEmbed).AsEphemeral(true)); + sb.Append('\n'); + discordEmbed.AddField(new DiscordEmbedField("Arguments", sb.ToString().Trim())); } - else + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, + new DiscordInteractionResponseBuilder().AddEmbed(discordEmbed).AsEphemeral(true)); + } + else + { + var command = applicationCommands.FirstOrDefault(cm => cm.Name.Equals(commandName, StringComparison.OrdinalIgnoreCase)); + if (command is null) { - var command = applicationCommands.FirstOrDefault(cm => cm.Name.Equals(commandName, StringComparison.OrdinalIgnoreCase)); - if (command is null) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder() - .WithContent($"No command called {commandName} in guild {ctx.Guild.Name}").AsEphemeral(true)); - return; - } - var discordEmbed = new DiscordEmbedBuilder - { - Title = "Help", - Description = $"{Formatter.InlineCode(command.Name)}: {command.Description ?? "No description provided."}" - }.AddField(new DiscordEmbedField("Command is NSFW", command.IsNsfw.ToString())); - if (command.Options is not null) - { - var commandOptions = command.Options.ToList(); - var sb = new StringBuilder(); + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder() + .WithContent($"No command called {commandName} in guild {ctx.Guild.Name}").AsEphemeral(true)); + return; + } + var discordEmbed = new DiscordEmbedBuilder + { + Title = "Help", + Description = $"{Formatter.InlineCode(command.Name)}: {command.Description ?? "No description provided."}" + }.AddField(new DiscordEmbedField("Command is NSFW", command.IsNsfw.ToString())); + if (command.Options is not null) + { + var commandOptions = command.Options.ToList(); + var sb = new StringBuilder(); - foreach (var option in commandOptions) - sb.Append('`').Append(option.Name).Append(" (").Append(")`: ").Append(option.Description ?? "No description provided.").Append('\n'); + foreach (var option in commandOptions) + sb.Append('`').Append(option.Name).Append(" (").Append(")`: ").Append(option.Description ?? "No description provided.").Append('\n'); - sb.Append('\n'); - discordEmbed.AddField(new DiscordEmbedField("Arguments", sb.ToString().Trim())); - } - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, - new DiscordInteractionResponseBuilder().AddEmbed(discordEmbed).AsEphemeral(true)); + sb.Append('\n'); + discordEmbed.AddField(new DiscordEmbedField("Arguments", sb.ToString().Trim())); } + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, + new DiscordInteractionResponseBuilder().AddEmbed(discordEmbed).AsEphemeral(true)); } } - #endregion } +#endregion diff --git a/DisCatSharp.ApplicationCommands/ApplicationCommandsModule.cs b/DisCatSharp.ApplicationCommands/ApplicationCommandsModule.cs index dcf9377da..9789c3887 100644 --- a/DisCatSharp.ApplicationCommands/ApplicationCommandsModule.cs +++ b/DisCatSharp.ApplicationCommands/ApplicationCommandsModule.cs @@ -1,64 +1,63 @@ // 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.Threading.Tasks; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// Represents a base class for application command modules +/// +public abstract class ApplicationCommandsModule { /// - /// Represents a base class for application command modules + /// Called before the execution of a slash command in the module. /// - public abstract class ApplicationCommandsModule - { - /// - /// Called before the execution of a slash command in the module. - /// - /// The context. - /// Whether or not to execute the slash command. - public virtual Task BeforeSlashExecutionAsync(InteractionContext ctx) - => Task.FromResult(true); + /// The context. + /// Whether or not to execute the slash command. + public virtual Task BeforeSlashExecutionAsync(InteractionContext ctx) + => Task.FromResult(true); - /// - /// Called after the execution of a slash command in the module. - /// - /// The context. - /// - public virtual Task AfterSlashExecutionAsync(InteractionContext ctx) - => Task.CompletedTask; + /// + /// Called after the execution of a slash command in the module. + /// + /// The context. + /// + public virtual Task AfterSlashExecutionAsync(InteractionContext ctx) + => Task.CompletedTask; - /// - /// Called before the execution of a context menu in the module. - /// - /// The context. - /// Whether or not to execute the slash command. - public virtual Task BeforeContextMenuExecutionAsync(ContextMenuContext ctx) - => Task.FromResult(true); + /// + /// Called before the execution of a context menu in the module. + /// + /// The context. + /// Whether or not to execute the slash command. + public virtual Task BeforeContextMenuExecutionAsync(ContextMenuContext ctx) + => Task.FromResult(true); - /// - /// Called after the execution of a context menu in the module. - /// - /// The context. - /// - public virtual Task AfterContextMenuExecutionAsync(ContextMenuContext ctx) - => Task.CompletedTask; - } + /// + /// Called after the execution of a context menu in the module. + /// + /// The context. + /// + public virtual Task AfterContextMenuExecutionAsync(ContextMenuContext ctx) + => Task.CompletedTask; } diff --git a/DisCatSharp.ApplicationCommands/Attributes/ApplicationCommandModuleLifespanAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/ApplicationCommandModuleLifespanAttribute.cs index 9da35c626..2b5588c78 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/ApplicationCommandModuleLifespanAttribute.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/ApplicationCommandModuleLifespanAttribute.cs @@ -1,69 +1,67 @@ // 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; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// Defines this application command module's lifespan. Module lifespans are transient by default. +/// +[AttributeUsage(AttributeTargets.Class)] +public class ApplicationCommandModuleLifespanAttribute : Attribute { + /// + /// Gets the lifespan. + /// + public ApplicationCommandModuleLifespan Lifespan { get; } /// - /// Defines this application command module's lifespan. Module lifespans are transient by default. + /// Defines this application command module's lifespan. /// - [AttributeUsage(AttributeTargets.Class)] - public class ApplicationCommandModuleLifespanAttribute : Attribute + /// The lifespan of the module. Module lifespans are transient by default. + public ApplicationCommandModuleLifespanAttribute(ApplicationCommandModuleLifespan lifespan) { - /// - /// Gets the lifespan. - /// - public ApplicationCommandModuleLifespan Lifespan { get; } - - /// - /// Defines this application command module's lifespan. - /// - /// The lifespan of the module. Module lifespans are transient by default. - public ApplicationCommandModuleLifespanAttribute(ApplicationCommandModuleLifespan lifespan) - { - this.Lifespan = lifespan; - } + this.Lifespan = lifespan; } +} +/// +/// Represents a application command module lifespan. +/// +public enum ApplicationCommandModuleLifespan +{ /// - /// Represents a application command module lifespan. + /// Whether this module should be initiated every time a command is run, with dependencies injected from a scope. /// - public enum ApplicationCommandModuleLifespan - { - /// - /// Whether this module should be initiated every time a command is run, with dependencies injected from a scope. - /// - Scoped, + Scoped, - /// - /// Whether this module should be initiated every time a command is run. - /// - Transient, + /// + /// Whether this module should be initiated every time a command is run. + /// + Transient, - /// - /// Whether this module should be initiated at startup. - /// - Singleton - } + /// + /// Whether this module should be initiated at startup. + /// + Singleton } diff --git a/DisCatSharp.ApplicationCommands/Attributes/ContextMenu/ContextMenuAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/ContextMenu/ContextMenuAttribute.cs index e0a62f042..551a35224 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/ContextMenu/ContextMenuAttribute.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/ContextMenu/ContextMenuAttribute.cs @@ -1,99 +1,97 @@ // 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 DisCatSharp.Enums; -namespace DisCatSharp.ApplicationCommands -{ +namespace DisCatSharp.ApplicationCommands; +/// +/// Marks this method as a context menu. +/// +[AttributeUsage(AttributeTargets.Method)] +public sealed class ContextMenuAttribute : Attribute +{ /// - /// Marks this method as a context menu. + /// Gets the name of this context menu. /// - [AttributeUsage(AttributeTargets.Method)] - public sealed class ContextMenuAttribute : Attribute - { - /// - /// Gets the name of this context menu. - /// - public string Name { get; internal set; } + public string Name { get; internal set; } - /// - /// Gets the type of this context menu. - /// - public ApplicationCommandType Type { get; internal set; } + /// + /// Gets the type of this context menu. + /// + public ApplicationCommandType Type { get; internal set; } - /// - /// Gets the commands needed permissions. - /// - public Permissions? DefaultMemberPermissions { get; internal set; } + /// + /// Gets the commands needed permissions. + /// + public Permissions? DefaultMemberPermissions { get; internal set; } - /// - /// Gets whether the command can be used in direct messages. - /// - internal bool? DmPermission { get; set; } + /// + /// Gets whether the command can be used in direct messages. + /// + internal bool? DmPermission { get; set; } - /// - /// Gets whether this command is marked as NSFW - /// - public bool IsNsfw { get; set; } + /// + /// Gets whether this command is marked as NSFW + /// + public bool IsNsfw { get; set; } - /// - /// Marks this method as a context menu. - /// - /// The type of the context menu. - /// The name of the context menu. - /// Whether this context menu command is marked as NSFW. - public ContextMenuAttribute(ApplicationCommandType type, string name, bool isNsfw = false) - { - if (type == ApplicationCommandType.ChatInput) - throw new ArgumentException("Context menus cannot be of type ChatInput (Slash)."); + /// + /// Marks this method as a context menu. + /// + /// The type of the context menu. + /// The name of the context menu. + /// Whether this context menu command is marked as NSFW. + public ContextMenuAttribute(ApplicationCommandType type, string name, bool isNsfw = false) + { + if (type == ApplicationCommandType.ChatInput) + throw new ArgumentException("Context menus cannot be of type ChatInput (Slash)."); - this.Type = type; - this.Name = name; - this.DefaultMemberPermissions = null; - this.DmPermission = null; - this.IsNsfw = isNsfw; - } + this.Type = type; + this.Name = name; + this.DefaultMemberPermissions = null; + this.DmPermission = null; + this.IsNsfw = isNsfw; + } - /// - /// Marks this method as a context menu. - /// - /// The type of the context menu. - /// The name of the context menu. - /// The default member permissions. - /// Whether this context menu command is marked as NSFW. - public ContextMenuAttribute(ApplicationCommandType type, string name, long defaultMemberPermissions, bool isNsfw = false) - { - if (type == ApplicationCommandType.ChatInput) - throw new ArgumentException("Context menus cannot be of type ChatInput (Slash)."); + /// + /// Marks this method as a context menu. + /// + /// The type of the context menu. + /// The name of the context menu. + /// The default member permissions. + /// Whether this context menu command is marked as NSFW. + public ContextMenuAttribute(ApplicationCommandType type, string name, long defaultMemberPermissions, bool isNsfw = false) + { + if (type == ApplicationCommandType.ChatInput) + throw new ArgumentException("Context menus cannot be of type ChatInput (Slash)."); - this.Type = type; - this.Name = name; - this.DefaultMemberPermissions = (Permissions)defaultMemberPermissions; - this.DmPermission = null; - this.IsNsfw = isNsfw; - } + this.Type = type; + this.Name = name; + this.DefaultMemberPermissions = (Permissions)defaultMemberPermissions; + this.DmPermission = null; + this.IsNsfw = isNsfw; } } diff --git a/DisCatSharp.ApplicationCommands/Attributes/ContextMenu/ContextMenuCheckBaseAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/ContextMenu/ContextMenuCheckBaseAttribute.cs index 057945423..35e3d26f8 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/ContextMenu/ContextMenuCheckBaseAttribute.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/ContextMenu/ContextMenuCheckBaseAttribute.cs @@ -1,40 +1,39 @@ // 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.Threading.Tasks; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// The base class for a pre-execution check for a context menu. +/// +public abstract class ContextMenuCheckBaseAttribute : Attribute { /// - /// The base class for a pre-execution check for a context menu. + /// Checks whether this command can be executed within the current context. /// - public abstract class ContextMenuCheckBaseAttribute : Attribute - { - /// - /// Checks whether this command can be executed within the current context. - /// - /// The context. - /// Whether the checks passed. - public abstract Task ExecuteChecksAsync(ContextMenuContext ctx); - } + /// The context. + /// Whether the checks passed. + public abstract Task ExecuteChecksAsync(ContextMenuContext ctx); } diff --git a/DisCatSharp.ApplicationCommands/Attributes/DontInjectAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/DontInjectAttribute.cs index c3011b5df..e71d1cc1b 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/DontInjectAttribute.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/DontInjectAttribute.cs @@ -1,33 +1,32 @@ // 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; -namespace DisCatSharp.ApplicationCommands -{ - /// - /// Prevents this field or property from having its value injected by dependency injection. - /// - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] - public class DontInjectAttribute : Attribute - { } -} +namespace DisCatSharp.ApplicationCommands; + +/// +/// Prevents this field or property from having its value injected by dependency injection. +/// +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] +public class DontInjectAttribute : Attribute +{ } diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/AutocompleteAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/AutocompleteAttribute.cs index d2aaee1ce..a64df8873 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/AutocompleteAttribute.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/AutocompleteAttribute.cs @@ -1,48 +1,46 @@ // 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; -namespace DisCatSharp.ApplicationCommands.Attributes +namespace DisCatSharp.ApplicationCommands.Attributes; + +/// +/// The autocomplete attribute. +/// +[AttributeUsage(AttributeTargets.Parameter)] +public class AutocompleteAttribute : Attribute { + /// + /// The type of the provider. + /// + public Type ProviderType { get; } /// - /// The autocomplete attribute. + /// Adds an autocomplete provider to this command option. /// - [AttributeUsage(AttributeTargets.Parameter)] - public class AutocompleteAttribute : Attribute + /// The type of the provider. + public AutocompleteAttribute(Type providerType) { - /// - /// The type of the provider. - /// - public Type ProviderType { get; } - - /// - /// Adds an autocomplete provider to this command option. - /// - /// The type of the provider. - public AutocompleteAttribute(Type providerType) - { - this.ProviderType = providerType; - } + this.ProviderType = providerType; } } diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChannelTypesAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChannelTypesAttribute.cs index 09735feaf..8060f26b4 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChannelTypesAttribute.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChannelTypesAttribute.cs @@ -1,48 +1,47 @@ // 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; -namespace DisCatSharp.ApplicationCommands.Attributes +namespace DisCatSharp.ApplicationCommands.Attributes; + +/// +/// Defines allowed channel types for a channel parameter. +/// +[AttributeUsage(AttributeTargets.Parameter)] +public class ChannelTypesAttribute : Attribute { + /// + /// Allowed channel types. + /// + public IEnumerable ChannelTypes { get; } + /// /// Defines allowed channel types for a channel parameter. /// - [AttributeUsage(AttributeTargets.Parameter)] - public class ChannelTypesAttribute : Attribute + /// The channel types to allow. + public ChannelTypesAttribute(params ChannelType[] channelTypes) { - /// - /// Allowed channel types. - /// - public IEnumerable ChannelTypes { get; } - - /// - /// Defines allowed channel types for a channel parameter. - /// - /// The channel types to allow. - public ChannelTypesAttribute(params ChannelType[] channelTypes) - { - this.ChannelTypes = channelTypes; - } + this.ChannelTypes = channelTypes; } } diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceAttribute.cs index d2fe0ee3c..73bce8e6f 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceAttribute.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceAttribute.cs @@ -1,87 +1,86 @@ // 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; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// Adds a choice for this slash command option +/// +[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true)] +public class ChoiceAttribute : Attribute { /// - /// Adds a choice for this slash command option + /// Gets the name of the choice /// - [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true)] - public class ChoiceAttribute : Attribute - { - /// - /// Gets the name of the choice - /// - public string Name { get; } + public string Name { get; } - /// - /// Gets the value of the choice - /// - public object Value { get; } + /// + /// Gets the value of the choice + /// + public object Value { get; } - /// - /// Adds a choice to the slash command option - /// - /// The name of the choice. - /// The value of the choice. - public ChoiceAttribute(string name, string value) - { - this.Name = name; - this.Value = value; - } + /// + /// Adds a choice to the slash command option + /// + /// The name of the choice. + /// The value of the choice. + public ChoiceAttribute(string name, string value) + { + this.Name = name; + this.Value = value; + } - /// - /// Adds a choice to the slash command option - /// - /// The name of the choice. - /// The value of the choice. - public ChoiceAttribute(string name, long value) - { - this.Name = name; - this.Value = value; - } + /// + /// Adds a choice to the slash command option + /// + /// The name of the choice. + /// The value of the choice. + public ChoiceAttribute(string name, long value) + { + this.Name = name; + this.Value = value; + } - /// - /// Adds a choice to the slash command option - /// - /// The name of the choice. - /// The value of the choice. - public ChoiceAttribute(string name, int value) - { - this.Name = name; - this.Value = value; - } + /// + /// Adds a choice to the slash command option + /// + /// The name of the choice. + /// The value of the choice. + public ChoiceAttribute(string name, int value) + { + this.Name = name; + this.Value = value; + } - /// - /// Adds a choice to the slash command option - /// - /// The name of the choice. - /// The value of the choice. - public ChoiceAttribute(string name, double value) - { - this.Name = name; - this.Value = value; - } + /// + /// Adds a choice to the slash command option + /// + /// The name of the choice. + /// The value of the choice. + public ChoiceAttribute(string name, double value) + { + this.Name = name; + this.Value = value; } } diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceNameAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceNameAttribute.cs index 3d66c6499..0aaf8d820 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceNameAttribute.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceNameAttribute.cs @@ -1,47 +1,46 @@ // 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; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// Sets the name for this enum choice. +/// +[AttributeUsage(AttributeTargets.All)] +public class ChoiceNameAttribute : Attribute { + /// + /// The name. + /// + public string Name { get; set; } + /// /// Sets the name for this enum choice. /// - [AttributeUsage(AttributeTargets.All)] - public class ChoiceNameAttribute : Attribute + /// The name for this enum choice. + public ChoiceNameAttribute(string name) { - /// - /// The name. - /// - public string Name { get; set; } - - /// - /// Sets the name for this enum choice. - /// - /// The name for this enum choice. - public ChoiceNameAttribute(string name) - { - this.Name = name; - } + this.Name = name; } } diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceProvider.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceProvider.cs index f72a1bd51..0f845d05c 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceProvider.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceProvider.cs @@ -1,51 +1,50 @@ // 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.Threading.Tasks; using DisCatSharp.Entities; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// Implementation of with access to service collection. +/// +public abstract class ChoiceProvider : IChoiceProvider { /// - /// Implementation of with access to service collection. + /// Sets the choices for the slash command. /// - public abstract class ChoiceProvider : IChoiceProvider - { - /// - /// Sets the choices for the slash command. - /// - public abstract Task> Provider(); + public abstract Task> Provider(); - /// - /// Sets the service provider. - /// - public IServiceProvider Services { get; set; } + /// + /// Sets the service provider. + /// + public IServiceProvider Services { get; set; } - /// - /// The optional ID of the Guild the command got registered for. - /// - public ulong? GuildId { get; set; } - } + /// + /// The optional ID of the Guild the command got registered for. + /// + public ulong? GuildId { get; set; } } diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceProviderAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceProviderAttribute.cs index bc660e461..a2f861a37 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceProviderAttribute.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceProviderAttribute.cs @@ -1,49 +1,48 @@ // 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; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// Sets a IChoiceProvider for a command options. ChoiceProviders can be used to provide +/// DiscordApplicationCommandOptionChoice from external sources such as a database. +/// +[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true)] +public class ChoiceProviderAttribute : Attribute { + /// - /// Sets a IChoiceProvider for a command options. ChoiceProviders can be used to provide - /// DiscordApplicationCommandOptionChoice from external sources such as a database. + /// The type of the provider. /// - [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true)] - public class ChoiceProviderAttribute : Attribute - { - - /// - /// The type of the provider. - /// - public Type ProviderType { get; } + public Type ProviderType { get; } - /// - /// Adds a choice provider to this command. - /// - /// The type of the provider. - public ChoiceProviderAttribute(Type providerType) - { - this.ProviderType = providerType; - } + /// + /// Adds a choice provider to this command. + /// + /// The type of the provider. + public ChoiceProviderAttribute(Type providerType) + { + this.ProviderType = providerType; } } diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/IAutocompleteProvider.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/IAutocompleteProvider.cs index 7c0a817ca..7c54b20bf 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/IAutocompleteProvider.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/IAutocompleteProvider.cs @@ -1,41 +1,40 @@ // 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.Threading.Tasks; using DisCatSharp.Entities; -namespace DisCatSharp.ApplicationCommands.Attributes +namespace DisCatSharp.ApplicationCommands.Attributes; + +/// +/// The autocomplete provider. +/// +public interface IAutocompleteProvider { /// - /// The autocomplete provider. + /// Provider the autocompletion. /// - public interface IAutocompleteProvider - { - /// - /// Provider the autocompletion. - /// - /// The context. - Task> Provider(AutocompleteContext context); - } + /// The context. + Task> Provider(AutocompleteContext context); } diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/IChoiceProvider.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/IChoiceProvider.cs index 40099da81..c6a7c02d6 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/IChoiceProvider.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/IChoiceProvider.cs @@ -1,40 +1,39 @@ // 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.Threading.Tasks; using DisCatSharp.Entities; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// All choice providers must inherit from this interface +/// +public interface IChoiceProvider { /// - /// All choice providers must inherit from this interface + /// Sets the choices for the slash command /// - public interface IChoiceProvider - { - /// - /// Sets the choices for the slash command - /// - Task> Provider(); - } + Task> Provider(); } diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/MinimumMaximumAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/MinimumMaximumAttribute.cs index 38369bf8a..0b93261fd 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/MinimumMaximumAttribute.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/MinimumMaximumAttribute.cs @@ -1,98 +1,97 @@ // 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; -namespace DisCatSharp.ApplicationCommands.Attributes +namespace DisCatSharp.ApplicationCommands.Attributes; + +/// +/// Sets a minimum value for this slash command option. Only valid for , or parameters. +/// +[AttributeUsage(AttributeTargets.Parameter)] +public class MinimumAttribute : Attribute { + /// + /// The value. + /// + public object Value { get; internal set; } + /// /// Sets a minimum value for this slash command option. Only valid for , or parameters. /// - [AttributeUsage(AttributeTargets.Parameter)] - public class MinimumAttribute : Attribute + public MinimumAttribute(int value) { - /// - /// The value. - /// - public object Value { get; internal set; } - - /// - /// Sets a minimum value for this slash command option. Only valid for , or parameters. - /// - public MinimumAttribute(int value) - { - this.Value = value; - } + this.Value = value; + } - /// - /// Sets a minimum value for this slash command option. Only valid for , or parameters. - /// - public MinimumAttribute(long value) - { - this.Value = value; - } + /// + /// Sets a minimum value for this slash command option. Only valid for , or parameters. + /// + public MinimumAttribute(long value) + { + this.Value = value; + } - /// - /// Sets a minimum value for this slash command option. Only valid for , or parameters. - /// - public MinimumAttribute(double value) - { - this.Value = value; - } + /// + /// Sets a minimum value for this slash command option. Only valid for , or parameters. + /// + public MinimumAttribute(double value) + { + this.Value = value; } +} + +/// +/// Sets a maximum value for this slash command option. Only valid for , or parameters. +/// +[AttributeUsage(AttributeTargets.Parameter)] +public class MaximumAttribute : Attribute +{ + /// + /// The value. + /// + public object Value { get; internal set; } /// /// Sets a maximum value for this slash command option. Only valid for , or parameters. /// - [AttributeUsage(AttributeTargets.Parameter)] - public class MaximumAttribute : Attribute + public MaximumAttribute(int value) { - /// - /// The value. - /// - public object Value { get; internal set; } - - /// - /// Sets a maximum value for this slash command option. Only valid for , or parameters. - /// - public MaximumAttribute(int value) - { - this.Value = value; - } + this.Value = value; + } - /// - /// Sets a maximum value for this slash command option. Only valid for , or parameters. - /// - public MaximumAttribute(long value) - { - this.Value = value; - } + /// + /// Sets a maximum value for this slash command option. Only valid for , or parameters. + /// + public MaximumAttribute(long value) + { + this.Value = value; + } - /// - /// Sets a maximum value for this slash command option. Only valid for , or parameters. - /// - public MaximumAttribute(double value) - { - this.Value = value; - } + /// + /// Sets a maximum value for this slash command option. Only valid for , or parameters. + /// + public MaximumAttribute(double value) + { + this.Value = value; } } diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/OptionAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/OptionAttribute.cs index 0ed398790..8bc823d53 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/OptionAttribute.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/OptionAttribute.cs @@ -1,66 +1,65 @@ // 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; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// Marks this parameter as an option for a slash command +/// +[AttributeUsage(AttributeTargets.Parameter)] +public class OptionAttribute : Attribute { /// - /// Marks this parameter as an option for a slash command + /// Gets the name of this option. /// - [AttributeUsage(AttributeTargets.Parameter)] - public class OptionAttribute : Attribute - { - /// - /// Gets the name of this option. - /// - public string Name; + public string Name; - /// - /// Gets the description of this option. - /// - public string Description; + /// + /// Gets the description of this option. + /// + public string Description; - /// - /// Whether to autocomplete this option. - /// - public bool Autocomplete; + /// + /// Whether to autocomplete this option. + /// + public bool Autocomplete; - /// - /// Initializes a new instance of the class. - /// - /// The name. - /// The description. - /// If true, autocomplete. - public OptionAttribute(string name, string description, bool autocomplete = false) - { - if (name.Length > 32) - throw new ArgumentException("Slash command option names cannot go over 32 characters."); - else if (description.Length > 100) - throw new ArgumentException("Slash command option descriptions cannot go over 100 characters."); + /// + /// Initializes a new instance of the class. + /// + /// The name. + /// The description. + /// If true, autocomplete. + public OptionAttribute(string name, string description, bool autocomplete = false) + { + if (name.Length > 32) + throw new ArgumentException("Slash command option names cannot go over 32 characters."); + else if (description.Length > 100) + throw new ArgumentException("Slash command option descriptions cannot go over 100 characters."); - this.Name = name.ToLower(); - this.Description = description; - this.Autocomplete = autocomplete; - } + this.Name = name.ToLower(); + this.Description = description; + this.Autocomplete = autocomplete; } } diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireBotPermissionsAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireBotPermissionsAttribute.cs index 41e40ea23..349e19471 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireBotPermissionsAttribute.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireBotPermissionsAttribute.cs @@ -1,75 +1,74 @@ // 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.Threading.Tasks; -namespace DisCatSharp.ApplicationCommands.Attributes +namespace DisCatSharp.ApplicationCommands.Attributes; + +/// +/// Defines that usage of this application command is only possible when the bot is granted a specific permission. +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] +public sealed class ApplicationCommandRequireBotPermissionsAttribute : SlashCheckBaseAttribute { /// - /// Defines that usage of this application command is only possible when the bot is granted a specific permission. + /// Gets the permissions required by this attribute. /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] - public sealed class ApplicationCommandRequireBotPermissionsAttribute : SlashCheckBaseAttribute - { - /// - /// Gets the permissions required by this attribute. - /// - public Permissions Permissions { get; } + public Permissions Permissions { get; } - /// - /// Gets or sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail. - /// - public bool IgnoreDms { get; } = true; + /// + /// Gets or sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail. + /// + public bool IgnoreDms { get; } = true; - /// - /// Defines that usage of this application command is only possible when the bot is granted a specific permission. - /// - /// Permissions required to execute this command. - /// Sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail. - public ApplicationCommandRequireBotPermissionsAttribute(Permissions permissions, bool ignoreDms = true) - { - this.Permissions = permissions; - this.IgnoreDms = ignoreDms; - } + /// + /// Defines that usage of this application command is only possible when the bot is granted a specific permission. + /// + /// Permissions required to execute this command. + /// Sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail. + public ApplicationCommandRequireBotPermissionsAttribute(Permissions permissions, bool ignoreDms = true) + { + this.Permissions = permissions; + this.IgnoreDms = ignoreDms; + } - /// - /// Runs checks. - /// - public override async Task ExecuteChecksAsync(InteractionContext ctx) - { - if (ctx.Guild == null) - return this.IgnoreDms; + /// + /// Runs checks. + /// + public override async Task ExecuteChecksAsync(InteractionContext ctx) + { + if (ctx.Guild == null) + return this.IgnoreDms; - var bot = await ctx.Guild.GetMemberAsync(ctx.Client.CurrentUser.Id).ConfigureAwait(false); - if (bot == null) - return false; + var bot = await ctx.Guild.GetMemberAsync(ctx.Client.CurrentUser.Id).ConfigureAwait(false); + if (bot == null) + return false; - if (bot.Id == ctx.Guild.OwnerId) - return true; + if (bot.Id == ctx.Guild.OwnerId) + return true; - var pbot = ctx.Channel.PermissionsFor(bot); + var pbot = ctx.Channel.PermissionsFor(bot); - return (pbot & Permissions.Administrator) != 0 || (pbot & this.Permissions) == this.Permissions; - } + return (pbot & Permissions.Administrator) != 0 || (pbot & this.Permissions) == this.Permissions; } } diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireDirectMessageAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireDirectMessageAttribute.cs index c50d060bc..6d142ecac 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireDirectMessageAttribute.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireDirectMessageAttribute.cs @@ -1,48 +1,47 @@ // 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.Threading.Tasks; using DisCatSharp.Entities; -namespace DisCatSharp.ApplicationCommands.Attributes +namespace DisCatSharp.ApplicationCommands.Attributes; + +/// +/// Defines that this application command is only usable within a direct message channel. +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] +public sealed class ApplicationCommandRequireDirectMessageAttribute : SlashCheckBaseAttribute { /// - /// Defines that this application command is only usable within a direct message channel. + /// Defines that this command is only usable within a direct message channel. /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] - public sealed class ApplicationCommandRequireDirectMessageAttribute : SlashCheckBaseAttribute - { - /// - /// Defines that this command is only usable within a direct message channel. - /// - public ApplicationCommandRequireDirectMessageAttribute() - { } + public ApplicationCommandRequireDirectMessageAttribute() + { } - /// - /// Runs checks. - /// - public override Task ExecuteChecksAsync(InteractionContext ctx) - => Task.FromResult(ctx.Channel is DiscordDmChannel); - } + /// + /// Runs checks. + /// + public override Task ExecuteChecksAsync(InteractionContext ctx) + => Task.FromResult(ctx.Channel is DiscordDmChannel); } diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireDisCatSharpDeveloperAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireDisCatSharpDeveloperAttribute.cs index d6a24261b..c88512cb0 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireDisCatSharpDeveloperAttribute.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireDisCatSharpDeveloperAttribute.cs @@ -1,50 +1,49 @@ // 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.Linq; using System.Threading.Tasks; -namespace DisCatSharp.ApplicationCommands.Attributes +namespace DisCatSharp.ApplicationCommands.Attributes; + +/// +/// Defines that this application command is restricted to the owner of the bot. +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] +public sealed class ApplicationCommandRequireDisCatSharpDeveloperAttribute : SlashCheckBaseAttribute { /// /// Defines that this application command is restricted to the owner of the bot. /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] - public sealed class ApplicationCommandRequireDisCatSharpDeveloperAttribute : SlashCheckBaseAttribute - { - /// - /// Defines that this application command is restricted to the owner of the bot. - /// - public ApplicationCommandRequireDisCatSharpDeveloperAttribute() - { } + public ApplicationCommandRequireDisCatSharpDeveloperAttribute() + { } - /// - /// Runs checks. - /// - public override async Task ExecuteChecksAsync(InteractionContext ctx) - { - var team = (await ctx.Client.GetLibraryDevelopmentTeamAsync()).Developers; - return team?.Any(x => x.Id == ctx.User.Id) ?? false; - } + /// + /// Runs checks. + /// + public override async Task ExecuteChecksAsync(InteractionContext ctx) + { + var team = (await ctx.Client.GetLibraryDevelopmentTeamAsync()).Developers; + return team?.Any(x => x.Id == ctx.User.Id) ?? false; } } diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireGuildAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireGuildAttribute.cs index d72c6b80a..4fa63b5ac 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireGuildAttribute.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireGuildAttribute.cs @@ -1,46 +1,45 @@ // 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.Threading.Tasks; -namespace DisCatSharp.ApplicationCommands.Attributes +namespace DisCatSharp.ApplicationCommands.Attributes; + +/// +/// Defines that this application command is only usable within a guild. +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] +public sealed class ApplicationCommandRequireGuildAttribute : SlashCheckBaseAttribute { /// - /// Defines that this application command is only usable within a guild. + /// Defines that this command is only usable within a guild. /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] - public sealed class ApplicationCommandRequireGuildAttribute : SlashCheckBaseAttribute - { - /// - /// Defines that this command is only usable within a guild. - /// - public ApplicationCommandRequireGuildAttribute() - { } + public ApplicationCommandRequireGuildAttribute() + { } - /// - /// Runs checks. - /// - public override Task ExecuteChecksAsync(InteractionContext ctx) - => Task.FromResult(ctx.Guild != null); - } + /// + /// Runs checks. + /// + public override Task ExecuteChecksAsync(InteractionContext ctx) + => Task.FromResult(ctx.Guild != null); } diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireNsfwAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireNsfwAttribute.cs index 7c25b081e..8887bf110 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireNsfwAttribute.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireNsfwAttribute.cs @@ -1,46 +1,45 @@ // 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.Threading.Tasks; -namespace DisCatSharp.ApplicationCommands.Attributes +namespace DisCatSharp.ApplicationCommands.Attributes; + +/// +/// Defines that this application command is only usable within a guild. +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] +public sealed class ApplicationCommandRequireNsfwAttribute : SlashCheckBaseAttribute { /// - /// Defines that this application command is only usable within a guild. + /// Defines that this command is only usable within a guild. /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] - public sealed class ApplicationCommandRequireNsfwAttribute : SlashCheckBaseAttribute - { - /// - /// Defines that this command is only usable within a guild. - /// - public ApplicationCommandRequireNsfwAttribute() - { } + public ApplicationCommandRequireNsfwAttribute() + { } - /// - /// Runs checks. - /// - public override Task ExecuteChecksAsync(InteractionContext ctx) - => Task.FromResult(ctx.Channel.Guild == null || ctx.Channel.IsNsfw); - } + /// + /// Runs checks. + /// + public override Task ExecuteChecksAsync(InteractionContext ctx) + => Task.FromResult(ctx.Channel.Guild == null || ctx.Channel.IsNsfw); } diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireOwnerAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireOwnerAttribute.cs index 46f9b9b34..099b511cf 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireOwnerAttribute.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireOwnerAttribute.cs @@ -1,52 +1,51 @@ // 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.Linq; using System.Threading.Tasks; -namespace DisCatSharp.ApplicationCommands.Attributes +namespace DisCatSharp.ApplicationCommands.Attributes; + +/// +/// Defines that this application command is restricted to the owner of the bot. +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] +public sealed class ApplicationCommandRequireOwnerAttribute : SlashCheckBaseAttribute { /// /// Defines that this application command is restricted to the owner of the bot. /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] - public sealed class ApplicationCommandRequireOwnerAttribute : SlashCheckBaseAttribute - { - /// - /// Defines that this application command is restricted to the owner of the bot. - /// - public ApplicationCommandRequireOwnerAttribute() - { } + public ApplicationCommandRequireOwnerAttribute() + { } - /// - /// Runs checks. - /// - public override Task ExecuteChecksAsync(InteractionContext ctx) - { - var app = ctx.Client.CurrentApplication; - var me = ctx.Client.CurrentUser; + /// + /// Runs checks. + /// + public override Task ExecuteChecksAsync(InteractionContext ctx) + { + var app = ctx.Client.CurrentApplication; + var me = ctx.Client.CurrentUser; - return app != null ? Task.FromResult(app.Owners.Any(x => x.Id == ctx.User.Id)) : Task.FromResult(ctx.User.Id == me.Id); - } + return app != null ? Task.FromResult(app.Owners.Any(x => x.Id == ctx.User.Id)) : Task.FromResult(ctx.User.Id == me.Id); } } diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireOwnerOrIdAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireOwnerOrIdAttribute.cs index 614915592..27d30038c 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireOwnerOrIdAttribute.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireOwnerOrIdAttribute.cs @@ -1,67 +1,66 @@ // 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.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; -namespace DisCatSharp.ApplicationCommands.Attributes +namespace DisCatSharp.ApplicationCommands.Attributes; + +/// +/// Requires ownership of the bot or a whitelisted id to execute this command. +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] +public sealed class ApplicationCommandRequireOwnerOrIdAttribute : SlashCheckBaseAttribute { /// - /// Requires ownership of the bot or a whitelisted id to execute this command. + /// Allowed user ids /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] - public sealed class ApplicationCommandRequireOwnerOrIdAttribute : SlashCheckBaseAttribute - { - /// - /// Allowed user ids - /// - public IReadOnlyList UserIds { get; } + public IReadOnlyList UserIds { get; } - /// - /// Defines that usage of this command is restricted to the owner or whitelisted ids of the bot. - /// - /// List of allowed user ids - public ApplicationCommandRequireOwnerOrIdAttribute(params ulong[] userIds) - { - this.UserIds = new ReadOnlyCollection(userIds); - } + /// + /// Defines that usage of this command is restricted to the owner or whitelisted ids of the bot. + /// + /// List of allowed user ids + public ApplicationCommandRequireOwnerOrIdAttribute(params ulong[] userIds) + { + this.UserIds = new ReadOnlyCollection(userIds); + } - /// - /// Executes the a check. - /// - /// The command context.s - public override Task ExecuteChecksAsync(InteractionContext ctx) - { - var app = ctx.Client.CurrentApplication; - var me = ctx.Client.CurrentUser; + /// + /// Executes the a check. + /// + /// The command context.s + public override Task ExecuteChecksAsync(InteractionContext ctx) + { + var app = ctx.Client.CurrentApplication; + var me = ctx.Client.CurrentUser; - var owner = app != null ? Task.FromResult(app.Owners.Any(x => x.Id == ctx.User.Id)) : Task.FromResult(ctx.User.Id == me.Id); + var owner = app != null ? Task.FromResult(app.Owners.Any(x => x.Id == ctx.User.Id)) : Task.FromResult(ctx.User.Id == me.Id); - var allowed = this.UserIds.Contains(ctx.User.Id); + var allowed = this.UserIds.Contains(ctx.User.Id); - return allowed ? Task.FromResult(true) : owner; - } + return allowed ? Task.FromResult(true) : owner; } } diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequirePermissionsAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequirePermissionsAttribute.cs index cdcf09590..cc8ff89ec 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequirePermissionsAttribute.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequirePermissionsAttribute.cs @@ -1,85 +1,84 @@ // 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.Threading.Tasks; -namespace DisCatSharp.ApplicationCommands.Attributes +namespace DisCatSharp.ApplicationCommands.Attributes; + +/// +/// Defines that usage of this application command is restricted to members with specified permissions. This check also verifies that the bot has the same permissions. +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] +public sealed class ApplicationCommandRequirePermissionsAttribute : SlashCheckBaseAttribute { /// - /// Defines that usage of this application command is restricted to members with specified permissions. This check also verifies that the bot has the same permissions. + /// Gets the permissions required by this attribute. /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] - public sealed class ApplicationCommandRequirePermissionsAttribute : SlashCheckBaseAttribute - { - /// - /// Gets the permissions required by this attribute. - /// - public Permissions Permissions { get; } + public Permissions Permissions { get; } - /// - /// Gets or sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail. - /// - public bool IgnoreDms { get; } = true; + /// + /// Gets or sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail. + /// + public bool IgnoreDms { get; } = true; - /// - /// Defines that usage of this command is restricted to members with specified permissions. This check also verifies that the bot has the same permissions. - /// - /// Permissions required to execute this command. - /// Sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail. - public ApplicationCommandRequirePermissionsAttribute(Permissions permissions, bool ignoreDms = true) - { - this.Permissions = permissions; - this.IgnoreDms = ignoreDms; - } + /// + /// Defines that usage of this command is restricted to members with specified permissions. This check also verifies that the bot has the same permissions. + /// + /// Permissions required to execute this command. + /// Sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail. + public ApplicationCommandRequirePermissionsAttribute(Permissions permissions, bool ignoreDms = true) + { + this.Permissions = permissions; + this.IgnoreDms = ignoreDms; + } - /// - /// Runs checks. - /// - public override async Task ExecuteChecksAsync(InteractionContext ctx) - { - if (ctx.Guild == null) - return this.IgnoreDms; + /// + /// Runs checks. + /// + public override async Task ExecuteChecksAsync(InteractionContext ctx) + { + if (ctx.Guild == null) + return this.IgnoreDms; - var usr = ctx.Member; - if (usr == null) - return false; - var pusr = ctx.Channel.PermissionsFor(usr); + var usr = ctx.Member; + if (usr == null) + return false; + var pusr = ctx.Channel.PermissionsFor(usr); - var bot = await ctx.Guild.GetMemberAsync(ctx.Client.CurrentUser.Id).ConfigureAwait(false); - if (bot == null) - return false; - var pbot = ctx.Channel.PermissionsFor(bot); + var bot = await ctx.Guild.GetMemberAsync(ctx.Client.CurrentUser.Id).ConfigureAwait(false); + if (bot == null) + return false; + var pbot = ctx.Channel.PermissionsFor(bot); - var usrok = ctx.Guild.OwnerId == usr.Id; - var botok = ctx.Guild.OwnerId == bot.Id; + var usrok = ctx.Guild.OwnerId == usr.Id; + var botok = ctx.Guild.OwnerId == bot.Id; - if (!usrok) - usrok = (pusr & Permissions.Administrator) != 0 || (pusr & this.Permissions) == this.Permissions; + if (!usrok) + usrok = (pusr & Permissions.Administrator) != 0 || (pusr & this.Permissions) == this.Permissions; - if (!botok) - botok = (pbot & Permissions.Administrator) != 0 || (pbot & this.Permissions) == this.Permissions; + if (!botok) + botok = (pbot & Permissions.Administrator) != 0 || (pbot & this.Permissions) == this.Permissions; - return usrok && botok; - } + return usrok && botok; } } diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireUserPermissionsAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireUserPermissionsAttribute.cs index 1dc7e7a3d..4a5bc1187 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireUserPermissionsAttribute.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireUserPermissionsAttribute.cs @@ -1,77 +1,76 @@ // 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.Threading.Tasks; -namespace DisCatSharp.ApplicationCommands.Attributes +namespace DisCatSharp.ApplicationCommands.Attributes; + +/// +/// Defines that usage of this application command is restricted to members with specified permissions. +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] +public sealed class ApplicationCommandRequireUserPermissionsAttribute : SlashCheckBaseAttribute { /// - /// Defines that usage of this application command is restricted to members with specified permissions. + /// Gets the permissions required by this attribute. /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] - public sealed class ApplicationCommandRequireUserPermissionsAttribute : SlashCheckBaseAttribute - { - /// - /// Gets the permissions required by this attribute. - /// - public Permissions Permissions { get; } + public Permissions Permissions { get; } - /// - /// Gets or sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail. - /// - public bool IgnoreDms { get; } = true; + /// + /// Gets or sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail. + /// + public bool IgnoreDms { get; } = true; - /// - /// Defines that usage of this command is restricted to members with specified permissions. - /// - /// Permissions required to execute this command. - /// Sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail. - public ApplicationCommandRequireUserPermissionsAttribute(Permissions permissions, bool ignoreDms = true) - { - this.Permissions = permissions; - this.IgnoreDms = ignoreDms; - } + /// + /// Defines that usage of this command is restricted to members with specified permissions. + /// + /// Permissions required to execute this command. + /// Sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail. + public ApplicationCommandRequireUserPermissionsAttribute(Permissions permissions, bool ignoreDms = true) + { + this.Permissions = permissions; + this.IgnoreDms = ignoreDms; + } - /// - /// Runs checks. - /// - public override Task ExecuteChecksAsync(InteractionContext ctx) - { - if (ctx.Guild == null) - return Task.FromResult(this.IgnoreDms); + /// + /// Runs checks. + /// + public override Task ExecuteChecksAsync(InteractionContext ctx) + { + if (ctx.Guild == null) + return Task.FromResult(this.IgnoreDms); - var usr = ctx.Member; - if (usr == null) - return Task.FromResult(false); + var usr = ctx.Member; + if (usr == null) + return Task.FromResult(false); - if (usr.Id == ctx.Guild.OwnerId) - return Task.FromResult(true); + if (usr.Id == ctx.Guild.OwnerId) + return Task.FromResult(true); - var pusr = ctx.Channel.PermissionsFor(usr); + var pusr = ctx.Channel.PermissionsFor(usr); - return (pusr & Permissions.Administrator) != 0 - ? Task.FromResult(true) - : (pusr & this.Permissions) == this.Permissions ? Task.FromResult(true) : Task.FromResult(false); - } + return (pusr & Permissions.Administrator) != 0 + ? Task.FromResult(true) + : (pusr & this.Permissions) == this.Permissions ? Task.FromResult(true) : Task.FromResult(false); } } diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/SlashCheckBaseAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/SlashCheckBaseAttribute.cs index d1cbd01ec..0fcac2c69 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/SlashCheckBaseAttribute.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/SlashCheckBaseAttribute.cs @@ -1,40 +1,39 @@ // 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.Threading.Tasks; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// The base class for a pre-execution check for a application command. +/// +public abstract class SlashCheckBaseAttribute : Attribute { /// - /// The base class for a pre-execution check for a application command. + /// Checks whether this command can be executed within the current context. /// - public abstract class SlashCheckBaseAttribute : Attribute - { - /// - /// Checks whether this command can be executed within the current context. - /// - /// The context. - /// Whether the checks passed. - public abstract Task ExecuteChecksAsync(InteractionContext ctx); - } + /// The context. + /// Whether the checks passed. + public abstract Task ExecuteChecksAsync(InteractionContext ctx); } diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/SlashCommandAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/SlashCommandAttribute.cs index 150708629..243a4163e 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/SlashCommandAttribute.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/SlashCommandAttribute.cs @@ -1,122 +1,121 @@ // 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; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// Marks this method as a slash command +/// +[AttributeUsage(AttributeTargets.Method)] +public class SlashCommandAttribute : Attribute { /// - /// Marks this method as a slash command + /// Gets the name of this command /// - [AttributeUsage(AttributeTargets.Method)] - public class SlashCommandAttribute : Attribute - { - /// - /// Gets the name of this command - /// - public string Name { get; set; } + public string Name { get; set; } - /// - /// Gets the description of this command - /// - public string Description { get; set; } + /// + /// Gets the description of this command + /// + public string Description { get; set; } - /// - /// Gets the needed permission of this command - /// - public Permissions? DefaultMemberPermissions { get; set; } + /// + /// Gets the needed permission of this command + /// + public Permissions? DefaultMemberPermissions { get; set; } - /// - /// Gets the dm permission of this command - /// - public bool? DmPermission { get; set; } + /// + /// Gets the dm permission of this command + /// + public bool? DmPermission { get; set; } - /// - /// Gets whether this command is marked as NSFW - /// - public bool IsNsfw { get; set; } + /// + /// Gets whether this command is marked as NSFW + /// + public bool IsNsfw { get; set; } - /// - /// Marks this method as a slash command - /// - /// The name of this slash command. - /// The description of this slash command. - /// Whether this command is marked as NSFW. - public SlashCommandAttribute(string name, string description, bool isNsfw = false) - { - this.Name = name.ToLower(); - this.Description = description; - this.DefaultMemberPermissions = null; - this.DmPermission = null; - this.IsNsfw = isNsfw; - } + /// + /// Marks this method as a slash command + /// + /// The name of this slash command. + /// The description of this slash command. + /// Whether this command is marked as NSFW. + public SlashCommandAttribute(string name, string description, bool isNsfw = false) + { + this.Name = name.ToLower(); + this.Description = description; + this.DefaultMemberPermissions = null; + this.DmPermission = null; + this.IsNsfw = isNsfw; + } - /// - /// Marks this method as a slash command - /// - /// The name of this slash command. - /// The description of this slash command. - /// The default member permissions. - /// Whether this command is marked as NSFW. - public SlashCommandAttribute(string name, string description, long defaultMemberPermissions, bool isNsfw = false) - { - this.Name = name.ToLower(); - this.Description = description; - this.DefaultMemberPermissions = (Permissions)defaultMemberPermissions; - this.DmPermission = null; - this.IsNsfw = isNsfw; - } + /// + /// Marks this method as a slash command + /// + /// The name of this slash command. + /// The description of this slash command. + /// The default member permissions. + /// Whether this command is marked as NSFW. + public SlashCommandAttribute(string name, string description, long defaultMemberPermissions, bool isNsfw = false) + { + this.Name = name.ToLower(); + this.Description = description; + this.DefaultMemberPermissions = (Permissions)defaultMemberPermissions; + this.DmPermission = null; + this.IsNsfw = isNsfw; + } - /// - /// Marks this method as a slash command - /// - /// The name of this slash command. - /// The description of this slash command. - /// The dm permission. - /// Whether this command is marked as NSFW. - public SlashCommandAttribute(string name, string description, bool dmPermission, bool isNsfw = false) - { - this.Name = name.ToLower(); - this.Description = description; - this.DefaultMemberPermissions = null; - this.DmPermission = dmPermission; - this.IsNsfw = isNsfw; - } + /// + /// Marks this method as a slash command + /// + /// The name of this slash command. + /// The description of this slash command. + /// The dm permission. + /// Whether this command is marked as NSFW. + public SlashCommandAttribute(string name, string description, bool dmPermission, bool isNsfw = false) + { + this.Name = name.ToLower(); + this.Description = description; + this.DefaultMemberPermissions = null; + this.DmPermission = dmPermission; + this.IsNsfw = isNsfw; + } - /// - /// Marks this method as a slash command - /// - /// The name of this slash command. - /// The description of this slash command. - /// The default member permissions. - /// The dm permission. - /// Whether this command is marked as NSFW. - public SlashCommandAttribute(string name, string description, long defaultMemberPermissions, bool dmPermission, bool isNsfw = false) - { - this.Name = name.ToLower(); - this.Description = description; - this.DefaultMemberPermissions = (Permissions)defaultMemberPermissions; - this.DmPermission = dmPermission; - this.IsNsfw = isNsfw; - } + /// + /// Marks this method as a slash command + /// + /// The name of this slash command. + /// The description of this slash command. + /// The default member permissions. + /// The dm permission. + /// Whether this command is marked as NSFW. + public SlashCommandAttribute(string name, string description, long defaultMemberPermissions, bool dmPermission, bool isNsfw = false) + { + this.Name = name.ToLower(); + this.Description = description; + this.DefaultMemberPermissions = (Permissions)defaultMemberPermissions; + this.DmPermission = dmPermission; + this.IsNsfw = isNsfw; } } diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/SlashCommandGroupAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/SlashCommandGroupAttribute.cs index 9480ae570..1aae5c0da 100644 --- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/SlashCommandGroupAttribute.cs +++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/SlashCommandGroupAttribute.cs @@ -1,122 +1,121 @@ // 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; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// Marks this class a slash command group +/// +[AttributeUsage(AttributeTargets.Class)] +public class SlashCommandGroupAttribute : Attribute { /// - /// Marks this class a slash command group + /// Gets the name of this slash command group /// - [AttributeUsage(AttributeTargets.Class)] - public class SlashCommandGroupAttribute : Attribute - { - /// - /// Gets the name of this slash command group - /// - public string Name { get; set; } + public string Name { get; set; } - /// - /// Gets the description of this slash command group - /// - public string Description { get; set; } + /// + /// Gets the description of this slash command group + /// + public string Description { get; set; } - /// - /// Gets the needed permission of this slash command group - /// - public Permissions? DefaultMemberPermissions { get; set; } + /// + /// Gets the needed permission of this slash command group + /// + public Permissions? DefaultMemberPermissions { get; set; } - /// - /// Gets the dm permission of this slash command group - /// - public bool? DmPermission { get; set; } + /// + /// Gets the dm permission of this slash command group + /// + public bool? DmPermission { get; set; } - /// - /// Gets whether this command is marked as NSFW - /// - public bool IsNsfw { get; set; } + /// + /// Gets whether this command is marked as NSFW + /// + public bool IsNsfw { get; set; } - /// - /// Marks this class as a slash command group - /// - /// The name of this slash command group. - /// The description of this slash command group. - /// Whether this command is marked as NSFW. - public SlashCommandGroupAttribute(string name, string description, bool isNsfw = false) - { - this.Name = name.ToLower(); - this.Description = description; - this.DefaultMemberPermissions = null; - this.DmPermission = null; - this.IsNsfw = isNsfw; - } + /// + /// Marks this class as a slash command group + /// + /// The name of this slash command group. + /// The description of this slash command group. + /// Whether this command is marked as NSFW. + public SlashCommandGroupAttribute(string name, string description, bool isNsfw = false) + { + this.Name = name.ToLower(); + this.Description = description; + this.DefaultMemberPermissions = null; + this.DmPermission = null; + this.IsNsfw = isNsfw; + } - /// - /// Marks this method as a slash command group - /// - /// The name of this slash command. - /// The description of this slash command. - /// The default member permissions. - /// Whether this command is marked as NSFW. - public SlashCommandGroupAttribute(string name, string description, long defaultMemberPermissions, bool isNsfw = false) - { - this.Name = name.ToLower(); - this.Description = description; - this.DefaultMemberPermissions = (Permissions)defaultMemberPermissions; - this.DmPermission = null; - this.IsNsfw = isNsfw; - } + /// + /// Marks this method as a slash command group + /// + /// The name of this slash command. + /// The description of this slash command. + /// The default member permissions. + /// Whether this command is marked as NSFW. + public SlashCommandGroupAttribute(string name, string description, long defaultMemberPermissions, bool isNsfw = false) + { + this.Name = name.ToLower(); + this.Description = description; + this.DefaultMemberPermissions = (Permissions)defaultMemberPermissions; + this.DmPermission = null; + this.IsNsfw = isNsfw; + } - /// - /// Marks this method as a slash command group - /// - /// The name of this slash command. - /// The description of this slash command. - /// The dm permission. - /// Whether this command is marked as NSFW. - public SlashCommandGroupAttribute(string name, string description, bool dmPermission, bool isNsfw = false) - { - this.Name = name.ToLower(); - this.Description = description; - this.DefaultMemberPermissions = null; - this.DmPermission = dmPermission; - this.IsNsfw = isNsfw; - } + /// + /// Marks this method as a slash command group + /// + /// The name of this slash command. + /// The description of this slash command. + /// The dm permission. + /// Whether this command is marked as NSFW. + public SlashCommandGroupAttribute(string name, string description, bool dmPermission, bool isNsfw = false) + { + this.Name = name.ToLower(); + this.Description = description; + this.DefaultMemberPermissions = null; + this.DmPermission = dmPermission; + this.IsNsfw = isNsfw; + } - /// - /// Marks this method as a slash command group - /// - /// The name of this slash command. - /// The description of this slash command. - /// The default member permissions. - /// The dm permission. - /// Whether this command is marked as NSFW. - public SlashCommandGroupAttribute(string name, string description, long defaultMemberPermissions, bool dmPermission, bool isNsfw = false) - { - this.Name = name.ToLower(); - this.Description = description; - this.DefaultMemberPermissions = (Permissions)defaultMemberPermissions; - this.DmPermission = dmPermission; - this.IsNsfw = isNsfw; - } + /// + /// Marks this method as a slash command group + /// + /// The name of this slash command. + /// The description of this slash command. + /// The default member permissions. + /// The dm permission. + /// Whether this command is marked as NSFW. + public SlashCommandGroupAttribute(string name, string description, long defaultMemberPermissions, bool dmPermission, bool isNsfw = false) + { + this.Name = name.ToLower(); + this.Description = description; + this.DefaultMemberPermissions = (Permissions)defaultMemberPermissions; + this.DmPermission = dmPermission; + this.IsNsfw = isNsfw; } } diff --git a/DisCatSharp.ApplicationCommands/Checks/ApplicationCommandEqualityChecks.cs b/DisCatSharp.ApplicationCommands/Checks/ApplicationCommandEqualityChecks.cs index 534656ed9..37347a81c 100644 --- a/DisCatSharp.ApplicationCommands/Checks/ApplicationCommandEqualityChecks.cs +++ b/DisCatSharp.ApplicationCommands/Checks/ApplicationCommandEqualityChecks.cs @@ -1,281 +1,280 @@ // 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 DisCatSharp.Entities; using DisCatSharp.Enums; using Microsoft.Extensions.Logging; using Newtonsoft.Json; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +internal static class ApplicationCommandEqualityChecks { - internal static class ApplicationCommandEqualityChecks + /// + /// Whether two application commands are equal. + /// + /// Source command. + /// Command to check against. + internal static bool IsEqualTo(this DiscordApplicationCommand ac1, DiscordApplicationCommand targetApplicationCommand) { - /// - /// Whether two application commands are equal. - /// - /// Source command. - /// Command to check against. - internal static bool IsEqualTo(this DiscordApplicationCommand ac1, DiscordApplicationCommand targetApplicationCommand) - { - if (targetApplicationCommand is null || ac1 is null) - return false; + if (targetApplicationCommand is null || ac1 is null) + return false; - DiscordApplicationCommand sourceApplicationCommand = new( - ac1.Name, ac1.Description, ac1.Options, - ac1.Type, - ac1.NameLocalizations, ac1.DescriptionLocalizations - ); + DiscordApplicationCommand sourceApplicationCommand = new( + ac1.Name, ac1.Description, ac1.Options, + ac1.Type, + ac1.NameLocalizations, ac1.DescriptionLocalizations + ); - ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC Change Check] Command {ac1.Name}\n\n[{JsonConvert.SerializeObject(sourceApplicationCommand)},{JsonConvert.SerializeObject(targetApplicationCommand)}]\n\n"); + ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC Change Check] Command {ac1.Name}\n\n[{JsonConvert.SerializeObject(sourceApplicationCommand)},{JsonConvert.SerializeObject(targetApplicationCommand)}]\n\n"); - return ac1.Type == targetApplicationCommand.Type && sourceApplicationCommand.SoftEqual(targetApplicationCommand, ac1.Type, ApplicationCommandsExtension.Configuration?.EnableLocalization ?? false); - } + return ac1.Type == targetApplicationCommand.Type && sourceApplicationCommand.SoftEqual(targetApplicationCommand, ac1.Type, ApplicationCommandsExtension.Configuration?.EnableLocalization ?? false); + } - /// - /// Checks softly whether two s are the same. - /// Excluding id, application id and version here. - /// - /// Source application command. - /// Application command to check against. - /// The application command type. - /// Whether localization is enabled. - internal static bool SoftEqual(this DiscordApplicationCommand source, DiscordApplicationCommand target, ApplicationCommandType type, bool localizationEnabled = false) - { - return localizationEnabled - ? type switch - { - ApplicationCommandType.ChatInput => DeepEqual(source, target, localizationEnabled), - _ => (source.Name == target.Name) - && (source.Type == target.Type) && (source.NameLocalizations == target.NameLocalizations) - && (source.DefaultMemberPermissions == target.DefaultMemberPermissions) - && (source.IsNsfw == target.IsNsfw) - } - : type switch - { - ApplicationCommandType.ChatInput => DeepEqual(source, target), - _ => (source.Name == target.Name) - && (source.Type == target.Type) - && (source.DefaultMemberPermissions == target.DefaultMemberPermissions) - && (source.IsNsfw == target.IsNsfw) - }; - } + /// + /// Checks softly whether two s are the same. + /// Excluding id, application id and version here. + /// + /// Source application command. + /// Application command to check against. + /// The application command type. + /// Whether localization is enabled. + internal static bool SoftEqual(this DiscordApplicationCommand source, DiscordApplicationCommand target, ApplicationCommandType type, bool localizationEnabled = false) + { + return localizationEnabled + ? type switch + { + ApplicationCommandType.ChatInput => DeepEqual(source, target, localizationEnabled), + _ => (source.Name == target.Name) + && (source.Type == target.Type) && (source.NameLocalizations == target.NameLocalizations) + && (source.DefaultMemberPermissions == target.DefaultMemberPermissions) + && (source.IsNsfw == target.IsNsfw) + } + : type switch + { + ApplicationCommandType.ChatInput => DeepEqual(source, target), + _ => (source.Name == target.Name) + && (source.Type == target.Type) + && (source.DefaultMemberPermissions == target.DefaultMemberPermissions) + && (source.IsNsfw == target.IsNsfw) + }; + } - /// - /// Checks deeply whether two s are the same. - /// Excluding id, application id and version here. - /// - /// Source application command. - /// Application command to check against. - /// Whether localization is enabled. - internal static bool DeepEqual(DiscordApplicationCommand source, DiscordApplicationCommand target, bool localizationEnabled = false) + /// + /// Checks deeply whether two s are the same. + /// Excluding id, application id and version here. + /// + /// Source application command. + /// Application command to check against. + /// Whether localization is enabled. + internal static bool DeepEqual(DiscordApplicationCommand source, DiscordApplicationCommand target, bool localizationEnabled = false) + { + var rootCheck = (source.Name == target.Name) && (source.Description == target.Description) && (source.Type == target.Type) + && (source.DefaultMemberPermissions == target.DefaultMemberPermissions) && (source.DmPermission == target.DmPermission) && (source.IsNsfw == target.IsNsfw); + if (localizationEnabled) + rootCheck = rootCheck && (source.NameLocalizations == target.NameLocalizations) && (source.DescriptionLocalizations == target.DescriptionLocalizations); + + if (source.Options == null && target.Options == null) + return rootCheck; + else if ((source.Options != null && target.Options == null) || (source.Options == null && target.Options != null)) + return false; + else if (source.Options.Any(o => o.Type == ApplicationCommandOptionType.SubCommandGroup) && target.Options.Any(o => o.Type == ApplicationCommandOptionType.SubCommandGroup)) { - var rootCheck = (source.Name == target.Name) && (source.Description == target.Description) && (source.Type == target.Type) - && (source.DefaultMemberPermissions == target.DefaultMemberPermissions) && (source.DmPermission == target.DmPermission) && (source.IsNsfw == target.IsNsfw); - if (localizationEnabled) - rootCheck = rootCheck && (source.NameLocalizations == target.NameLocalizations) && (source.DescriptionLocalizations == target.DescriptionLocalizations); - - if (source.Options == null && target.Options == null) - return rootCheck; - else if ((source.Options != null && target.Options == null) || (source.Options == null && target.Options != null)) - return false; - else if (source.Options.Any(o => o.Type == ApplicationCommandOptionType.SubCommandGroup) && target.Options.Any(o => o.Type == ApplicationCommandOptionType.SubCommandGroup)) + List minimalSourceOptions = new(); + List minimalTargetOptions = new(); + + foreach (var option in source.Options) { - List minimalSourceOptions = new(); - List minimalTargetOptions = new(); + List minimalSubSourceOptions = new(); - foreach (var option in source.Options) + foreach (var subOption in option.Options) { - List minimalSubSourceOptions = new(); + List minimalSubSubSourceOptions = null; - foreach (var subOption in option.Options) + if (subOption.Options != null) { - List minimalSubSubSourceOptions = null; + minimalSubSubSourceOptions = new(); - if (subOption.Options != null) + foreach (var subSubOption in subOption.Options) { - minimalSubSubSourceOptions = new(); - - foreach (var subSubOption in subOption.Options) - { - minimalSubSubSourceOptions.Add(new DiscordApplicationCommandOption( - subSubOption.Name, subSubOption.Description, subSubOption.Type, subSubOption.Required ?? false, - subSubOption.Choices, null, subSubOption.ChannelTypes, subSubOption.AutoComplete ?? false, - subSubOption.MinimumValue, subSubOption.MaximumValue, - localizationEnabled ? subSubOption.NameLocalizations : null, - localizationEnabled ? subSubOption.DescriptionLocalizations : null - )); - } - - minimalSubSourceOptions.Add(new DiscordApplicationCommandOption( - subOption.Name, subOption.Description, subOption.Type, - options: minimalSubSubSourceOptions, - nameLocalizations: localizationEnabled ? subOption.NameLocalizations : null, - descriptionLocalizations: localizationEnabled ? subOption.DescriptionLocalizations : null + minimalSubSubSourceOptions.Add(new DiscordApplicationCommandOption( + subSubOption.Name, subSubOption.Description, subSubOption.Type, subSubOption.Required ?? false, + subSubOption.Choices, null, subSubOption.ChannelTypes, subSubOption.AutoComplete ?? false, + subSubOption.MinimumValue, subSubOption.MaximumValue, + localizationEnabled ? subSubOption.NameLocalizations : null, + localizationEnabled ? subSubOption.DescriptionLocalizations : null )); } + minimalSubSourceOptions.Add(new DiscordApplicationCommandOption( + subOption.Name, subOption.Description, subOption.Type, + options: minimalSubSubSourceOptions, + nameLocalizations: localizationEnabled ? subOption.NameLocalizations : null, + descriptionLocalizations: localizationEnabled ? subOption.DescriptionLocalizations : null + )); } - minimalSourceOptions.Add(new DiscordApplicationCommandOption( - option.Name, option.Description, option.Type, - options: minimalSubSourceOptions, - nameLocalizations: localizationEnabled ? option.NameLocalizations : null, - descriptionLocalizations: localizationEnabled ? option.DescriptionLocalizations : null - )); } - foreach (var option in target.Options) + minimalSourceOptions.Add(new DiscordApplicationCommandOption( + option.Name, option.Description, option.Type, + options: minimalSubSourceOptions, + nameLocalizations: localizationEnabled ? option.NameLocalizations : null, + descriptionLocalizations: localizationEnabled ? option.DescriptionLocalizations : null + )); + } + + foreach (var option in target.Options) + { + List minimalSubTargetOptions = new(); + + foreach (var subOption in option.Options) { - List minimalSubTargetOptions = new(); + List minimalSubSubTargetOptions = null; - foreach (var subOption in option.Options) + if (subOption.Options != null && subOption.Options.Any()) { - List minimalSubSubTargetOptions = null; + minimalSubSubTargetOptions = new(); - if (subOption.Options != null && subOption.Options.Any()) + foreach (var subSubOption in subOption.Options) { - minimalSubSubTargetOptions = new(); - - foreach (var subSubOption in subOption.Options) - { - minimalSubSubTargetOptions.Add(new DiscordApplicationCommandOption( - subSubOption.Name, subSubOption.Description, subSubOption.Type, subSubOption.Required ?? false, - subSubOption.Choices, null, subSubOption.ChannelTypes, subSubOption.AutoComplete ?? false, - subSubOption.MinimumValue, subSubOption.MaximumValue, - localizationEnabled ? subSubOption.NameLocalizations : null, - localizationEnabled ? subSubOption.DescriptionLocalizations : null - )); - } - - minimalSubTargetOptions.Add(new DiscordApplicationCommandOption( - subOption.Name, subOption.Description, subOption.Type, - options: minimalSubSubTargetOptions, - nameLocalizations: localizationEnabled ? subOption.NameLocalizations : null, - descriptionLocalizations: localizationEnabled ? subOption.DescriptionLocalizations : null + minimalSubSubTargetOptions.Add(new DiscordApplicationCommandOption( + subSubOption.Name, subSubOption.Description, subSubOption.Type, subSubOption.Required ?? false, + subSubOption.Choices, null, subSubOption.ChannelTypes, subSubOption.AutoComplete ?? false, + subSubOption.MinimumValue, subSubOption.MaximumValue, + localizationEnabled ? subSubOption.NameLocalizations : null, + localizationEnabled ? subSubOption.DescriptionLocalizations : null )); } - } - minimalTargetOptions.Add(new DiscordApplicationCommandOption( - option.Name, option.Description, option.Type, - options: minimalSubTargetOptions, - nameLocalizations: localizationEnabled ? option.NameLocalizations : null, - descriptionLocalizations: localizationEnabled ? option.DescriptionLocalizations : null - )); + minimalSubTargetOptions.Add(new DiscordApplicationCommandOption( + subOption.Name, subOption.Description, subOption.Type, + options: minimalSubSubTargetOptions, + nameLocalizations: localizationEnabled ? subOption.NameLocalizations : null, + descriptionLocalizations: localizationEnabled ? subOption.DescriptionLocalizations : null + )); + } } - return rootCheck && JsonConvert.SerializeObject(minimalSourceOptions) == JsonConvert.SerializeObject(minimalTargetOptions); + minimalTargetOptions.Add(new DiscordApplicationCommandOption( + option.Name, option.Description, option.Type, + options: minimalSubTargetOptions, + nameLocalizations: localizationEnabled ? option.NameLocalizations : null, + descriptionLocalizations: localizationEnabled ? option.DescriptionLocalizations : null + )); } - else if (source.Options.Any(o => o.Type == ApplicationCommandOptionType.SubCommand) && target.Options.Any(o => o.Type == ApplicationCommandOptionType.SubCommand)) + + return rootCheck && JsonConvert.SerializeObject(minimalSourceOptions) == JsonConvert.SerializeObject(minimalTargetOptions); + } + else if (source.Options.Any(o => o.Type == ApplicationCommandOptionType.SubCommand) && target.Options.Any(o => o.Type == ApplicationCommandOptionType.SubCommand)) + { + List minimalSourceOptions = new(); + List minimalTargetOptions = new(); + + foreach (var option in source.Options) { - List minimalSourceOptions = new(); - List minimalTargetOptions = new(); + List minimalSubSourceOptions =null; - foreach (var option in source.Options) + if (option.Options != null) { - List minimalSubSourceOptions =null; + minimalSubSourceOptions = new(); - if (option.Options != null) + foreach (var subOption in option.Options) { - minimalSubSourceOptions = new(); - - foreach (var subOption in option.Options) - { - minimalSubSourceOptions.Add(new DiscordApplicationCommandOption( - subOption.Name, subOption.Description, subOption.Type, subOption.Required ?? false, - subOption.Choices, null, subOption.ChannelTypes, subOption.AutoComplete ?? false, - subOption.MinimumValue, subOption.MaximumValue, - localizationEnabled ? subOption.NameLocalizations : null, - localizationEnabled ? subOption.DescriptionLocalizations : null - )); - } + minimalSubSourceOptions.Add(new DiscordApplicationCommandOption( + subOption.Name, subOption.Description, subOption.Type, subOption.Required ?? false, + subOption.Choices, null, subOption.ChannelTypes, subOption.AutoComplete ?? false, + subOption.MinimumValue, subOption.MaximumValue, + localizationEnabled ? subOption.NameLocalizations : null, + localizationEnabled ? subOption.DescriptionLocalizations : null + )); } - - minimalSourceOptions.Add(new DiscordApplicationCommandOption( - option.Name, option.Description, option.Type, - options: minimalSubSourceOptions, - nameLocalizations: localizationEnabled ? option.NameLocalizations : null, - descriptionLocalizations: localizationEnabled ? option.DescriptionLocalizations : null - )); } - foreach (var option in target.Options) + minimalSourceOptions.Add(new DiscordApplicationCommandOption( + option.Name, option.Description, option.Type, + options: minimalSubSourceOptions, + nameLocalizations: localizationEnabled ? option.NameLocalizations : null, + descriptionLocalizations: localizationEnabled ? option.DescriptionLocalizations : null + )); + } + + foreach (var option in target.Options) + { + List minimalSubTargetOptions = null; + + if (option.Options != null && option.Options.Any()) { - List minimalSubTargetOptions = null; + minimalSubTargetOptions = new(); - if (option.Options != null && option.Options.Any()) + foreach (var subOption in option.Options) { - minimalSubTargetOptions = new(); - - foreach (var subOption in option.Options) - { - minimalSubTargetOptions.Add(new DiscordApplicationCommandOption( - subOption.Name, subOption.Description, subOption.Type, subOption.Required ?? false, - subOption.Choices, null, subOption.ChannelTypes, subOption.AutoComplete ?? false, - subOption.MinimumValue, subOption.MaximumValue, - localizationEnabled ? subOption.NameLocalizations : null, - localizationEnabled ? subOption.DescriptionLocalizations : null - )); - } + minimalSubTargetOptions.Add(new DiscordApplicationCommandOption( + subOption.Name, subOption.Description, subOption.Type, subOption.Required ?? false, + subOption.Choices, null, subOption.ChannelTypes, subOption.AutoComplete ?? false, + subOption.MinimumValue, subOption.MaximumValue, + localizationEnabled ? subOption.NameLocalizations : null, + localizationEnabled ? subOption.DescriptionLocalizations : null + )); } - - minimalTargetOptions.Add(new DiscordApplicationCommandOption( - option.Name, option.Description, option.Type, - options: minimalSubTargetOptions, - nameLocalizations: localizationEnabled ? option.NameLocalizations : null, - descriptionLocalizations: localizationEnabled ? option.DescriptionLocalizations : null - )); } - return rootCheck && JsonConvert.SerializeObject(minimalSourceOptions) == JsonConvert.SerializeObject(minimalTargetOptions); - } - else - { - List minimalSourceOptions = new(); - List minimalTargetOptions = new(); - - foreach (var option in source.Options) - minimalSourceOptions.Add(new DiscordApplicationCommandOption( - option.Name, option.Description, option.Type, option.Required ?? false, - option.Choices, null, option.ChannelTypes, option.AutoComplete ?? false, option.MinimumValue, option.MaximumValue, - localizationEnabled ? option.NameLocalizations : null, - localizationEnabled ? option.DescriptionLocalizations : null - )); - - foreach (var option in target.Options) - minimalTargetOptions.Add(new DiscordApplicationCommandOption( - option.Name, option.Description, option.Type, option.Required ?? false, - option.Choices, null, option.ChannelTypes, option.AutoComplete ?? false, option.MinimumValue, option.MaximumValue, - localizationEnabled ? option.NameLocalizations : null, - localizationEnabled ? option.DescriptionLocalizations : null - )); - - return rootCheck && JsonConvert.SerializeObject(minimalSourceOptions) == JsonConvert.SerializeObject(minimalTargetOptions); + minimalTargetOptions.Add(new DiscordApplicationCommandOption( + option.Name, option.Description, option.Type, + options: minimalSubTargetOptions, + nameLocalizations: localizationEnabled ? option.NameLocalizations : null, + descriptionLocalizations: localizationEnabled ? option.DescriptionLocalizations : null + )); } + + return rootCheck && JsonConvert.SerializeObject(minimalSourceOptions) == JsonConvert.SerializeObject(minimalTargetOptions); + } + else + { + List minimalSourceOptions = new(); + List minimalTargetOptions = new(); + + foreach (var option in source.Options) + minimalSourceOptions.Add(new DiscordApplicationCommandOption( + option.Name, option.Description, option.Type, option.Required ?? false, + option.Choices, null, option.ChannelTypes, option.AutoComplete ?? false, option.MinimumValue, option.MaximumValue, + localizationEnabled ? option.NameLocalizations : null, + localizationEnabled ? option.DescriptionLocalizations : null + )); + + foreach (var option in target.Options) + minimalTargetOptions.Add(new DiscordApplicationCommandOption( + option.Name, option.Description, option.Type, option.Required ?? false, + option.Choices, null, option.ChannelTypes, option.AutoComplete ?? false, option.MinimumValue, option.MaximumValue, + localizationEnabled ? option.NameLocalizations : null, + localizationEnabled ? option.DescriptionLocalizations : null + )); + + return rootCheck && JsonConvert.SerializeObject(minimalSourceOptions) == JsonConvert.SerializeObject(minimalTargetOptions); } } } diff --git a/DisCatSharp.ApplicationCommands/Context/ApplicationCommandsTranslationContext.cs b/DisCatSharp.ApplicationCommands/Context/ApplicationCommandsTranslationContext.cs index 115e2f62c..300060de8 100644 --- a/DisCatSharp.ApplicationCommands/Context/ApplicationCommandsTranslationContext.cs +++ b/DisCatSharp.ApplicationCommands/Context/ApplicationCommandsTranslationContext.cs @@ -1,61 +1,60 @@ // 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; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// The application commands translation context. +/// +public class ApplicationCommandsTranslationContext { /// - /// The application commands translation context. + /// Gets the type. /// - public class ApplicationCommandsTranslationContext - { - /// - /// Gets the type. - /// - public Type Type { get; } - - /// - /// Gets the name. - /// - public string Name { get; } + public Type Type { get; } - /// - /// Gets the translation json. - /// - internal string Translations { get; set; } + /// + /// Gets the name. + /// + public string Name { get; } - /// - /// Initializes a new instance of the class. - /// - /// The type. - /// The name. - internal ApplicationCommandsTranslationContext(Type type, string name) - { - this.Type = type; - this.Name = name; - } + /// + /// Gets the translation json. + /// + internal string Translations { get; set; } - public void AddTranslation(string translationJson) - => this.Translations = translationJson; + /// + /// Initializes a new instance of the class. + /// + /// The type. + /// The name. + internal ApplicationCommandsTranslationContext(Type type, string name) + { + this.Type = type; + this.Name = name; } + + public void AddTranslation(string translationJson) + => this.Translations = translationJson; } diff --git a/DisCatSharp.ApplicationCommands/Context/AutocompleteContext.cs b/DisCatSharp.ApplicationCommands/Context/AutocompleteContext.cs index 8c48bb9fb..5c48aae0b 100644 --- a/DisCatSharp.ApplicationCommands/Context/AutocompleteContext.cs +++ b/DisCatSharp.ApplicationCommands/Context/AutocompleteContext.cs @@ -1,100 +1,99 @@ // 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 DisCatSharp.Entities; using Microsoft.Extensions.DependencyInjection; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// Represents a context for an autocomplete interaction. +/// +public class AutocompleteContext { /// - /// Represents a context for an autocomplete interaction. + /// The interaction created. + /// + public DiscordInteraction Interaction { get; internal set; } + + /// + /// Gets the client for this interaction. + /// + public DiscordClient Client { get; internal set; } + + /// + /// Gets the guild this interaction was executed in. + /// + public DiscordGuild Guild { get; internal set; } + + /// + /// Gets the channel this interaction was executed in. + /// + public DiscordChannel Channel { get; internal set; } + + /// + /// Gets the user which executed this interaction. + /// + public DiscordUser User { get; internal set; } + + /// + /// Gets the member which executed this interaction, or null if the command is in a DM. + /// + public DiscordMember Member + => this.User is DiscordMember member ? member : null; + + /// + /// Gets the invoking user locale. + /// + public string Locale { get; internal set; } + + /// + /// Gets the guild locale if applicable. + /// + public string GuildLocale { get; internal set; } + + /// + /// Gets the slash command module this interaction was created in. + /// + public ApplicationCommandsExtension ApplicationCommandsExtension { get; internal set; } + + /// + /// Gets the service provider. + /// This allows passing data around without resorting to static members. + /// Defaults to an empty service provider. + /// + public IServiceProvider Services { get; internal set; } = new ServiceCollection().BuildServiceProvider(true); + + /// + /// The options already provided. + /// + public IReadOnlyList Options { get; internal set; } + + /// + /// The option to autocomplete. /// - public class AutocompleteContext - { - /// - /// The interaction created. - /// - public DiscordInteraction Interaction { get; internal set; } - - /// - /// Gets the client for this interaction. - /// - public DiscordClient Client { get; internal set; } - - /// - /// Gets the guild this interaction was executed in. - /// - public DiscordGuild Guild { get; internal set; } - - /// - /// Gets the channel this interaction was executed in. - /// - public DiscordChannel Channel { get; internal set; } - - /// - /// Gets the user which executed this interaction. - /// - public DiscordUser User { get; internal set; } - - /// - /// Gets the member which executed this interaction, or null if the command is in a DM. - /// - public DiscordMember Member - => this.User is DiscordMember member ? member : null; - - /// - /// Gets the invoking user locale. - /// - public string Locale { get; internal set; } - - /// - /// Gets the guild locale if applicable. - /// - public string GuildLocale { get; internal set; } - - /// - /// Gets the slash command module this interaction was created in. - /// - public ApplicationCommandsExtension ApplicationCommandsExtension { get; internal set; } - - /// - /// Gets the service provider. - /// This allows passing data around without resorting to static members. - /// Defaults to an empty service provider. - /// - public IServiceProvider Services { get; internal set; } = new ServiceCollection().BuildServiceProvider(true); - - /// - /// The options already provided. - /// - public IReadOnlyList Options { get; internal set; } - - /// - /// The option to autocomplete. - /// - public DiscordInteractionDataOption FocusedOption { get; internal set; } - } + public DiscordInteractionDataOption FocusedOption { get; internal set; } } diff --git a/DisCatSharp.ApplicationCommands/Context/BaseContext.cs b/DisCatSharp.ApplicationCommands/Context/BaseContext.cs index a73d7f162..73bf2c39c 100644 --- a/DisCatSharp.ApplicationCommands/Context/BaseContext.cs +++ b/DisCatSharp.ApplicationCommands/Context/BaseContext.cs @@ -1,199 +1,198 @@ // 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.Threading.Tasks; using DisCatSharp.Entities; using DisCatSharp.Enums; using Microsoft.Extensions.DependencyInjection; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// Represents a base context for application command contexts. +/// +public class BaseContext { /// - /// Represents a base context for application command contexts. - /// - public class BaseContext - { - /// - /// Gets the interaction that was created. - /// - public DiscordInteraction Interaction { get; internal set; } - - /// - /// Gets the client for this interaction. - /// - public DiscordClient Client { get; internal set; } - - /// - /// Gets the guild this interaction was executed in. - /// - public DiscordGuild Guild { get; internal set; } - - /// - /// Gets the channel this interaction was executed in. - /// - public DiscordChannel Channel { get; internal set; } - - /// - /// Gets the user which executed this interaction. - /// - public DiscordUser User { get; internal set; } - - /// - /// Gets the member which executed this interaction, or null if the command is in a DM. - /// - public DiscordMember Member - => this.User is DiscordMember member ? member : null; - - /// - /// Gets the application command module this interaction was created in. - /// - public ApplicationCommandsExtension ApplicationCommandsExtension { get; internal set; } - - /// - /// Gets the token for this interaction. - /// - public string Token { get; internal set; } - - /// - /// Gets the id for this interaction. - /// - public ulong InteractionId { get; internal set; } - - /// - /// Gets the name of the command. - /// - public string CommandName { get; internal set; } - - /// - /// Gets the invoking user locale. - /// - public string Locale { get; internal set; } - - /// - /// Gets the guild locale if applicable. - /// - public string GuildLocale { get; internal set; } - - /// - /// Gets the type of this interaction. - /// - public ApplicationCommandType Type { get; internal set; } - - /// - /// Gets the service provider. - /// This allows passing data around without resorting to static members. - /// Defaults to an empty service provider. - /// - public IServiceProvider Services { get; internal set; } = new ServiceCollection().BuildServiceProvider(true); - - /// - /// Creates a response to this interaction. - /// You must create a response within 3 seconds of this interaction being executed; if the command has the potential to take more than 3 seconds, create a at the start, and edit the response later. - /// - /// The type of the response. - /// The data to be sent, if any. - /// - public Task CreateResponseAsync(InteractionResponseType type, DiscordInteractionResponseBuilder builder = null) - => this.Interaction.CreateResponseAsync(type, builder); - - /// - /// Creates a modal response to this interaction. - /// - /// The data to send. - public Task CreateModalResponseAsync(DiscordInteractionModalBuilder builder) - => this.Interaction.Type != InteractionType.Ping && this.Interaction.Type != InteractionType.ModalSubmit ? this.Interaction.CreateInteractionModalResponseAsync(builder) : throw new NotSupportedException("You can't respond to an PING with a modal."); - - /// - /// Edits the interaction response. - /// - /// The data to edit the response with. - /// - public Task EditResponseAsync(DiscordWebhookBuilder builder) - => this.Interaction.EditOriginalResponseAsync(builder); - - /// - /// Deletes the interaction response. - /// - /// - public Task DeleteResponseAsync() - => this.Interaction.DeleteOriginalResponseAsync(); - - /// - /// Creates a follow up message to the interaction. - /// - /// The message to be sent, in the form of a webhook. - /// The created message. - public Task FollowUpAsync(DiscordFollowupMessageBuilder builder) - => this.Interaction.CreateFollowupMessageAsync(builder); - - /// - /// Creates a follow up message to the interaction. - /// - /// The content of the message to be sent. - /// The created message. - public Task FollowUpAsync(string content) - => this.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(content)); - - /// - /// Edits a followup message. - /// - /// The id of the followup message to edit. - /// The webhook builder. - /// The created message. - public Task EditFollowupAsync(ulong followupMessageId, DiscordWebhookBuilder builder) - => this.Interaction.EditFollowupMessageAsync(followupMessageId, builder); - - /// - /// Edits a followup message. - /// - /// The id of the followup message to edit. - /// The content of the webhook. - /// The created message. - public Task EditFollowupAsync(ulong followupMessageId, string content) - => this.EditFollowupAsync(followupMessageId, new DiscordWebhookBuilder().WithContent(content)); - - /// - /// Deletes a followup message. - /// - /// The id of the followup message to delete. - /// - public Task DeleteFollowupAsync(ulong followupMessageId) - => this.Interaction.DeleteFollowupMessageAsync(followupMessageId); - - /// - /// Gets the followup message. - /// - /// The followup message id. - public Task GetFollowupMessageAsync(ulong followupMessageId) - => this.Interaction.GetFollowupMessageAsync(followupMessageId); - - /// - /// Gets the original interaction response. - /// - /// The original interaction response. - public Task GetOriginalResponseAsync() - => this.Interaction.GetOriginalResponseAsync(); - } + /// Gets the interaction that was created. + /// + public DiscordInteraction Interaction { get; internal set; } + + /// + /// Gets the client for this interaction. + /// + public DiscordClient Client { get; internal set; } + + /// + /// Gets the guild this interaction was executed in. + /// + public DiscordGuild Guild { get; internal set; } + + /// + /// Gets the channel this interaction was executed in. + /// + public DiscordChannel Channel { get; internal set; } + + /// + /// Gets the user which executed this interaction. + /// + public DiscordUser User { get; internal set; } + + /// + /// Gets the member which executed this interaction, or null if the command is in a DM. + /// + public DiscordMember Member + => this.User is DiscordMember member ? member : null; + + /// + /// Gets the application command module this interaction was created in. + /// + public ApplicationCommandsExtension ApplicationCommandsExtension { get; internal set; } + + /// + /// Gets the token for this interaction. + /// + public string Token { get; internal set; } + + /// + /// Gets the id for this interaction. + /// + public ulong InteractionId { get; internal set; } + + /// + /// Gets the name of the command. + /// + public string CommandName { get; internal set; } + + /// + /// Gets the invoking user locale. + /// + public string Locale { get; internal set; } + + /// + /// Gets the guild locale if applicable. + /// + public string GuildLocale { get; internal set; } + + /// + /// Gets the type of this interaction. + /// + public ApplicationCommandType Type { get; internal set; } + + /// + /// Gets the service provider. + /// This allows passing data around without resorting to static members. + /// Defaults to an empty service provider. + /// + public IServiceProvider Services { get; internal set; } = new ServiceCollection().BuildServiceProvider(true); + + /// + /// Creates a response to this interaction. + /// You must create a response within 3 seconds of this interaction being executed; if the command has the potential to take more than 3 seconds, create a at the start, and edit the response later. + /// + /// The type of the response. + /// The data to be sent, if any. + /// + public Task CreateResponseAsync(InteractionResponseType type, DiscordInteractionResponseBuilder builder = null) + => this.Interaction.CreateResponseAsync(type, builder); + + /// + /// Creates a modal response to this interaction. + /// + /// The data to send. + public Task CreateModalResponseAsync(DiscordInteractionModalBuilder builder) + => this.Interaction.Type != InteractionType.Ping && this.Interaction.Type != InteractionType.ModalSubmit ? this.Interaction.CreateInteractionModalResponseAsync(builder) : throw new NotSupportedException("You can't respond to an PING with a modal."); + + /// + /// Edits the interaction response. + /// + /// The data to edit the response with. + /// + public Task EditResponseAsync(DiscordWebhookBuilder builder) + => this.Interaction.EditOriginalResponseAsync(builder); + + /// + /// Deletes the interaction response. + /// + /// + public Task DeleteResponseAsync() + => this.Interaction.DeleteOriginalResponseAsync(); + + /// + /// Creates a follow up message to the interaction. + /// + /// The message to be sent, in the form of a webhook. + /// The created message. + public Task FollowUpAsync(DiscordFollowupMessageBuilder builder) + => this.Interaction.CreateFollowupMessageAsync(builder); + + /// + /// Creates a follow up message to the interaction. + /// + /// The content of the message to be sent. + /// The created message. + public Task FollowUpAsync(string content) + => this.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(content)); + + /// + /// Edits a followup message. + /// + /// The id of the followup message to edit. + /// The webhook builder. + /// The created message. + public Task EditFollowupAsync(ulong followupMessageId, DiscordWebhookBuilder builder) + => this.Interaction.EditFollowupMessageAsync(followupMessageId, builder); + + /// + /// Edits a followup message. + /// + /// The id of the followup message to edit. + /// The content of the webhook. + /// The created message. + public Task EditFollowupAsync(ulong followupMessageId, string content) + => this.EditFollowupAsync(followupMessageId, new DiscordWebhookBuilder().WithContent(content)); + + /// + /// Deletes a followup message. + /// + /// The id of the followup message to delete. + /// + public Task DeleteFollowupAsync(ulong followupMessageId) + => this.Interaction.DeleteFollowupMessageAsync(followupMessageId); + + /// + /// Gets the followup message. + /// + /// The followup message id. + public Task GetFollowupMessageAsync(ulong followupMessageId) + => this.Interaction.GetFollowupMessageAsync(followupMessageId); + + /// + /// Gets the original interaction response. + /// + /// The original interaction response. + public Task GetOriginalResponseAsync() + => this.Interaction.GetOriginalResponseAsync(); } diff --git a/DisCatSharp.ApplicationCommands/Context/ContextMenuContext.cs b/DisCatSharp.ApplicationCommands/Context/ContextMenuContext.cs index 2494f8175..41d73ad4c 100644 --- a/DisCatSharp.ApplicationCommands/Context/ContextMenuContext.cs +++ b/DisCatSharp.ApplicationCommands/Context/ContextMenuContext.cs @@ -1,48 +1,47 @@ // 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 DisCatSharp.Entities; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// Represents a context for a context menu. +/// +public sealed class ContextMenuContext : BaseContext { /// - /// Represents a context for a context menu. + /// The user this command targets, if applicable. /// - public sealed class ContextMenuContext : BaseContext - { - /// - /// The user this command targets, if applicable. - /// - public DiscordUser TargetUser { get; internal set; } + public DiscordUser TargetUser { get; internal set; } - /// - /// The member this command targets, if applicable. - /// - public DiscordMember TargetMember - => this.TargetUser is DiscordMember member ? member : null; + /// + /// The member this command targets, if applicable. + /// + public DiscordMember TargetMember + => this.TargetUser is DiscordMember member ? member : null; - /// - /// The message this command targets, if applicable. - /// - public DiscordMessage TargetMessage { get; internal set; } - } + /// + /// The message this command targets, if applicable. + /// + public DiscordMessage TargetMessage { get; internal set; } } diff --git a/DisCatSharp.ApplicationCommands/Context/InteractionContext.cs b/DisCatSharp.ApplicationCommands/Context/InteractionContext.cs index 0634b07f3..df19b3e85 100644 --- a/DisCatSharp.ApplicationCommands/Context/InteractionContext.cs +++ b/DisCatSharp.ApplicationCommands/Context/InteractionContext.cs @@ -1,54 +1,53 @@ // 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 DisCatSharp.Entities; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// Represents a context for an interaction. +/// +public sealed class InteractionContext : BaseContext { /// - /// Represents a context for an interaction. + /// Gets the users mentioned in the command parameters. /// - public sealed class InteractionContext : BaseContext - { - /// - /// Gets the users mentioned in the command parameters. - /// - public IReadOnlyList ResolvedUserMentions { get; internal set; } + public IReadOnlyList ResolvedUserMentions { get; internal set; } - /// - /// Gets the roles mentioned in the command parameters. - /// - public IReadOnlyList ResolvedRoleMentions { get; internal set; } + /// + /// Gets the roles mentioned in the command parameters. + /// + public IReadOnlyList ResolvedRoleMentions { get; internal set; } - /// - /// Gets the channels mentioned in the command parameters. - /// - public IReadOnlyList ResolvedChannelMentions { get; internal set; } + /// + /// Gets the channels mentioned in the command parameters. + /// + public IReadOnlyList ResolvedChannelMentions { get; internal set; } - /// - /// Gets the attachments in the command parameters, if applicable. - /// - public IReadOnlyList ResolvedAttachments { get; internal set; } - } + /// + /// Gets the attachments in the command parameters, if applicable. + /// + public IReadOnlyList ResolvedAttachments { get; internal set; } } diff --git a/DisCatSharp.ApplicationCommands/Entities/ChoiceTranslator.cs b/DisCatSharp.ApplicationCommands/Entities/ChoiceTranslator.cs index eb0bb777c..6f9ab002c 100644 --- a/DisCatSharp.ApplicationCommands/Entities/ChoiceTranslator.cs +++ b/DisCatSharp.ApplicationCommands/Entities/ChoiceTranslator.cs @@ -1,51 +1,50 @@ // 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 DisCatSharp.Entities; using Newtonsoft.Json; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// Represents a choice translator. +/// +internal class ChoiceTranslator { /// - /// Represents a choice translator. + /// Gets the choice name. /// - internal class ChoiceTranslator - { - /// - /// Gets the choice name. - /// - [JsonProperty("name")] - public string Name { get; set; } + [JsonProperty("name")] + public string Name { get; set; } - /// - /// Gets the choice name translations. - /// - [JsonProperty("name_translations")] - internal Dictionary NameTranslationsDictionary { get; set; } - [JsonIgnore] - public DiscordApplicationCommandLocalization NameTranslations - => new(this.NameTranslationsDictionary); - } + /// + /// Gets the choice name translations. + /// + [JsonProperty("name_translations")] + internal Dictionary NameTranslationsDictionary { get; set; } + [JsonIgnore] + public DiscordApplicationCommandLocalization NameTranslations + => new(this.NameTranslationsDictionary); } diff --git a/DisCatSharp.ApplicationCommands/Entities/CommandTranslator.cs b/DisCatSharp.ApplicationCommands/Entities/CommandTranslator.cs index 4bd5672e9..df5572718 100644 --- a/DisCatSharp.ApplicationCommands/Entities/CommandTranslator.cs +++ b/DisCatSharp.ApplicationCommands/Entities/CommandTranslator.cs @@ -1,74 +1,73 @@ // 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 DisCatSharp.Entities; using DisCatSharp.Enums; using Newtonsoft.Json; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// Represents a command translator. +/// +internal class CommandTranslator { /// - /// Represents a command translator. + /// Gets the command name. /// - internal class CommandTranslator - { - /// - /// Gets the command name. - /// - [JsonProperty("name")] - public string Name { get; set; } + [JsonProperty("name")] + public string Name { get; set; } - /// - /// Gets the application command type. - /// Used to determine whether it is an translator for context menu or not. - /// - [JsonProperty("type")] - public ApplicationCommandType Type { get; set; } + /// + /// Gets the application command type. + /// Used to determine whether it is an translator for context menu or not. + /// + [JsonProperty("type")] + public ApplicationCommandType Type { get; set; } - /// - /// Gets the command name translations. - /// - [JsonProperty("name_translations")] - internal Dictionary NameTranslationDictionary { get; set; } - [JsonIgnore] - public DiscordApplicationCommandLocalization NameTranslations - => new(this.NameTranslationDictionary); + /// + /// Gets the command name translations. + /// + [JsonProperty("name_translations")] + internal Dictionary NameTranslationDictionary { get; set; } + [JsonIgnore] + public DiscordApplicationCommandLocalization NameTranslations + => new(this.NameTranslationDictionary); - /// - /// Gets the command description translations. - /// - [JsonProperty("description_translations")] - internal Dictionary DescriptionTranslationDictionary { get; set; } - [JsonIgnore] - public DiscordApplicationCommandLocalization DescriptionTranslations - => new(this.DescriptionTranslationDictionary); + /// + /// Gets the command description translations. + /// + [JsonProperty("description_translations")] + internal Dictionary DescriptionTranslationDictionary { get; set; } + [JsonIgnore] + public DiscordApplicationCommandLocalization DescriptionTranslations + => new(this.DescriptionTranslationDictionary); - /// - /// Gets the option translators, if applicable. - /// - [JsonProperty("options")] - public List Options { get; set; } - } + /// + /// Gets the option translators, if applicable. + /// + [JsonProperty("options")] + public List Options { get; set; } } diff --git a/DisCatSharp.ApplicationCommands/Entities/GroupTranslator.cs b/DisCatSharp.ApplicationCommands/Entities/GroupTranslator.cs index f50268c9d..8e4d04b3e 100644 --- a/DisCatSharp.ApplicationCommands/Entities/GroupTranslator.cs +++ b/DisCatSharp.ApplicationCommands/Entities/GroupTranslator.cs @@ -1,72 +1,71 @@ // 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 DisCatSharp.Entities; using Newtonsoft.Json; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// Represents a group translator. +/// +internal class GroupTranslator { /// - /// Represents a group translator. + /// Gets the group name. /// - internal class GroupTranslator - { - /// - /// Gets the group name. - /// - [JsonProperty("name")] - public string Name { get; set; } + [JsonProperty("name")] + public string Name { get; set; } - /// - /// Gets the group name translations. - /// - [JsonProperty("name_translations")] - internal Dictionary NameTranslationsDictionary { get; set; } - [JsonIgnore] - public DiscordApplicationCommandLocalization NameTranslations - => new(this.NameTranslationsDictionary); + /// + /// Gets the group name translations. + /// + [JsonProperty("name_translations")] + internal Dictionary NameTranslationsDictionary { get; set; } + [JsonIgnore] + public DiscordApplicationCommandLocalization NameTranslations + => new(this.NameTranslationsDictionary); - /// - /// Gets the group description translations. - /// - [JsonProperty("description_translations")] - internal Dictionary DescriptionTranslationsDictionary { get; set; } - [JsonIgnore] - public DiscordApplicationCommandLocalization DescriptionTranslations - => new(this.DescriptionTranslationsDictionary); + /// + /// Gets the group description translations. + /// + [JsonProperty("description_translations")] + internal Dictionary DescriptionTranslationsDictionary { get; set; } + [JsonIgnore] + public DiscordApplicationCommandLocalization DescriptionTranslations + => new(this.DescriptionTranslationsDictionary); - /// - /// Gets the sub group translators, if applicable. - /// - [JsonProperty("groups")] - public List SubGroups { get; set; } + /// + /// Gets the sub group translators, if applicable. + /// + [JsonProperty("groups")] + public List SubGroups { get; set; } - /// - /// Gets the command translators, if applicable. - /// - [JsonProperty("commands")] - public List Commands { get; set; } - } + /// + /// Gets the command translators, if applicable. + /// + [JsonProperty("commands")] + public List Commands { get; set; } } diff --git a/DisCatSharp.ApplicationCommands/Entities/OptionTranslator.cs b/DisCatSharp.ApplicationCommands/Entities/OptionTranslator.cs index cffe3e0ac..be8525313 100644 --- a/DisCatSharp.ApplicationCommands/Entities/OptionTranslator.cs +++ b/DisCatSharp.ApplicationCommands/Entities/OptionTranslator.cs @@ -1,66 +1,65 @@ // 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 DisCatSharp.Entities; using Newtonsoft.Json; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// Represents a option translator. +/// +internal class OptionTranslator { /// - /// Represents a option translator. + /// Gets the option name. /// - internal class OptionTranslator - { - /// - /// Gets the option name. - /// - [JsonProperty("name")] - public string Name { get; set; } + [JsonProperty("name")] + public string Name { get; set; } - /// - /// Gets the option name translations. - /// - [JsonProperty("name_translations")] - internal Dictionary NameTranslationsDictionary { get; set; } - [JsonIgnore] - public DiscordApplicationCommandLocalization NameTranslations - => new(this.NameTranslationsDictionary); + /// + /// Gets the option name translations. + /// + [JsonProperty("name_translations")] + internal Dictionary NameTranslationsDictionary { get; set; } + [JsonIgnore] + public DiscordApplicationCommandLocalization NameTranslations + => new(this.NameTranslationsDictionary); - /// - /// Gets the option description translations. - /// - [JsonProperty("description_translations")] - internal Dictionary DescriptionTranslationsDictionary { get; set; } - [JsonIgnore] - public DiscordApplicationCommandLocalization DescriptionTranslations - => new(this.DescriptionTranslationsDictionary); + /// + /// Gets the option description translations. + /// + [JsonProperty("description_translations")] + internal Dictionary DescriptionTranslationsDictionary { get; set; } + [JsonIgnore] + public DiscordApplicationCommandLocalization DescriptionTranslations + => new(this.DescriptionTranslationsDictionary); - /// - /// Gets the choice translators, if applicable. - /// - [JsonProperty("choices")] - public List Choices { get; set; } - } + /// + /// Gets the choice translators, if applicable. + /// + [JsonProperty("choices")] + public List Choices { get; set; } } diff --git a/DisCatSharp.ApplicationCommands/Entities/SubGroupTranslator.cs b/DisCatSharp.ApplicationCommands/Entities/SubGroupTranslator.cs index 5af69fdc3..5639c4c04 100644 --- a/DisCatSharp.ApplicationCommands/Entities/SubGroupTranslator.cs +++ b/DisCatSharp.ApplicationCommands/Entities/SubGroupTranslator.cs @@ -1,66 +1,65 @@ // 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 DisCatSharp.Entities; using Newtonsoft.Json; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// Represents a sub group translator. +/// +internal class SubGroupTranslator { /// - /// Represents a sub group translator. + /// Gets the sub group name. /// - internal class SubGroupTranslator - { - /// - /// Gets the sub group name. - /// - [JsonProperty("name")] - public string Name { get; set; } + [JsonProperty("name")] + public string Name { get; set; } - /// - /// Gets the sub group name translations. - /// - [JsonProperty("name_translations")] - internal Dictionary NameTranslationsDictionary { get; set; } - [JsonIgnore] - public DiscordApplicationCommandLocalization NameTranslations - => new(this.NameTranslationsDictionary); + /// + /// Gets the sub group name translations. + /// + [JsonProperty("name_translations")] + internal Dictionary NameTranslationsDictionary { get; set; } + [JsonIgnore] + public DiscordApplicationCommandLocalization NameTranslations + => new(this.NameTranslationsDictionary); - /// - /// Gets the sub group description translations. - /// - [JsonProperty("description_translations")] - internal Dictionary DescriptionTranslationsDictionary { get; set; } - [JsonIgnore] - public DiscordApplicationCommandLocalization DescriptionTranslations - => new(this.DescriptionTranslationsDictionary); + /// + /// Gets the sub group description translations. + /// + [JsonProperty("description_translations")] + internal Dictionary DescriptionTranslationsDictionary { get; set; } + [JsonIgnore] + public DiscordApplicationCommandLocalization DescriptionTranslations + => new(this.DescriptionTranslationsDictionary); - /// - /// Gets the command translators. - /// - [JsonProperty("commands")] - public List Commands { get; set; } - } + /// + /// Gets the command translators. + /// + [JsonProperty("commands")] + public List Commands { get; set; } } diff --git a/DisCatSharp.ApplicationCommands/EventArgs/ContextMenu/ContextMenuErrorEventArgs.cs b/DisCatSharp.ApplicationCommands/EventArgs/ContextMenu/ContextMenuErrorEventArgs.cs index 6aeb86e2e..9e2a35929 100644 --- a/DisCatSharp.ApplicationCommands/EventArgs/ContextMenu/ContextMenuErrorEventArgs.cs +++ b/DisCatSharp.ApplicationCommands/EventArgs/ContextMenu/ContextMenuErrorEventArgs.cs @@ -1,52 +1,50 @@ // 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 DisCatSharp.EventArgs; -namespace DisCatSharp.ApplicationCommands.EventArgs -{ +namespace DisCatSharp.ApplicationCommands.EventArgs; +/// +/// Represents arguments for a +/// +public class ContextMenuErrorEventArgs : DiscordEventArgs +{ /// - /// Represents arguments for a + /// The context of the command. /// - public class ContextMenuErrorEventArgs : DiscordEventArgs - { - /// - /// The context of the command. - /// - public ContextMenuContext Context { get; internal set; } + public ContextMenuContext Context { get; internal set; } - /// - /// The exception thrown. - /// - public Exception Exception { get; internal set; } + /// + /// The exception thrown. + /// + public Exception Exception { get; internal set; } - /// - /// Initializes a new instance of the class. - /// - /// The provider. - public ContextMenuErrorEventArgs(IServiceProvider provider) : base(provider) - { } - } + /// + /// Initializes a new instance of the class. + /// + /// The provider. + public ContextMenuErrorEventArgs(IServiceProvider provider) : base(provider) + { } } diff --git a/DisCatSharp.ApplicationCommands/EventArgs/ContextMenu/ContextMenuExecutedEventArgs.cs b/DisCatSharp.ApplicationCommands/EventArgs/ContextMenu/ContextMenuExecutedEventArgs.cs index d6cc94976..763cdd115 100644 --- a/DisCatSharp.ApplicationCommands/EventArgs/ContextMenu/ContextMenuExecutedEventArgs.cs +++ b/DisCatSharp.ApplicationCommands/EventArgs/ContextMenu/ContextMenuExecutedEventArgs.cs @@ -1,46 +1,45 @@ // 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 DisCatSharp.EventArgs; -namespace DisCatSharp.ApplicationCommands.EventArgs +namespace DisCatSharp.ApplicationCommands.EventArgs; + +/// +/// Represents the arguments for a event +/// +public sealed class ContextMenuExecutedEventArgs : DiscordEventArgs { /// - /// Represents the arguments for a event + /// The context of the command. /// - public sealed class ContextMenuExecutedEventArgs : DiscordEventArgs - { - /// - /// The context of the command. - /// - public ContextMenuContext Context { get; internal set; } + public ContextMenuContext Context { get; internal set; } - /// - /// Initializes a new instance of the class. - /// - /// The provider. - public ContextMenuExecutedEventArgs(IServiceProvider provider) : base(provider) - { } - } + /// + /// Initializes a new instance of the class. + /// + /// The provider. + public ContextMenuExecutedEventArgs(IServiceProvider provider) : base(provider) + { } } diff --git a/DisCatSharp.ApplicationCommands/EventArgs/Module/ApplicationCommandsModuleReadyEventArgs.cs b/DisCatSharp.ApplicationCommands/EventArgs/Module/ApplicationCommandsModuleReadyEventArgs.cs index 345687a72..5825aaf22 100644 --- a/DisCatSharp.ApplicationCommands/EventArgs/Module/ApplicationCommandsModuleReadyEventArgs.cs +++ b/DisCatSharp.ApplicationCommands/EventArgs/Module/ApplicationCommandsModuleReadyEventArgs.cs @@ -1,47 +1,46 @@ // 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 DisCatSharp.EventArgs; -namespace DisCatSharp.ApplicationCommands.EventArgs +namespace DisCatSharp.ApplicationCommands.EventArgs; + +/// +/// Represents arguments for a event. +/// +public class ApplicationCommandsModuleReadyEventArgs : DiscordEventArgs { /// - /// Represents arguments for a event. + /// Gets a list of all guild ids missing the application commands scope. /// - public class ApplicationCommandsModuleReadyEventArgs : DiscordEventArgs - { - /// - /// Gets a list of all guild ids missing the application commands scope. - /// - public IReadOnlyList GuildsWithoutScope { get; internal set; } + public IReadOnlyList GuildsWithoutScope { get; internal set; } - /// - /// Initializes a new instance of the class. - /// - /// The provider. - internal ApplicationCommandsModuleReadyEventArgs(IServiceProvider provider) : base(provider) - { } - } + /// + /// Initializes a new instance of the class. + /// + /// The provider. + internal ApplicationCommandsModuleReadyEventArgs(IServiceProvider provider) : base(provider) + { } } diff --git a/DisCatSharp.ApplicationCommands/EventArgs/Module/ApplicationCommandsModuleStartupFinishedEventArgs.cs b/DisCatSharp.ApplicationCommands/EventArgs/Module/ApplicationCommandsModuleStartupFinishedEventArgs.cs index 435a5134e..9b6ceb90a 100644 --- a/DisCatSharp.ApplicationCommands/EventArgs/Module/ApplicationCommandsModuleStartupFinishedEventArgs.cs +++ b/DisCatSharp.ApplicationCommands/EventArgs/Module/ApplicationCommandsModuleStartupFinishedEventArgs.cs @@ -1,58 +1,57 @@ // 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 DisCatSharp.Entities; using DisCatSharp.EventArgs; -namespace DisCatSharp.ApplicationCommands.EventArgs +namespace DisCatSharp.ApplicationCommands.EventArgs; + +/// +/// Represents arguments for a event. +/// +public class ApplicationCommandsModuleStartupFinishedEventArgs : DiscordEventArgs { /// - /// Represents arguments for a event. + /// Gets a list of all guild ids missing the application commands scope. /// - public class ApplicationCommandsModuleStartupFinishedEventArgs : DiscordEventArgs - { - /// - /// Gets a list of all guild ids missing the application commands scope. - /// - public IReadOnlyList GuildsWithoutScope { get; internal set; } + public IReadOnlyList GuildsWithoutScope { get; internal set; } - /// - /// Gets all registered global commands. - /// - public IReadOnlyList RegisteredGlobalCommands { get; internal set; } + /// + /// Gets all registered global commands. + /// + public IReadOnlyList RegisteredGlobalCommands { get; internal set; } - /// - /// Gets all registered guild commands mapped by guild id. - /// - public IReadOnlyDictionary> RegisteredGuildCommands { get; internal set; } + /// + /// Gets all registered guild commands mapped by guild id. + /// + public IReadOnlyDictionary> RegisteredGuildCommands { get; internal set; } - /// - /// Initializes a new instance of the class. - /// - /// The provider. - internal ApplicationCommandsModuleStartupFinishedEventArgs(IServiceProvider provider) : base(provider) - { } - } + /// + /// Initializes a new instance of the class. + /// + /// The provider. + internal ApplicationCommandsModuleStartupFinishedEventArgs(IServiceProvider provider) : base(provider) + { } } diff --git a/DisCatSharp.ApplicationCommands/EventArgs/Module/GlobalApplicationCommandsRegisteredEventArgs.cs b/DisCatSharp.ApplicationCommands/EventArgs/Module/GlobalApplicationCommandsRegisteredEventArgs.cs index d3bd7e0bc..391051bae 100644 --- a/DisCatSharp.ApplicationCommands/EventArgs/Module/GlobalApplicationCommandsRegisteredEventArgs.cs +++ b/DisCatSharp.ApplicationCommands/EventArgs/Module/GlobalApplicationCommandsRegisteredEventArgs.cs @@ -1,48 +1,47 @@ // 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 DisCatSharp.Entities; using DisCatSharp.EventArgs; -namespace DisCatSharp.ApplicationCommands.EventArgs +namespace DisCatSharp.ApplicationCommands.EventArgs; + +/// +/// Represents arguments for a event. +/// +public class GlobalApplicationCommandsRegisteredEventArgs : DiscordEventArgs { /// - /// Represents arguments for a event. + /// Gets all registered global commands. /// - public class GlobalApplicationCommandsRegisteredEventArgs : DiscordEventArgs - { - /// - /// Gets all registered global commands. - /// - public IReadOnlyList RegisteredCommands { get; internal set; } + public IReadOnlyList RegisteredCommands { get; internal set; } - /// - /// Initializes a new instance of the class. - /// - /// The provider. - internal GlobalApplicationCommandsRegisteredEventArgs(IServiceProvider provider) : base(provider) - { } - } + /// + /// Initializes a new instance of the class. + /// + /// The provider. + internal GlobalApplicationCommandsRegisteredEventArgs(IServiceProvider provider) : base(provider) + { } } diff --git a/DisCatSharp.ApplicationCommands/EventArgs/Module/GuildApplicationCommandsRegisteredEventArgs.cs b/DisCatSharp.ApplicationCommands/EventArgs/Module/GuildApplicationCommandsRegisteredEventArgs.cs index cd078da81..7eee4ec19 100644 --- a/DisCatSharp.ApplicationCommands/EventArgs/Module/GuildApplicationCommandsRegisteredEventArgs.cs +++ b/DisCatSharp.ApplicationCommands/EventArgs/Module/GuildApplicationCommandsRegisteredEventArgs.cs @@ -1,53 +1,52 @@ // 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 DisCatSharp.Entities; using DisCatSharp.EventArgs; -namespace DisCatSharp.ApplicationCommands.EventArgs +namespace DisCatSharp.ApplicationCommands.EventArgs; + +/// +/// Represents arguments for a event. +/// +public class GuildApplicationCommandsRegisteredEventArgs : DiscordEventArgs { /// - /// Represents arguments for a event. + /// Gets the target guild id. /// - public class GuildApplicationCommandsRegisteredEventArgs : DiscordEventArgs - { - /// - /// Gets the target guild id. - /// - public ulong GuildId { get; internal set; } + public ulong GuildId { get; internal set; } - /// - /// Gets all registered guild commands. - /// - public IReadOnlyList RegisteredCommands { get; internal set; } + /// + /// Gets all registered guild commands. + /// + public IReadOnlyList RegisteredCommands { get; internal set; } - /// - /// Initializes a new instance of the class. - /// - /// The provider. - internal GuildApplicationCommandsRegisteredEventArgs(IServiceProvider provider) : base(provider) - { } - } + /// + /// Initializes a new instance of the class. + /// + /// The provider. + internal GuildApplicationCommandsRegisteredEventArgs(IServiceProvider provider) : base(provider) + { } } diff --git a/DisCatSharp.ApplicationCommands/EventArgs/SlashCommand/SlashCommandErrorEventArgs.cs b/DisCatSharp.ApplicationCommands/EventArgs/SlashCommand/SlashCommandErrorEventArgs.cs index 0be5f0543..cbb544bcc 100644 --- a/DisCatSharp.ApplicationCommands/EventArgs/SlashCommand/SlashCommandErrorEventArgs.cs +++ b/DisCatSharp.ApplicationCommands/EventArgs/SlashCommand/SlashCommandErrorEventArgs.cs @@ -1,51 +1,50 @@ // 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 DisCatSharp.EventArgs; -namespace DisCatSharp.ApplicationCommands.EventArgs +namespace DisCatSharp.ApplicationCommands.EventArgs; + +/// +/// Represents arguments for a event +/// +public class SlashCommandErrorEventArgs : DiscordEventArgs { /// - /// Represents arguments for a event + /// The context of the command. /// - public class SlashCommandErrorEventArgs : DiscordEventArgs - { - /// - /// The context of the command. - /// - public InteractionContext Context { get; internal set; } + public InteractionContext Context { get; internal set; } - /// - /// The exception thrown. - /// - public Exception Exception { get; internal set; } + /// + /// The exception thrown. + /// + public Exception Exception { get; internal set; } - /// - /// Initializes a new instance of the class. - /// - /// The provider. - public SlashCommandErrorEventArgs(IServiceProvider provider) : base(provider) - { } - } + /// + /// Initializes a new instance of the class. + /// + /// The provider. + public SlashCommandErrorEventArgs(IServiceProvider provider) : base(provider) + { } } diff --git a/DisCatSharp.ApplicationCommands/EventArgs/SlashCommand/SlashCommandExecutedEventArgs.cs b/DisCatSharp.ApplicationCommands/EventArgs/SlashCommand/SlashCommandExecutedEventArgs.cs index 2ffeb91e1..6286a78d5 100644 --- a/DisCatSharp.ApplicationCommands/EventArgs/SlashCommand/SlashCommandExecutedEventArgs.cs +++ b/DisCatSharp.ApplicationCommands/EventArgs/SlashCommand/SlashCommandExecutedEventArgs.cs @@ -1,46 +1,45 @@ // 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 DisCatSharp.EventArgs; -namespace DisCatSharp.ApplicationCommands.EventArgs +namespace DisCatSharp.ApplicationCommands.EventArgs; + +/// +/// Represents the arguments for a event +/// +public class SlashCommandExecutedEventArgs : DiscordEventArgs { /// - /// Represents the arguments for a event + /// The context of the command. /// - public class SlashCommandExecutedEventArgs : DiscordEventArgs - { - /// - /// The context of the command. - /// - public InteractionContext Context { get; internal set; } + public InteractionContext Context { get; internal set; } - /// - /// Initializes a new instance of the class. - /// - /// The provider. - public SlashCommandExecutedEventArgs(IServiceProvider provider) : base(provider) - { } - } + /// + /// Initializes a new instance of the class. + /// + /// The provider. + public SlashCommandExecutedEventArgs(IServiceProvider provider) : base(provider) + { } } diff --git a/DisCatSharp.ApplicationCommands/Exceptions/ContextMenu/ContextMenuExecutionChecksFailedException.cs b/DisCatSharp.ApplicationCommands/Exceptions/ContextMenu/ContextMenuExecutionChecksFailedException.cs index 3cb9017d6..06e5d2307 100644 --- a/DisCatSharp.ApplicationCommands/Exceptions/ContextMenu/ContextMenuExecutionChecksFailedException.cs +++ b/DisCatSharp.ApplicationCommands/Exceptions/ContextMenu/ContextMenuExecutionChecksFailedException.cs @@ -1,38 +1,37 @@ // 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; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// Thrown when a pre-execution check for a context menu command fails. +/// +public sealed class ContextMenuExecutionChecksFailedException : Exception { /// - /// Thrown when a pre-execution check for a context menu command fails. + /// The list of failed checks. /// - public sealed class ContextMenuExecutionChecksFailedException : Exception - { - /// - /// The list of failed checks. - /// - public IReadOnlyList FailedChecks; - } + public IReadOnlyList FailedChecks; } diff --git a/DisCatSharp.ApplicationCommands/Exceptions/SlashCommand/SlashExecutionChecksFailedException.cs b/DisCatSharp.ApplicationCommands/Exceptions/SlashCommand/SlashExecutionChecksFailedException.cs index bb2790554..45f6f4b06 100644 --- a/DisCatSharp.ApplicationCommands/Exceptions/SlashCommand/SlashExecutionChecksFailedException.cs +++ b/DisCatSharp.ApplicationCommands/Exceptions/SlashCommand/SlashExecutionChecksFailedException.cs @@ -1,38 +1,37 @@ // 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; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// Thrown when a pre-execution check for a slash command fails. +/// +public class SlashExecutionChecksFailedException : Exception { /// - /// Thrown when a pre-execution check for a slash command fails. + /// The list of failed checks. /// - public class SlashExecutionChecksFailedException : Exception - { - /// - /// The list of failed checks. - /// - public IReadOnlyList FailedChecks; - } + public IReadOnlyList FailedChecks; } diff --git a/DisCatSharp.ApplicationCommands/ExtensionMethods.cs b/DisCatSharp.ApplicationCommands/ExtensionMethods.cs index 84adaf1b0..94e8c41a8 100644 --- a/DisCatSharp.ApplicationCommands/ExtensionMethods.cs +++ b/DisCatSharp.ApplicationCommands/ExtensionMethods.cs @@ -1,110 +1,109 @@ // 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.Globalization; using System.Linq; using System.Reflection; using System.Threading.Tasks; -namespace DisCatSharp.ApplicationCommands +namespace DisCatSharp.ApplicationCommands; + +/// +/// Defines various extension methods for application commands. +/// +public static class ExtensionMethods { /// - /// Defines various extension methods for application commands. + /// Enables application commands on this . /// - public static class ExtensionMethods + /// Client to enable application commands for. + /// Configuration to use. + /// Created . + public static ApplicationCommandsExtension UseApplicationCommands(this DiscordClient client, + ApplicationCommandsConfiguration config = null) { - /// - /// Enables application commands on this . - /// - /// Client to enable application commands for. - /// Configuration to use. - /// Created . - public static ApplicationCommandsExtension UseApplicationCommands(this DiscordClient client, - ApplicationCommandsConfiguration config = null) - { - if (client.GetExtension() != null) - throw new InvalidOperationException("Application commands are already enabled for that client."); + if (client.GetExtension() != null) + throw new InvalidOperationException("Application commands are already enabled for that client."); - var scomm = new ApplicationCommandsExtension(config); - client.AddExtension(scomm); - return scomm; - } + var scomm = new ApplicationCommandsExtension(config); + client.AddExtension(scomm); + return scomm; + } - /// - /// Gets the application commands module for this client. - /// - /// Client to get application commands for. - /// The module, or null if not activated. - public static ApplicationCommandsExtension GetApplicationCommands(this DiscordClient client) - => client.GetExtension(); + /// + /// Gets the application commands module for this client. + /// + /// Client to get application commands for. + /// The module, or null if not activated. + public static ApplicationCommandsExtension GetApplicationCommands(this DiscordClient client) + => client.GetExtension(); - /// - /// Enables application commands on this . - /// - /// Client to enable application commands on. - /// Configuration to use. - /// A dictionary of created with the key being the shard id. - public static async Task> UseApplicationCommandsAsync(this DiscordShardedClient client, ApplicationCommandsConfiguration config = null) + /// + /// Enables application commands on this . + /// + /// Client to enable application commands on. + /// Configuration to use. + /// A dictionary of created with the key being the shard id. + public static async Task> UseApplicationCommandsAsync(this DiscordShardedClient client, ApplicationCommandsConfiguration config = null) + { + var modules = new Dictionary(); + await (Task)client.GetType().GetMethod("InitializeShardsAsync", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(client, null); + foreach (var shard in client.ShardClients.Values) { - var modules = new Dictionary(); - await (Task)client.GetType().GetMethod("InitializeShardsAsync", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(client, null); - foreach (var shard in client.ShardClients.Values) - { - var scomm = shard.GetApplicationCommands(); - if (scomm == null) - scomm = shard.UseApplicationCommands(config); + var scomm = shard.GetApplicationCommands(); + if (scomm == null) + scomm = shard.UseApplicationCommands(config); - modules[shard.ShardId] = scomm; - } - - return modules; + modules[shard.ShardId] = scomm; } - /// - /// Gets the name from the for this enum value. - /// - /// The name. - public static string GetName(this T e) where T : IConvertible + return modules; + } + + /// + /// Gets the name from the for this enum value. + /// + /// The name. + public static string GetName(this T e) where T : IConvertible + { + if (e is Enum) { - if (e is Enum) - { - var type = e.GetType(); - var values = Enum.GetValues(type); + var type = e.GetType(); + var values = Enum.GetValues(type); - foreach (int val in values) + foreach (int val in values) + { + if (val == e.ToInt32(CultureInfo.InvariantCulture)) { - if (val == e.ToInt32(CultureInfo.InvariantCulture)) - { - var memInfo = type.GetMember(type.GetEnumName(val)); + var memInfo = type.GetMember(type.GetEnumName(val)); - return memInfo[0] - .GetCustomAttributes(typeof(ChoiceNameAttribute), false) - .FirstOrDefault() is ChoiceNameAttribute nameAttribute ? nameAttribute.Name : type.GetEnumName(val); - } + return memInfo[0] + .GetCustomAttributes(typeof(ChoiceNameAttribute), false) + .FirstOrDefault() is ChoiceNameAttribute nameAttribute ? nameAttribute.Name : type.GetEnumName(val); } } - return null; } + return null; } } diff --git a/DisCatSharp.ApplicationCommands/Workers/ApplicationCommandWorker.cs b/DisCatSharp.ApplicationCommands/Workers/ApplicationCommandWorker.cs index 7958c9a08..00c8004e0 100644 --- a/DisCatSharp.ApplicationCommands/Workers/ApplicationCommandWorker.cs +++ b/DisCatSharp.ApplicationCommands/Workers/ApplicationCommandWorker.cs @@ -1,383 +1,382 @@ // 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 +namespace DisCatSharp.ApplicationCommands; + +/// +/// Represents a . +/// +internal class CommandWorker { /// - /// Represents a . + /// Parses context menu application commands. /// - internal class CommandWorker + /// The type. + /// List of method infos. + /// The optional command translations. + /// Too much. + internal static Task< + ( + List applicationCommands, + List> commandTypeSources, + List contextMenuCommands, + bool withLocalization + ) + > ParseContextMenuCommands(Type type, IEnumerable methods, List translator = null) { - /// - /// 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, - bool withLocalization - ) - > ParseContextMenuCommands(Type type, IEnumerable methods, List translator = null) - { - List commands = new(); - List> commandTypeSources = new(); - List contextMenuCommands = new(); + List commands = new(); + List> commandTypeSources = new(); + List contextMenuCommands = new(); - foreach (var contextMethod in methods) - { - var contextAttribute = contextMethod.GetCustomAttribute(); + foreach (var contextMethod in methods) + { + var contextAttribute = contextMethod.GetCustomAttribute(); - DiscordApplicationCommandLocalization nameLocalizations = null; + DiscordApplicationCommandLocalization nameLocalizations = null; - var commandTranslation = translator?.Single(c => c.Name == contextAttribute.Name && c.Type == contextAttribute.Type); - if (commandTranslation != null) - nameLocalizations = commandTranslation.NameTranslations; + 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.Type, nameLocalizations, null, contextAttribute.DefaultMemberPermissions, contextAttribute.DmPermission); + var command = new DiscordApplicationCommand(contextAttribute.Name, null, null, contextAttribute.Type, nameLocalizations, null, contextAttribute.DefaultMemberPermissions, contextAttribute.DmPermission); - 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!"); + 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 }); + contextMenuCommands.Add(new ContextMenuCommand { Method = contextMethod, Name = contextAttribute.Name }); - commands.Add(command); - commandTypeSources.Add(new KeyValuePair(type, type)); - } - - return Task.FromResult((commands, commandTypeSources, contextMenuCommands, translator != null)); + commands.Add(command); + commandTypeSources.Add(new KeyValuePair(type, type)); } - /// - /// 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, - bool withLocalization - ) - > ParseBasicSlashCommandsAsync(Type type, IEnumerable methods, ulong? guildId = null, List translator = null) - { - List commands = new(); - List> commandTypeSources = new(); - List commandMethods = new(); + return Task.FromResult((commands, commandTypeSources, contextMenuCommands, translator != null)); + } - foreach (var method in methods) - { - var commandAttribute = method.GetCustomAttribute(); + /// + /// 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, + bool withLocalization + ) + > ParseBasicSlashCommandsAsync(Type type, IEnumerable methods, ulong? guildId = null, List translator = null) + { + List commands = new(); + List> commandTypeSources = new(); + List commandMethods = new(); - 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!"); - var options = await ApplicationCommandsExtension.ParseParametersAsync(parameters.Skip(1), guildId); + foreach (var method in methods) + { + var commandAttribute = method.GetCustomAttribute(); - commandMethods.Add(new CommandMethod { Method = method, Name = commandAttribute.Name }); + 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!"); + var options = await ApplicationCommandsExtension.ParseParametersAsync(parameters.Skip(1), guildId); - DiscordApplicationCommandLocalization nameLocalizations = null; - DiscordApplicationCommandLocalization descriptionLocalizations = null; - List localizedOptions = null; + commandMethods.Add(new CommandMethod { Method = method, Name = commandAttribute.Name }); + + DiscordApplicationCommandLocalization nameLocalizations = null; + DiscordApplicationCommandLocalization descriptionLocalizations = null; + List localizedOptions = null; - var commandTranslation = translator?.Single(c => c.Name == commandAttribute.Name && c.Type == ApplicationCommandType.ChatInput); + var commandTranslation = translator?.Single(c => c.Name == commandAttribute.Name && c.Type == ApplicationCommandType.ChatInput); - if (commandTranslation != null && commandTranslation.Options != null) + if (commandTranslation != null && commandTranslation.Options != null) + { + localizedOptions = new List(options.Count); + foreach (var option in options) { - localizedOptions = new List(options.Count); - foreach (var option in options) + var choices = option.Choices != null ? new List(option.Choices.Count) : null; + if (option.Choices != null) { - var choices = option.Choices != null ? new List(option.Choices.Count) : null; - if (option.Choices != null) + foreach (var choice in option.Choices) { - 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)); - } + choices.Add(new DiscordApplicationCommandOptionChoice(choice.Name, choice.Value, commandTranslation.Options.Single(o => o.Name == option.Name).Choices.Single(c => c.Name == choice.Name).NameTranslations)); } - - localizedOptions.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; + localizedOptions.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 + )); } - var payload = new DiscordApplicationCommand(commandAttribute.Name, commandAttribute.Description, (localizedOptions != null && localizedOptions.Any() ? localizedOptions : null) ?? (options != null && options.Any() ? options : null), ApplicationCommandType.ChatInput, nameLocalizations, descriptionLocalizations, commandAttribute.DefaultMemberPermissions, commandAttribute.DmPermission); - commands.Add(payload); - commandTypeSources.Add(new KeyValuePair(type, type)); + nameLocalizations = commandTranslation.NameTranslations; + descriptionLocalizations = commandTranslation.DescriptionTranslations; } - return (commands, commandTypeSources, commandMethods, translator != null); + var payload = new DiscordApplicationCommand(commandAttribute.Name, commandAttribute.Description, (localizedOptions != null && localizedOptions.Any() ? localizedOptions : null) ?? (options != null && options.Any() ? options : null), ApplicationCommandType.ChatInput, nameLocalizations, descriptionLocalizations, commandAttribute.DefaultMemberPermissions, commandAttribute.DmPermission); + commands.Add(payload); + commandTypeSources.Add(new KeyValuePair(type, type)); } + + return (commands, commandTypeSources, commandMethods, translator != null); } +} +/// +/// Represents a . +/// +internal class NestedCommandWorker +{ /// - /// Represents a . + /// Parses application command groups. /// - internal class NestedCommandWorker + /// 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, + bool withLocalization + ) + > ParseSlashGroupsAsync(Type type, List types, ulong? guildId = null, List translator = null) { - /// - /// 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, - bool withLocalization - ) - > 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) { - 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()) { - //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!"); - } + throw new ArgumentException("Slash command groups cannot have both subcommands and subgroups!"); + } - DiscordApplicationCommandLocalization nameLocalizations = null; - DiscordApplicationCommandLocalization descriptionLocalizations = null; + DiscordApplicationCommandLocalization nameLocalizations = null; + DiscordApplicationCommandLocalization descriptionLocalizations = null; - if (translator != null) + if (translator != null) + { + var commandTranslation = translator.Single(c => c.Name == groupAttribute.Name); + if (commandTranslation != null) { - var commandTranslation = translator.Single(c => c.Name == groupAttribute.Name); - if (commandTranslation != null) - { - nameLocalizations = commandTranslation.NameTranslations; - descriptionLocalizations = commandTranslation.DescriptionTranslations; - } + nameLocalizations = commandTranslation.NameTranslations; + descriptionLocalizations = commandTranslation.DescriptionTranslations; } + } - //Initializes the command - var payload = new DiscordApplicationCommand(groupAttribute.Name, groupAttribute.Description, nameLocalizations: nameLocalizations, descriptionLocalizations: descriptionLocalizations, defaultMemberPermissions: groupAttribute.DefaultMemberPermissions, dmPermission: groupAttribute.DmPermission); - commandTypeSources.Add(new KeyValuePair(type, type)); + //Initializes the command + var payload = new DiscordApplicationCommand(groupAttribute.Name, groupAttribute.Description, nameLocalizations: nameLocalizations, descriptionLocalizations: descriptionLocalizations, defaultMemberPermissions: groupAttribute.DefaultMemberPermissions, dmPermission: groupAttribute.DmPermission); + commandTypeSources.Add(new KeyValuePair(type, type)); - var commandMethods = new List>(); - //Handles commands in the group - foreach (var submethod in submethods) - { - var commandAttribute = submethod.GetCustomAttribute(); + var commandMethods = new List>(); + //Handles commands in the group + foreach (var submethod in submethods) + { + var commandAttribute = submethod.GetCustomAttribute(); - //Gets the parameters 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!"); + //Gets the parameters 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!"); - var options = await ApplicationCommandsExtension.ParseParametersAsync(parameters.Skip(1), guildId); + var options = await ApplicationCommandsExtension.ParseParametersAsync(parameters.Skip(1), guildId); - DiscordApplicationCommandLocalization subNameLocalizations = null; - DiscordApplicationCommandLocalization subDescriptionLocalizations = null; - List localizedOptions = null; + DiscordApplicationCommandLocalization subNameLocalizations = null; + DiscordApplicationCommandLocalization subDescriptionLocalizations = null; + List localizedOptions = null; - var commandTranslation = translator?.Single(c => c.Name == payload.Name); + var commandTranslation = translator?.Single(c => c.Name == payload.Name); - if (commandTranslation?.Commands != null) - { + if (commandTranslation?.Commands != null) + { - var subCommandTranslation = commandTranslation.Commands.Single(sc => sc.Name == commandAttribute.Name); - if (subCommandTranslation.Options != null) + var subCommandTranslation = commandTranslation.Commands.Single(sc => sc.Name == commandAttribute.Name); + if (subCommandTranslation.Options != null) + { + localizedOptions = new List(options.Count); + foreach (var option in options) { - localizedOptions = new List(options.Count); - foreach (var option in options) + var choices = option.Choices != null ? new List(option.Choices.Count) : null; + if (option.Choices != null) { - var choices = option.Choices != null ? new List(option.Choices.Count) : null; - if (option.Choices != null) + foreach (var choice in option.Choices) { - 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)); - } + choices.Add(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 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; + localizedOptions.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, localizedOptions ?? options, nameLocalizations: subNameLocalizations, descriptionLocalizations: subDescriptionLocalizations); - payload = new DiscordApplicationCommand(payload.Name, payload.Description, payload.Options?.Append(subpayload) ?? new[] { subpayload }, nameLocalizations: payload.NameLocalizations, descriptionLocalizations: payload.DescriptionLocalizations, defaultMemberPermissions: payload.DefaultMemberPermissions, dmPermission: payload.DmPermission); - 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 }); - } + //Creates the subcommand and adds it to the main command + var subpayload = new DiscordApplicationCommandOption(commandAttribute.Name, commandAttribute.Description, ApplicationCommandOptionType.SubCommand, null, null, localizedOptions ?? options, nameLocalizations: subNameLocalizations, descriptionLocalizations: subDescriptionLocalizations); + payload = new DiscordApplicationCommand(payload.Name, payload.Description, payload.Options?.Append(subpayload) ?? new[] { subpayload }, nameLocalizations: payload.NameLocalizations, descriptionLocalizations: payload.DescriptionLocalizations, defaultMemberPermissions: payload.DefaultMemberPermissions, dmPermission: payload.DmPermission); + commandTypeSources.Add(new KeyValuePair(subclassInfo, type)); - 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); + //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 options = new List(); - var currentMethods = new List>(); + var currentMethods = new List>(); - DiscordApplicationCommandLocalization subNameLocalizations = null; - DiscordApplicationCommandLocalization subDescriptionLocalizations = null; + DiscordApplicationCommandLocalization subNameLocalizations = null; + DiscordApplicationCommandLocalization subDescriptionLocalizations = null; - if (translator != null) + if (translator != null) + { + var commandTranslation = translator.Single(c => c.Name == payload.Name); + if (commandTranslation != null && commandTranslation.SubGroups != 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); + var subCommandTranslation = commandTranslation.SubGroups.Single(sc => sc.Name == subgroupAttribute.Name); - if (subCommandTranslation != null) - { - subNameLocalizations = subCommandTranslation.NameTranslations; - subDescriptionLocalizations = subCommandTranslation.DescriptionTranslations; - } + 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!"); + //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!"); - suboptions = suboptions.Concat(await ApplicationCommandsExtension.ParseParametersAsync(parameters.Skip(1), guildId)).ToList(); + suboptions = suboptions.Concat(await ApplicationCommandsExtension.ParseParametersAsync(parameters.Skip(1), guildId)).ToList(); - DiscordApplicationCommandLocalization subSubNameLocalizations = null; - DiscordApplicationCommandLocalization subSubDescriptionLocalizations = null; - List localizedOptions = null; + DiscordApplicationCommandLocalization subSubNameLocalizations = null; + DiscordApplicationCommandLocalization subSubDescriptionLocalizations = null; + List localizedOptions = null; - var commandTranslation = translator?.Single(c => c.Name == payload.Name); + 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 == commatt.Name); - var subSubCommandTranslation = subCommandTranslation?.Commands.Single(sc => sc.Name == commatt.Name); + var subSubCommandTranslation = subCommandTranslation?.Commands.Single(sc => sc.Name == commatt.Name); - if (subSubCommandTranslation != null && subSubCommandTranslation.Options != null) + if (subSubCommandTranslation != null && subSubCommandTranslation.Options != null) + { + localizedOptions = new List(suboptions.Count); + foreach (var option in suboptions) { - localizedOptions = new List(suboptions.Count); - foreach (var option in suboptions) + var choices = option.Choices != null ? new List(option.Choices.Count) : null; + if (option.Choices != null) { - var choices = option.Choices != null ? new List(option.Choices.Count) : null; - if (option.Choices != null) + foreach (var choice in option.Choices) { - 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)); - } + choices.Add(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 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; + localizedOptions.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 + )); } - var subsubpayload = new DiscordApplicationCommandOption(commatt.Name, commatt.Description, ApplicationCommandOptionType.SubCommand, null, null, (localizedOptions != null && localizedOptions.Any() ? localizedOptions : null) ?? (suboptions != null && suboptions.Any() ? suboptions : null), nameLocalizations: subSubNameLocalizations, descriptionLocalizations: subSubDescriptionLocalizations); - options.Add(subsubpayload); - commandMethods.Add(new KeyValuePair(commatt.Name, subsubmethod)); - currentMethods.Add(new KeyValuePair(commatt.Name, subsubmethod)); + subSubNameLocalizations = subSubCommandTranslation.NameTranslations; + subSubDescriptionLocalizations = subSubCommandTranslation.DescriptionTranslations; } - //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 }, nameLocalizations: payload.NameLocalizations, descriptionLocalizations: payload.DescriptionLocalizations, defaultMemberPermissions: payload.DefaultMemberPermissions, dmPermission: payload.DmPermission); - 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)); - } + var subsubpayload = new DiscordApplicationCommandOption(commatt.Name, commatt.Description, ApplicationCommandOptionType.SubCommand, null, null, (localizedOptions != null && localizedOptions.Any() ? localizedOptions : null) ?? (suboptions != null && suboptions.Any() ? suboptions : null), nameLocalizations: subSubNameLocalizations, descriptionLocalizations: subSubDescriptionLocalizations); + options.Add(subsubpayload); + commandMethods.Add(new KeyValuePair(commatt.Name, subsubmethod)); + currentMethods.Add(new KeyValuePair(commatt.Name, subsubmethod)); } - if (command.SubCommands.Any()) subGroupCommands.Add(command); - commands.Add(payload); - //Accounts for lifespans - if (subclassInfo.GetCustomAttribute() != null && subclassInfo.GetCustomAttribute().Lifespan == ApplicationCommandModuleLifespan.Singleton) + //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 }, nameLocalizations: payload.NameLocalizations, descriptionLocalizations: payload.DescriptionLocalizations, defaultMemberPermissions: payload.DefaultMemberPermissions, dmPermission: payload.DmPermission); + 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(subclassInfo, ApplicationCommandsExtension.Configuration?.ServiceProvider)); + singletonModules.Add(ApplicationCommandsExtension.CreateInstance(subclass, ApplicationCommandsExtension.Configuration?.ServiceProvider)); } } + if (command.SubCommands.Any()) subGroupCommands.Add(command); + commands.Add(payload); - return (commands, commandTypeSources, singletonModules, groupCommands, subGroupCommands, translator != null); + //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, translator != null); } } diff --git a/DisCatSharp.ApplicationCommands/Workers/RegistrationWorker.cs b/DisCatSharp.ApplicationCommands/Workers/RegistrationWorker.cs index 0482fde92..b55b76375 100644 --- a/DisCatSharp.ApplicationCommands/Workers/RegistrationWorker.cs +++ b/DisCatSharp.ApplicationCommands/Workers/RegistrationWorker.cs @@ -1,585 +1,584 @@ // 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 +namespace DisCatSharp.ApplicationCommands; + +/// +/// Represents a . +/// +internal class RegistrationWorker { /// - /// Represents a . + /// Registers the global commands. /// - internal class RegistrationWorker + /// The command list. + /// A list of registered commands. + internal static async Task> RegisterGlobalCommandsAsync(List commands) { - /// - /// Registers the global commands. - /// - /// The command list. - /// A list of registered commands. - internal static async Task> RegisterGlobalCommandsAsync(List commands) + var (changedCommands, unchangedCommands) = BuildGlobalOverwriteList(commands); + var globalCommandsCreateList = BuildGlobalCreateList(commands); + var globalCommandsDeleteList = BuildGlobalDeleteList(commands); + + if (globalCommandsCreateList.NotEmptyAndNotNull() && unchangedCommands.NotEmptyAndNotNull() && changedCommands.NotEmptyAndNotNull()) { - var (changedCommands, unchangedCommands) = BuildGlobalOverwriteList(commands); - var globalCommandsCreateList = BuildGlobalCreateList(commands); - var globalCommandsDeleteList = BuildGlobalDeleteList(commands); + ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Creating, re-using and overwriting application commands."); - if (globalCommandsCreateList.NotEmptyAndNotNull() && unchangedCommands.NotEmptyAndNotNull() && changedCommands.NotEmptyAndNotNull()) + foreach (var cmd in globalCommandsCreateList) { - ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Creating, re-using and overwriting application commands."); - - foreach (var cmd in globalCommandsCreateList) - { - var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.CreateGlobalApplicationCommandAsync(cmd); - commands.Add(discordBackendCommand); - } - - foreach (var cmd in changedCommands) - { - 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.Some(command.Options); - if (command.DefaultMemberPermissions.HasValue) - action.DefaultMemberPermissions = command.DefaultMemberPermissions.Value; - if (command.DmPermission.HasValue) - action.DmPermission = command.DmPermission.Value; - action.IsNsfw = command.IsNsfw; - }); - - commands.Add(discordBackendCommand); - } - - commands.AddRange(unchangedCommands); + var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.CreateGlobalApplicationCommandAsync(cmd); + commands.Add(discordBackendCommand); } - else if (globalCommandsCreateList.NotEmptyAndNotNull() && (unchangedCommands.NotEmptyAndNotNull() || changedCommands.NotEmptyAndNotNull())) - { - ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Creating, re-using and overwriting application commands."); - foreach (var cmd in globalCommandsCreateList) + foreach (var cmd in changedCommands) + { + var command = cmd.Value; + var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.EditGlobalApplicationCommandAsync(cmd.Key, action => { - var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.CreateGlobalApplicationCommandAsync(cmd); - commands.Add(discordBackendCommand); - } + 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.Some(command.Options); + if (command.DefaultMemberPermissions.HasValue) + action.DefaultMemberPermissions = command.DefaultMemberPermissions.Value; + if (command.DmPermission.HasValue) + action.DmPermission = command.DmPermission.Value; + action.IsNsfw = command.IsNsfw; + }); + + commands.Add(discordBackendCommand); + } - if (changedCommands.NotEmptyAndNotNull()) - { - foreach (var cmd in changedCommands) - { - 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.Some(command.Options); - if (command.DefaultMemberPermissions.HasValue) - action.DefaultMemberPermissions = command.DefaultMemberPermissions.Value; - if (command.DmPermission.HasValue) - action.DmPermission = command.DmPermission.Value; - action.IsNsfw = command.IsNsfw; - }); - - commands.Add(discordBackendCommand); - } - } + commands.AddRange(unchangedCommands); + } + else if (globalCommandsCreateList.NotEmptyAndNotNull() && (unchangedCommands.NotEmptyAndNotNull() || changedCommands.NotEmptyAndNotNull())) + { + ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Creating, re-using and overwriting application commands."); - if (unchangedCommands.NotEmptyAndNotNull()) - commands.AddRange(unchangedCommands); - } - else if (globalCommandsCreateList.EmptyOrNull() && unchangedCommands.NotEmptyAndNotNull() && changedCommands.NotEmptyAndNotNull()) + foreach (var cmd in globalCommandsCreateList) { - ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Editing & re-using application commands."); + var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.CreateGlobalApplicationCommandAsync(cmd); + commands.Add(discordBackendCommand); + } + if (changedCommands.NotEmptyAndNotNull()) + { foreach (var cmd in changedCommands) { 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.Some(command.Options); if (command.DefaultMemberPermissions.HasValue) action.DefaultMemberPermissions = command.DefaultMemberPermissions.Value; if (command.DmPermission.HasValue) action.DmPermission = command.DmPermission.Value; action.IsNsfw = command.IsNsfw; }); commands.Add(discordBackendCommand); } + } + if (unchangedCommands.NotEmptyAndNotNull()) commands.AddRange(unchangedCommands); - } - else if (globalCommandsCreateList.EmptyOrNull() && changedCommands.NotEmptyAndNotNull() && unchangedCommands.EmptyOrNull()) - { - ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Overwriting all application commands."); + } + else if (globalCommandsCreateList.EmptyOrNull() && unchangedCommands.NotEmptyAndNotNull() && changedCommands.NotEmptyAndNotNull()) + { + ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Editing & re-using application commands."); - List overwriteList = new(); - foreach (var overwrite in changedCommands) - { - 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() && changedCommands.EmptyOrNull() && unchangedCommands.EmptyOrNull()) + foreach (var cmd in changedCommands) { - ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Creating all application commands."); - - var cmds = await ApplicationCommandsExtension.ClientInternal.BulkOverwriteGlobalApplicationCommandsAsync(globalCommandsCreateList); - commands.AddRange(cmds); + 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.Some(command.Options); + if (command.DefaultMemberPermissions.HasValue) + action.DefaultMemberPermissions = command.DefaultMemberPermissions.Value; + if (command.DmPermission.HasValue) + action.DmPermission = command.DmPermission.Value; + action.IsNsfw = command.IsNsfw; + }); + + commands.Add(discordBackendCommand); } - else if (globalCommandsCreateList.EmptyOrNull() && changedCommands.EmptyOrNull() && unchangedCommands.NotEmptyAndNotNull()) - { - ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Re-using all application commands."); - commands.AddRange(unchangedCommands); - } + commands.AddRange(unchangedCommands); + } + else if (globalCommandsCreateList.EmptyOrNull() && changedCommands.NotEmptyAndNotNull() && unchangedCommands.EmptyOrNull()) + { + ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Overwriting all application commands."); - if (globalCommandsDeleteList.NotEmptyAndNotNull()) + List overwriteList = new(); + foreach (var overwrite in changedCommands) { - ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Deleting missing application commands."); - - 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"); - } - } + 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() && changedCommands.EmptyOrNull() && unchangedCommands.EmptyOrNull()) + { + ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Creating all application commands."); - return commands.NotEmptyAndNotNull() ? commands : null; + var cmds = await ApplicationCommandsExtension.ClientInternal.BulkOverwriteGlobalApplicationCommandsAsync(globalCommandsCreateList); + commands.AddRange(cmds); } + else if (globalCommandsCreateList.EmptyOrNull() && changedCommands.EmptyOrNull() && unchangedCommands.NotEmptyAndNotNull()) + { + ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Re-using all application commands."); - /// - /// 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) + commands.AddRange(unchangedCommands); + } + + if (globalCommandsDeleteList.NotEmptyAndNotNull()) { - var (changedCommands, unchangedCommands) = BuildGuildOverwriteList(guildId, commands); - var guildCommandsCreateList = BuildGuildCreateList(guildId, commands); - var guildCommandsDeleteList = BuildGuildDeleteList(guildId, commands); + ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Deleting missing application commands."); - if (guildCommandsCreateList.NotEmptyAndNotNull() && unchangedCommands.NotEmptyAndNotNull() && changedCommands.NotEmptyAndNotNull()) + foreach (var cmdId in globalCommandsDeleteList) { - ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Creating, re-using and overwriting application commands. Guild ID: {guildId}"); - - foreach (var cmd in guildCommandsCreateList) + try { - var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.CreateGuildApplicationCommandAsync(guildId, cmd); - commands.Add(discordBackendCommand); + await ApplicationCommandsExtension.ClientInternal.DeleteGlobalApplicationCommandAsync(cmdId); } - - foreach (var cmd in changedCommands) + catch (NotFoundException) { - 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.Some(command.Options); - if (command.DefaultMemberPermissions.HasValue) - action.DefaultMemberPermissions = command.DefaultMemberPermissions.Value; - if (command.DmPermission.HasValue) - action.DmPermission = command.DmPermission.Value; - action.IsNsfw = command.IsNsfw; - }); - - commands.Add(discordBackendCommand); + ApplicationCommandsExtension.ClientInternal.Logger.LogError($"Could not delete global command {cmdId}. Please clean up manually"); } - - commands.AddRange(unchangedCommands); } - else if (guildCommandsCreateList.NotEmptyAndNotNull() && (unchangedCommands.NotEmptyAndNotNull() || changedCommands.NotEmptyAndNotNull())) + } + + 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 (changedCommands, unchangedCommands) = BuildGuildOverwriteList(guildId, commands); + var guildCommandsCreateList = BuildGuildCreateList(guildId, commands); + var guildCommandsDeleteList = BuildGuildDeleteList(guildId, commands); + + if (guildCommandsCreateList.NotEmptyAndNotNull() && unchangedCommands.NotEmptyAndNotNull() && changedCommands.NotEmptyAndNotNull()) + { + ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Creating, re-using and overwriting application commands. Guild ID: {guildId}"); + + foreach (var cmd in guildCommandsCreateList) { - ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Creating, re-using and overwriting application commands. Guild ID: {guildId}"); + var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.CreateGuildApplicationCommandAsync(guildId, cmd); + commands.Add(discordBackendCommand); + } - foreach (var cmd in guildCommandsCreateList) + foreach (var cmd in changedCommands) + { + var command = cmd.Value; + var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.EditGuildApplicationCommandAsync(guildId, cmd.Key, action => { - var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.CreateGuildApplicationCommandAsync(guildId, cmd); - commands.Add(discordBackendCommand); - } + 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.Some(command.Options); + if (command.DefaultMemberPermissions.HasValue) + action.DefaultMemberPermissions = command.DefaultMemberPermissions.Value; + if (command.DmPermission.HasValue) + action.DmPermission = command.DmPermission.Value; + action.IsNsfw = command.IsNsfw; + }); + + commands.Add(discordBackendCommand); + } - if (changedCommands.NotEmptyAndNotNull()) - { - foreach (var cmd in changedCommands) - { - 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.Some(command.Options); - if (command.DefaultMemberPermissions.HasValue) - action.DefaultMemberPermissions = command.DefaultMemberPermissions.Value; - if (command.DmPermission.HasValue) - action.DmPermission = command.DmPermission.Value; - action.IsNsfw = command.IsNsfw; - }); - - commands.Add(discordBackendCommand); - } - } + commands.AddRange(unchangedCommands); + } + else if (guildCommandsCreateList.NotEmptyAndNotNull() && (unchangedCommands.NotEmptyAndNotNull() || changedCommands.NotEmptyAndNotNull())) + { + ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Creating, re-using and overwriting application commands. Guild ID: {guildId}"); - if (unchangedCommands.NotEmptyAndNotNull()) - commands.AddRange(unchangedCommands); - } - else if (guildCommandsCreateList.EmptyOrNull() && unchangedCommands.NotEmptyAndNotNull() && changedCommands.NotEmptyAndNotNull()) + foreach (var cmd in guildCommandsCreateList) { - ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Editing & re-using application commands. Guild ID: {guildId}"); + var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.CreateGuildApplicationCommandAsync(guildId, cmd); + commands.Add(discordBackendCommand); + } + if (changedCommands.NotEmptyAndNotNull()) + { foreach (var cmd in changedCommands) { 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.Some(command.Options); if (command.DefaultMemberPermissions.HasValue) action.DefaultMemberPermissions = command.DefaultMemberPermissions.Value; if (command.DmPermission.HasValue) action.DmPermission = command.DmPermission.Value; action.IsNsfw = command.IsNsfw; }); commands.Add(discordBackendCommand); } + } + if (unchangedCommands.NotEmptyAndNotNull()) commands.AddRange(unchangedCommands); - } - else if (guildCommandsCreateList.EmptyOrNull() && changedCommands.NotEmptyAndNotNull() && unchangedCommands.EmptyOrNull()) - { - ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Overwriting all application commands. Guild ID: {guildId}"); + } + else if (guildCommandsCreateList.EmptyOrNull() && unchangedCommands.NotEmptyAndNotNull() && changedCommands.NotEmptyAndNotNull()) + { + ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Editing & re-using application commands. Guild ID: {guildId}"); - List overwriteList = new(); - foreach (var overwrite in changedCommands) + foreach (var cmd in changedCommands) + { + var command = cmd.Value; + var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.EditGuildApplicationCommandAsync(guildId, cmd.Key, action => { - var cmd = overwrite.Value; - cmd.Id = overwrite.Key; - overwriteList.Add(cmd); - } - var discordBackendCommands = await ApplicationCommandsExtension.ClientInternal.BulkOverwriteGuildApplicationCommandsAsync(guildId, overwriteList); - commands.AddRange(discordBackendCommands); + 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.Some(command.Options); + if (command.DefaultMemberPermissions.HasValue) + action.DefaultMemberPermissions = command.DefaultMemberPermissions.Value; + if (command.DmPermission.HasValue) + action.DmPermission = command.DmPermission.Value; + action.IsNsfw = command.IsNsfw; + }); + + commands.Add(discordBackendCommand); } - else if (guildCommandsCreateList.NotEmptyAndNotNull() && changedCommands.EmptyOrNull() && unchangedCommands.EmptyOrNull()) - { - ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Creating all application commands. Guild ID: {guildId}"); - var cmds = await ApplicationCommandsExtension.ClientInternal.BulkOverwriteGuildApplicationCommandsAsync(guildId, guildCommandsCreateList); - commands.AddRange(cmds); - } - else if (guildCommandsCreateList.EmptyOrNull() && changedCommands.EmptyOrNull() && unchangedCommands.NotEmptyAndNotNull()) - { - ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Re-using all application commands Guild ID: {guildId}."); + commands.AddRange(unchangedCommands); + } + else if (guildCommandsCreateList.EmptyOrNull() && changedCommands.NotEmptyAndNotNull() && unchangedCommands.EmptyOrNull()) + { + ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Overwriting all application commands. Guild ID: {guildId}"); - commands.AddRange(unchangedCommands); + List overwriteList = new(); + foreach (var overwrite in changedCommands) + { + 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() && changedCommands.EmptyOrNull() && unchangedCommands.EmptyOrNull()) + { + ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Creating all application commands. Guild ID: {guildId}"); - if (guildCommandsDeleteList.NotEmptyAndNotNull()) + var cmds = await ApplicationCommandsExtension.ClientInternal.BulkOverwriteGuildApplicationCommandsAsync(guildId, guildCommandsCreateList); + commands.AddRange(cmds); + } + else if (guildCommandsCreateList.EmptyOrNull() && changedCommands.EmptyOrNull() && unchangedCommands.NotEmptyAndNotNull()) + { + ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Re-using all application commands Guild ID: {guildId}."); + + commands.AddRange(unchangedCommands); + } + + if (guildCommandsDeleteList.NotEmptyAndNotNull()) + { + foreach (var cmdId in guildCommandsDeleteList) { - foreach (var cmdId in guildCommandsDeleteList) + ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Deleting missing application commands. Guild ID: {guildId}"); + try { - ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Deleting missing application commands. Guild ID: {guildId}"); - 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"); - } + 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; + 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; + if (ApplicationCommandsExtension.GuildDiscordCommands == null || !ApplicationCommandsExtension.GuildDiscordCommands.Any() + || !ApplicationCommandsExtension.GuildDiscordCommands.GetFirstValueByKey(guildId, out discord) + ) + return null; - List invalidCommandIds = new(); + List invalidCommandIds = new(); - if (discord == null) - return null; + if (discord == null) + return null; - if (updateList == null) + if (updateList == null) + { + foreach (var cmd in discord) { - foreach (var cmd in discord) - { - invalidCommandIds.Add(cmd.Id); - } + invalidCommandIds.Add(cmd.Id); } - else + } + else + { + foreach (var cmd in discord) { - foreach (var cmd in discord) - { - if (!updateList.Any(ul => ul.Name == cmd.Name)) - invalidCommandIds.Add(cmd.Id); - } + 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; + 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; + if (ApplicationCommandsExtension.GuildDiscordCommands == null || !ApplicationCommandsExtension.GuildDiscordCommands.Any() + || updateList == null || !ApplicationCommandsExtension.GuildDiscordCommands.GetFirstValueByKey(guildId, out discord) + ) + return updateList; - List newCommands = new(); + List newCommands = new(); - if (discord == null) - return updateList; + if (discord == null) + return updateList; - foreach (var cmd in updateList) + foreach (var cmd in updateList) + { + if (discord.All(d => d.Name != cmd.Name)) { - if (discord.All(d => d.Name != cmd.Name)) - { - newCommands.Add(cmd); - } + 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 changedCommands, - List unchangedCommands - ) BuildGuildOverwriteList(ulong guildId, List updateList) - { - List discord; + 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 changedCommands, + List unchangedCommands + ) BuildGuildOverwriteList(ulong guildId, List updateList) + { + List discord; - if (ApplicationCommandsExtension.GuildDiscordCommands == null || !ApplicationCommandsExtension.GuildDiscordCommands.Any() - || ApplicationCommandsExtension.GuildDiscordCommands.All(l => l.Key != guildId) || updateList == null - || !ApplicationCommandsExtension.GuildDiscordCommands.GetFirstValueByKey(guildId, out discord) - ) - return (null, null); + if (ApplicationCommandsExtension.GuildDiscordCommands == null || !ApplicationCommandsExtension.GuildDiscordCommands.Any() + || ApplicationCommandsExtension.GuildDiscordCommands.All(l => l.Key != guildId) || updateList == null + || !ApplicationCommandsExtension.GuildDiscordCommands.GetFirstValueByKey(guildId, out discord) + ) + return (null, null); - Dictionary updateCommands = new(); - List unchangedCommands = new(); + Dictionary updateCommands = new(); + List unchangedCommands = new(); - if (discord == null) - return (null, null); + if (discord == null) + return (null, null); - foreach (var cmd in updateList) + foreach (var cmd in updateList) + { + if (discord.GetFirstValueWhere(d => d.Name == cmd.Name, out var command)) { - if (discord.GetFirstValueWhere(d => d.Name == cmd.Name, out var command)) + if (command.IsEqualTo(cmd)) { - if (command.IsEqualTo(cmd)) - { - if (ApplicationCommandsExtension.DebugEnabled) - ApplicationCommandsExtension.ClientInternal.Logger.LogDebug($"[AC] Command {cmd.Name} unchanged"); - cmd.Id = command.Id; - cmd.ApplicationId = command.ApplicationId; - cmd.Version = command.Version; - unchangedCommands.Add(cmd); - } - else - { - if (ApplicationCommandsExtension.DebugEnabled) - ApplicationCommandsExtension.ClientInternal.Logger.LogDebug($"[AC] Command {cmd.Name} changed"); - updateCommands.Add(command.Id, cmd); - } + if (ApplicationCommandsExtension.DebugEnabled) + ApplicationCommandsExtension.ClientInternal.Logger.LogDebug($"[AC] Command {cmd.Name} unchanged"); + cmd.Id = command.Id; + cmd.ApplicationId = command.ApplicationId; + cmd.Version = command.Version; + unchangedCommands.Add(cmd); + } + else + { + if (ApplicationCommandsExtension.DebugEnabled) + ApplicationCommandsExtension.ClientInternal.Logger.LogDebug($"[AC] Command {cmd.Name} changed"); + updateCommands.Add(command.Id, cmd); } } - - return (updateCommands, unchangedCommands); } - /// - /// 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; + return (updateCommands, unchangedCommands); + } + + /// + /// 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; + var discord = ApplicationCommandsExtension.GlobalDiscordCommands; - List invalidCommandIds = new(); + List invalidCommandIds = new(); - if (discord == null) - return null; + if (discord == null) + return null; - if (updateList == null) + if (updateList == null) + { + foreach (var cmd in discord) { - foreach (var cmd in discord) - { - invalidCommandIds.Add(cmd.Id); - } + invalidCommandIds.Add(cmd.Id); } - else + } + else + { + foreach (var cmd in discord) { - foreach (var cmd in discord) - { - if (updateList.All(ul => ul.Name != cmd.Name)) - invalidCommandIds.Add(cmd.Id); - } + 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; + 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; + var discord = ApplicationCommandsExtension.GlobalDiscordCommands; - List newCommands = new(); + List newCommands = new(); - if (discord == null) - return updateList; + if (discord == null) + return updateList; - foreach (var cmd in updateList) + foreach (var cmd in updateList) + { + if (discord.All(d => d.Name != cmd.Name)) { - if (discord.All(d => d.Name != cmd.Name)) - { - newCommands.Add(cmd); - } + 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 changedCommands, - List unchangedCommands - ) BuildGlobalOverwriteList(List updateList) - { - if (ApplicationCommandsExtension.GlobalDiscordCommands == null || !ApplicationCommandsExtension.GlobalDiscordCommands.Any() - || updateList == null || ApplicationCommandsExtension.GlobalDiscordCommands == null - ) - return (null, null); + return newCommands; + } - var discord = ApplicationCommandsExtension.GlobalDiscordCommands; + /// + /// 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 changedCommands, + List unchangedCommands + ) BuildGlobalOverwriteList(List updateList) + { + if (ApplicationCommandsExtension.GlobalDiscordCommands == null || !ApplicationCommandsExtension.GlobalDiscordCommands.Any() + || updateList == null || ApplicationCommandsExtension.GlobalDiscordCommands == null + ) + return (null, null); + + var discord = ApplicationCommandsExtension.GlobalDiscordCommands; - if (discord == null) - return (null, null); + if (discord == null) + return (null, null); - Dictionary updateCommands = new(); - List unchangedCommands = new(); - foreach (var cmd in updateList) + Dictionary updateCommands = new(); + List unchangedCommands = new(); + foreach (var cmd in updateList) + { + if (discord.GetFirstValueWhere(d => d.Name == cmd.Name, out var command)) { - if (discord.GetFirstValueWhere(d => d.Name == cmd.Name, out var command)) + if (command.IsEqualTo(cmd)) { - if (command.IsEqualTo(cmd)) - { - if (ApplicationCommandsExtension.DebugEnabled) - ApplicationCommandsExtension.ClientInternal.Logger.LogDebug($"[AC] Command {cmd.Name} unchanged"); - cmd.Id = command.Id; - cmd.ApplicationId = command.ApplicationId; - cmd.Version = command.Version; - unchangedCommands.Add(cmd); - } - else - { - if (ApplicationCommandsExtension.DebugEnabled) - ApplicationCommandsExtension.ClientInternal.Logger.LogDebug($"[AC] Command {cmd.Name} changed"); - updateCommands.Add(command.Id, cmd); - } + if (ApplicationCommandsExtension.DebugEnabled) + ApplicationCommandsExtension.ClientInternal.Logger.LogDebug($"[AC] Command {cmd.Name} unchanged"); + cmd.Id = command.Id; + cmd.ApplicationId = command.ApplicationId; + cmd.Version = command.Version; + unchangedCommands.Add(cmd); + } + else + { + if (ApplicationCommandsExtension.DebugEnabled) + ApplicationCommandsExtension.ClientInternal.Logger.LogDebug($"[AC] Command {cmd.Name} changed"); + updateCommands.Add(command.Id, cmd); } } - - return (updateCommands, unchangedCommands); } + + return (updateCommands, unchangedCommands); } }