| @@ -1,209 +1,285 @@ | |||
| # The Command Service | |||
| # The Command Service | |||
| >[!WARNING] | |||
| >This article is out of date and has not been rewritten yet. | |||
| Information is not guaranteed to be accurate. | |||
| >This article is out of date and has not been rewritten yet. | |||
| Information is not guaranteed to be accurate. | |||
| [Discord.Commands](xref:Discord.Commands) provides an Attribute-based command parser. | |||
| [Discord.Commands](xref:Discord.Commands) provides an Attribute-based | |||
| command parser. | |||
| ## Setup | |||
| ## Setup | |||
| To use Commands, you must create a [Commands Service] and a Command Handler. | |||
| To use Commands, you must create a [Commands Service] and a Command | |||
| Handler. | |||
| Included below is a very barebone Command Handler. You can extend your Command Handler as much as you like; however, the below is the bare minimum. | |||
| Included below is a very barebone Command Handler. You can extend your | |||
| Command Handler as much as you like; however, the below is the bare | |||
| minimum. | |||
| The `CommandService` will optionally accept a [CommandServiceConfig], which _does_ set a few default values for you. It is recommended to look over the properties in [CommandServiceConfig] and their default values. | |||
| The `CommandService` will optionally accept a [CommandServiceConfig], | |||
| which _does_ set a few default values for you. It is recommended to | |||
| look over the properties in [CommandServiceConfig] and their default | |||
| values. | |||
| [!code-csharp[Command Handler](samples/command_handler.cs)] | |||
| [!code-csharp[Command Handler](samples/command_handler.cs)] | |||
| [Command Service]: xref:Discord.Commands.CommandService | |||
| [CommandServiceConfig]: xref:Discord.Commands.CommandServiceConfig | |||
| [Command Service]: xref:Discord.Commands.CommandService | |||
| [CommandServiceConfig]: xref:Discord.Commands.CommandServiceConfig | |||
| ## With Attributes | |||
| ## With Attributes | |||
| In 1.0, Commands can be defined ahead of time with attributes, or at runtime with builders. | |||
| In 1.0, Commands can be defined ahead of time with attributes, or at | |||
| runtime with builders. | |||
| For most bots, ahead-of-time Commands should be all you need, and this is the recommended method of defining Commands. | |||
| For most bots, ahead-of-time Commands should be all you need, and this | |||
| is the recommended method of defining Commands. | |||
| ### Modules | |||
| ### Modules | |||
| The first step to creating Commands is to create a _module_. | |||
| The first step to creating Commands is to create a _module_. | |||
| A Module is an organizational pattern that allows you to write your Commands in different classes and have them automatically loaded. | |||
| A Module is an organizational pattern that allows you to write your | |||
| Commands in different classes and have them automatically loaded. | |||
| Discord.NET's implementation of Modules is influenced heavily from ASP.NET Core's Controller pattern. This means that the lifetime of a module instance is only as long as the Command is being invoked. | |||
| Discord.NET's implementation of Modules is influenced heavily from | |||
| ASP.NET Core's Controller pattern. This means that the lifetime of a | |||
| module instance is only as long as the Command is being invoked. | |||
| **Avoid using long-running code** in your modules wherever possible. You should **not** be implementing very much logic into your modules; you should outsource to a service for that. | |||
| **Avoid using long-running code** in your modules wherever possible. | |||
| You should **not** be implementing very much logic into your modules; | |||
| you should outsource to a service for that. | |||
| If you are unfamiliar with Inversion of Control, it is recommended to read the MSDN article on [IoC] and [Dependency Injection]. | |||
| If you are unfamiliar with Inversion of Control, it is recommended to | |||
| read the MSDN article on [IoC] and [Dependency Injection]. | |||
| To begin, create a new class somewhere in your project and inherit the class from [ModuleBase]. This class **must** be `public`. | |||
| To begin, create a new class somewhere in your project and inherit the | |||
| class from [ModuleBase]. This class **must** be `public`. | |||
| >[!NOTE] | |||
| >[ModuleBase] is an _abstract_ class, meaning that you may extend it | |||
| >or override it as you see fit. Your module may inherit from any | |||
| >extension of ModuleBase. | |||
| >[!NOTE] | |||
| >[ModuleBase] is an _abstract_ class, meaning that you may extend it | |||
| >or override it as you see fit. Your module may inherit from any | |||
| >extension of ModuleBase. | |||
| By now, your module should look like this: | |||
| [!code-csharp[Empty Module](samples/empty-module.cs)] | |||
| By now, your module should look like this: | |||
| [!code-csharp[Empty Module](samples/empty-module.cs)] | |||
| [IoC]: https://msdn.microsoft.com/en-us/library/ff921087.aspx | |||
| [Dependency Injection]: https://msdn.microsoft.com/en-us/library/ff921152.aspx | |||
| [ModuleBase]: xref:Discord.Commands.ModuleBase`1 | |||
| [IoC]: https://msdn.microsoft.com/en-us/library/ff921087.aspx | |||
| [Dependency Injection]: https://msdn.microsoft.com/en-us/library/ff921152.aspx | |||
| [ModuleBase]: xref:Discord.Commands.ModuleBase`1 | |||
| ### Adding Commands | |||
| ### Adding Commands | |||
| The next step to creating Commands is actually creating the Commands. | |||
| The next step to creating Commands is actually creating the Commands. | |||
| To create a Command, add a method to your module of type `Task`. Typically, you will want to mark this method as `async`, although it is not required. | |||
| To create a Command, add a method to your module of type `Task`. | |||
| Typically, you will want to mark this method as `async`, although it | |||
| is not required. | |||
| Adding parameters to a Command is done by adding parameters to the parent Task. | |||
| Adding parameters to a Command is done by adding parameters to the | |||
| parent Task. | |||
| For example, to take an integer as an argument from the user, add `int arg`; to take a user as an argument from the user, add `IUser user`. In 1.0, a Command can accept nearly any type of argument; a full list of types that are parsed by default can be found in the below section on _Type Readers_. | |||
| For example, to take an integer as an argument from the user, add `int | |||
| arg`; to take a user as an argument from the user, add `IUser user`. | |||
| In 1.0, a Command can accept nearly any type of argument; a full list | |||
| of types that are parsed by default can be found in the below section | |||
| on _Type Readers_. | |||
| Parameters, by default, are always required. To make a parameter optional, give it a default value. To accept a comma-separated list, set the parameter to `params Type[]`. | |||
| Parameters, by default, are always required. To make a parameter | |||
| optional, give it a default value. To accept a comma-separated list, | |||
| set the parameter to `params Type[]`. | |||
| Should a parameter include spaces, it **must** be wrapped in quotes. For example, for a Command with a parameter `string food`, you would execute it with `!favoritefood "Key Lime Pie"`. | |||
| Should a parameter include spaces, it **must** be wrapped in quotes. | |||
| For example, for a Command with a parameter `string food`, you would | |||
| execute it with `!favoritefood "Key Lime Pie"`. | |||
| If you would like a parameter to parse until the end of a Command, flag the parameter with the [RemainderAttribute]. This will allow a user to invoke a Command without wrapping a parameter in quotes. | |||
| If you would like a parameter to parse until the end of a Command, | |||
| flag the parameter with the [RemainderAttribute]. This will allow a | |||
| user to invoke a Command without wrapping a parameter in quotes. | |||
| Finally, flag your Command with the [CommandAttribute] (you must specify a name for this Command, except for when it is part of a Module Group - see below). | |||
| Finally, flag your Command with the [CommandAttribute] (you must | |||
| specify a name for this Command, except for when it is part of a | |||
| Module Group - see below). | |||
| [RemainderAttribute]: xref:Discord.Commands.RemainderAttribute | |||
| [CommandAttribute]: xref:Discord.Commands.CommandAttribute | |||
| [RemainderAttribute]: xref:Discord.Commands.RemainderAttribute | |||
| [CommandAttribute]: xref:Discord.Commands.CommandAttribute | |||
| ### Command Overloads | |||
| ### Command Overloads | |||
| You may add overloads to your Commands, and the Command parser will automatically pick up on it. | |||
| You may add overloads to your Commands, and the Command parser will | |||
| automatically pick up on it. | |||
| If for whatever reason, you have two Commands which are ambiguous to each other, you may use the @Discord.Commands.PriorityAttribute to specify which should be tested before the other. | |||
| If for whatever reason, you have two Commands which are ambiguous to | |||
| each other, you may use the @Discord.Commands.PriorityAttribute to | |||
| specify which should be tested before the other. | |||
| The `Priority` attributes are sorted in ascending order; the higher priority will be called first. | |||
| The `Priority` attributes are sorted in ascending order; the higher | |||
| priority will be called first. | |||
| ### Command Context | |||
| ### Command Context | |||
| Every Command can access the execution context through the [Context] property on [ModuleBase]. `ICommandContext` allows you to access the message, channel, guild, and user that the Command was invoked from, as well as the underlying Discord client that the Command was invoked from. | |||
| Every Command can access the execution context through the [Context] | |||
| property on [ModuleBase]. `ICommandContext` allows you to access the | |||
| message, channel, guild, and user that the Command was invoked from, | |||
| as well as the underlying Discord client that the Command was invoked | |||
| from. | |||
| Different types of Contexts may be specified using the generic variant of [ModuleBase]. When using a [SocketCommandContext], for example, the properties on this context will already be Socket entities, so you will not need to cast them. | |||
| Different types of Contexts may be specified using the generic variant | |||
| of [ModuleBase]. When using a [SocketCommandContext], for example, the | |||
| properties on this context will already be Socket entities, so you | |||
| will not need to cast them. | |||
| To reply to messages, you may also invoke [ReplyAsync], instead of accessing the channel through the [Context] and sending a message. | |||
| To reply to messages, you may also invoke [ReplyAsync], instead of | |||
| accessing the channel through the [Context] and sending a message. | |||
| [Context]: xref:Discord.Commands.ModuleBase`1#Discord_Commands_ModuleBase_1_Context | |||
| [SocketCommandContext]: xref:Discord.Commands.SocketCommandContext | |||
| [SocketCommandContext]: xref:Discord.Commands.SocketCommandContext | |||
| >![WARNING] | |||
| >Contexts should **NOT** be mixed! You cannot have one module that | |||
| >uses CommandContext and another that uses SocketCommandContext. | |||
| >![WARNING] | |||
| >Contexts should **NOT** be mixed! You cannot have one module that | |||
| >uses CommandContext and another that uses SocketCommandContext. | |||
| ### Example Module | |||
| ### Example Module | |||
| At this point, your module should look comparable to this example: | |||
| [!code-csharp[Example Module](samples/module.cs)] | |||
| At this point, your module should look comparable to this example: | |||
| [!code-csharp[Example Module](samples/module.cs)] | |||
| #### Loading Modules Automatically | |||
| #### Loading Modules Automatically | |||
| The Command Service can automatically discover all classes in an Assembly that inherits [ModuleBase] and load them. | |||
| The Command Service can automatically discover all classes in an | |||
| Assembly that inherits [ModuleBase] and load them. | |||
| To opt a module out of auto-loading, flag it with [DontAutoLoadAttribute]. | |||
| To opt a module out of auto-loading, flag it with | |||
| [DontAutoLoadAttribute]. | |||
| Invoke [CommandService.AddModulesAsync] to discover modules and install them. | |||
| Invoke [CommandService.AddModulesAsync] to discover modules and | |||
| install them. | |||
| [DontAutoLoadAttribute]: xref:Discord.Commands.DontAutoLoadAttribute | |||
| [CommandService.AddModulesAsync]: xref:Discord_Commands_CommandService#Discord_Commands_CommandService_AddModulesAsync_Assembly_ | |||
| [DontAutoLoadAttribute]: xref:Discord.Commands.DontAutoLoadAttribute | |||
| [CommandService.AddModulesAsync]: xref:Discord_Commands_CommandService#Discord_Commands_CommandService_AddModulesAsync_Assembly_ | |||
| #### Loading Modules Manually | |||
| #### Loading Modules Manually | |||
| To manually load a module, invoke [CommandService.AddModuleAsync], by passing in the generic type of your module and optionally, a dependency map. | |||
| To manually load a module, invoke [CommandService.AddModuleAsync], by | |||
| passing in the generic type of your module and optionally, a | |||
| dependency map. | |||
| [CommandService.AddModuleAsync]: xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddModuleAsync__1 | |||
| ### Module Constructors | |||
| ### Module Constructors | |||
| Modules are constructed using Dependency Injection. Any parameters that are placed in the Module's constructor must be injected into an @System.IServiceProvider first. Alternatively, you may accept an `IServiceProvider` as an argument and extract services yourself. | |||
| Modules are constructed using Dependency Injection. Any parameters | |||
| that are placed in the Module's constructor must be injected into an | |||
| @System.IServiceProvider first. Alternatively, you may accept an | |||
| `IServiceProvider` as an argument and extract services yourself. | |||
| ### Module Properties | |||
| ### Module Properties | |||
| Modules with `public` settable properties will have the dependencies injected after the construction of the Module. | |||
| Modules with `public` settable properties will have the dependencies | |||
| injected after the construction of the Module. | |||
| ### Module Groups | |||
| ### Module Groups | |||
| Module Groups allow you to create a module where Commands are prefixed. To create a group, flag a module with the @Discord.Commands.GroupAttribute. | |||
| Module Groups allow you to create a module where Commands are | |||
| prefixed. To create a group, flag a module with the | |||
| @Discord.Commands.GroupAttribute. | |||
| Module groups also allow you to create **nameless Commands**, where the [CommandAttribute] is configured with no name. In this case, the Command will inherit the name of the group it belongs to. | |||
| Module groups also allow you to create **nameless Commands**, where | |||
| the [CommandAttribute] is configured with no name. In this case, the | |||
| Command will inherit the name of the group it belongs to. | |||
| ### Submodules | |||
| ### Submodules | |||
| Submodules are Modules that reside within another one. Typically, submodules are used to create nested groups (although not required to create nested groups). | |||
| Submodules are Modules that reside within another one. Typically, | |||
| submodules are used to create nested groups (although not required to | |||
| create nested groups). | |||
| [!code-csharp[Groups and Submodules](samples/groups.cs)] | |||
| [!code-csharp[Groups and Submodules](samples/groups.cs)] | |||
| ## With Builders | |||
| ## With Builders | |||
| **TODO** | |||
| **TODO** | |||
| ## Dependency Injection | |||
| ## Dependency Injection | |||
| The Command Service is bundled with a very barebone Dependency Injection service for your convenience. It is recommended that you use DI when writing your modules. | |||
| The Command Service is bundled with a very barebone Dependency | |||
| Injection service for your convenience. It is recommended that you use | |||
| DI when writing your modules. | |||
| ### Setup | |||
| ### Setup | |||
| First, you need to create an @System.IServiceProvider; you may create your own one if you wish. | |||
| First, you need to create an @System.IServiceProvider; you may create | |||
| your own one if you wish. | |||
| Next, add the dependencies that your modules will use to the map. | |||
| Next, add the dependencies that your modules will use to the map. | |||
| Finally, pass the map into the `LoadAssembly` method. Your modules will be automatically loaded with this dependency map. | |||
| Finally, pass the map into the `LoadAssembly` method. Your modules | |||
| will be automatically loaded with this dependency map. | |||
| [!code-csharp[IServiceProvider Setup](samples/dependency_map_setup.cs)] | |||
| ### Usage in Modules | |||
| ### Usage in Modules | |||
| In the constructor of your Module, any parameters will be filled in by the @System.IServiceProvider that you've passed into `LoadAssembly`. | |||
| In the constructor of your Module, any parameters will be filled in by | |||
| the @System.IServiceProvider that you've passed into `LoadAssembly`. | |||
| Any publicly settable properties will also be filled in the same manner. | |||
| Any publicly settable properties will also be filled in the same | |||
| manner. | |||
| >[!NOTE] | |||
| > Annotating a property with the [DontInject] attribute will prevent it from being injected. | |||
| >[!NOTE] | |||
| > Annotating a property with the [DontInject] attribute will | |||
| prevent it from being injected. | |||
| >[!NOTE] | |||
| >If you accept `CommandService` or `IServiceProvider` as a parameter in your constructor, or as an injectable property, these entries will be filled by the `CommandService` that the Module is loaded from, and the `ServiceProvider` that is passed into it respectively. | |||
| >[!NOTE] | |||
| >If you accept `CommandService` or `IServiceProvider` as a parameter | |||
| in your constructor, or as an injectable property, these entries will | |||
| be filled by the `CommandService` that the Module is loaded from, and | |||
| the `ServiceProvider` that is passed into it respectively. | |||
| [!code-csharp[ServiceProvider in Modules](samples/dependency_module.cs)] | |||
| [!code-csharp[ServiceProvider in Modules](samples/dependency_module.cs)] | |||
| # Preconditions | |||
| # Preconditions | |||
| Precondition serve as a permissions system for your Commands. Keep in mind, however, that they are not limited to _just_ permissions and can be as complex as you want them to be. | |||
| Precondition serve as a permissions system for your Commands. Keep in | |||
| mind, however, that they are not limited to _just_ permissions and can | |||
| be as complex as you want them to be. | |||
| >[!NOTE] | |||
| >Preconditions can be applied to Modules, Groups, or Commands. | |||
| >[!NOTE] | |||
| >Preconditions can be applied to Modules, Groups, or Commands. | |||
| ## Bundled Preconditions | |||
| ## Bundled Preconditions | |||
| Commands ship with four bundled preconditions; you may view their usages on their respective API pages. | |||
| Commands ship with four bundled preconditions; you may view their | |||
| usages on their respective API pages. | |||
| - @Discord.Commands.RequireContextAttribute | |||
| - @Discord.Commands.RequireOwnerAttribute | |||
| - @Discord.Commands.RequireBotPermissionAttribute | |||
| - @Discord.Commands.RequireUserPermissionAttribute | |||
| ## Custom Preconditions | |||
| ## Custom Preconditions | |||
| To write your own precondition, create a new class that inherits from @Discord.Commands.PreconditionAttribute. | |||
| To write your own precondition, create a new class that inherits from | |||
| @Discord.Commands.PreconditionAttribute. | |||
| In order for your precondition to function, you will need to override the [CheckPermissions] method. | |||
| In order for your precondition to function, you will need to override | |||
| the [CheckPermissions] method. | |||
| Your IDE should provide an option to fill this in for you. | |||
| Your IDE should provide an option to fill this in for you. | |||
| Return [PreconditionResult.FromSuccess] if the context meets the required parameters, otherwise return [PreconditionResult.FromError] and optionally include an error message. | |||
| Return [PreconditionResult.FromSuccess] if the context meets the | |||
| required parameters, otherwise return [PreconditionResult.FromError] | |||
| and optionally include an error message. | |||
| [!code-csharp[Custom Precondition](samples/require_owner.cs)] | |||
| [!code-csharp[Custom Precondition](samples/require_owner.cs)] | |||
| [CheckPermissions]: xref:Discord.Commands.PreconditionAttribute#Discord_Commands_PreconditionAttribute_CheckPermissions_Discord_Commands_CommandContext_Discord_Commands_CommandInfo_Discord_Commands_IDependencyMap_ | |||
| [PreconditionResult.FromSuccess]: xref:Discord.Commands.PreconditionResult#Discord_Commands_PreconditionResult_FromSuccess | |||
| [PreconditionResult.FromError]: xref:Discord.Commands.PreconditionResult#Discord_Commands_PreconditionResult_FromError_System_String_ | |||
| # Type Readers | |||
| # Type Readers | |||
| Type Readers allow you to parse different types of arguments in your Commands. | |||
| Type Readers allow you to parse different types of arguments in | |||
| your commands. | |||
| By default, the following Types are supported arguments: | |||
| @@ -221,29 +297,35 @@ By default, the following Types are supported arguments: | |||
| - IUser/IGuildUser/IGroupUser | |||
| - IRole | |||
| ### Creating a Type Readers | |||
| ### Creating a Type Readers | |||
| To create a `TypeReader`, create a new class that imports @Discord and @Discord.Commands and ensure the class inherits from @Discord.Commands.TypeReader. | |||
| To create a `TypeReader`, create a new class that imports @Discord and | |||
| @Discord.Commands and ensure the class inherits from | |||
| @Discord.Commands.TypeReader. | |||
| Next, satisfy the `TypeReader` class by overriding the [Read] method. | |||
| Next, satisfy the `TypeReader` class by overriding the [Read] method. | |||
| >[!NOTE] | |||
| >In many cases, Visual Studio can fill this in for you using the | |||
| >In many cases, Visual Studio can fill this in for you, using the | |||
| >"Implement Abstract Class" IntelliSense hint. | |||
| Inside this task, add whatever logic you need to parse the input string. | |||
| Inside this task, add whatever logic you need to parse the input | |||
| string. | |||
| Finally, return a `TypeReaderResult`. If you were able to successfully | |||
| parse the input, return `TypeReaderResult.FromSuccess(parsedInput)`, otherwise, return `TypeReaderResult.FromError` and optionally include an error message. | |||
| Finally, return a `TypeReaderResult`. If you were able to successfully | |||
| parse the input, return `TypeReaderResult.FromSuccess(parsedInput)`, | |||
| otherwise, return `TypeReaderResult.FromError` and optionally include | |||
| an error message. | |||
| [Read]: xref:Discord.Commands.TypeReader#Discord_Commands_TypeReader_Read_Discord_Commands_CommandContext_System_String_ | |||
| #### Sample | |||
| #### Sample | |||
| [!code-csharp[TypeReaders](samples/typereader.cs)] | |||
| [!code-csharp[TypeReaders](samples/typereader.cs)] | |||
| ### Installing TypeReaders | |||
| ### Installing TypeReaders | |||
| TypeReaders are not automatically discovered by the Command Service and must be explicitly added. | |||
| TypeReaders are not automatically discovered by the Command Service | |||
| and must be explicitly added. | |||
| To install a TypeReader, invoke [CommandService.AddTypeReader](xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddTypeReader__1_Discord_Commands_TypeReader_). | |||