@@ -1,282 +1,282 @@
# 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,
**Avoid using long-running code** in your modules wherever possible.
You should **not** be implementing very much logic into your modules,
instead, 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.
@@ -297,35 +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
>"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_).