@@ -3,82 +3,152 @@
[Discord.Commands](xref:Discord.Commands) provides an Attribute-based
Command Parser.
### Setup
## Setup
To use Commands, you must create a
[Commands Service](xref:Discord.Commands.CommandService)
and a Command Handler.
To use Commands, you must create a [Commands Service] and a
Command Handler.
Included below is a very bare-bones Command Handler. You can extend
your Command Handler as much as you like, however the below is the
bare minimum.
[!code-csharp[Barebones Command Handler](samples/command_handler.cs)]
The CommandService optionally will 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.
## Commands
[!code-csharp[Command Handler](samples/command_handler.cs)]
In 1.0, Commands are no longer implemented at runtime with a builder
pattern. While a builder pattern may be provided later, commands are
created primarily with attributes.
[Command Service]: xref:Discord.Commands.CommandService
[CommandServiceConfig]: xref:Discord.Commands.CommandServiceConfig
### Basic Structure
## With Attributes
All commands belong to a Module. (See the below section for creating
modules) .
In 1.0, Commands can be defined ahead of time, with attributes, or
at runtime, with builders .
All commands in a module must be defined as a `Task`.
For most bots, ahead-of-time commands should be all you need, and this
is the recommended method of defining commands.
To add parameters to your command, you simply need to add parameters
to the Task that represents the command. You are _not_ required to
accept all arguments as `String`, they will be automatically parsed
into the type you specify for the arument. See the Example Module
for an example of command parameters.
### Modules
## Modules
The first step to creating commands is to create a _module_.
Modules are an organizational pattern that allow 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 being ran in it .
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 being invoked .
**Avoid using long-running code** in your modules whereever possible.
You should **not** be implementing very much logic into your modules;
You should **not** be implementing very much logic into your modules;
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].
To create a module, create a class that inherits from
@Discord.Commands.ModuleBase.
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.
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
### Adding Commands
The next step to creating commands, is actually creating commands.
To create a command, add a method to your module of type `Task`.
Typically, you will want to mark his method as `async`, although it is
not required.
Adding parameters to a command is done by adding parameters to the
parent Task.
For example, to take an integer as an argument, add `int arg`. To take
a user as an argument, 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[]`.
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.
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
### Command Overloads
You may add overloads of your commands, and the command parser will
automatically pick up on it.
If, for whatever reason, you have too commands which are ambiguous to
each other, you may use the @Discord.Commands.PriorityAttribute to
specify which should be tested before the other.
Priority's are sorted in ascending order; the higher priority will be
called first.
### CommandContext
Every command can access the execution context through the [Context]
property on [ModuleBase]. CommandContext allows you to access the
message, channel, guild, and user that the command was invoked from,
as well as the underlying discord client the command was invoked from.
You may need to cast these objects to their Socket counterparts (see
the terminology section).
### Example Module
To reply to messages, you may also invoke [ReplyAsync], instead of
accessing the channel through the [Context] and sending a message.
[!code-csharp[Modules](samples/module.cs)]
### Example Module
At this point, your module should look comparable to this example:
[!code-csharp[Example Module](samples/module.cs)]
#### Loading Modules Automatically
The Command Service can automatically discover all classes in an
Assembly that inherit @Discord.Commands.ModuleBase, and load them.
Assembly that inherit [ModuleBase] , and load them.
To have a module opt-out of auto-loading, pass `autoload: false` in
the Module attribute.
To opt a module out of auto-loading, flag it with
[DontAutoLoadAttribute]
Invoke [CommandService.AddModules] to discover modules and install them.
Invoke [CommandService.AddModulesAsync] to discover modules and
install them.
[CommandService.AddModules]: xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddModules
[DontAutoLoadAttribute]: Discord.Commands.DontAutoLoadAttribute
[CommandService.AddModulesAsync]: xref:Discord_Commands_CommandService#AddModulesAsync_System_Reflection_Assembly_
#### Loading Modules Manually
To manually load a module, invoke [CommandService.AddModule],
To manually load a module, invoke [CommandService.AddModuleAsync ],
by passing in the generic type of your module, and optionally
a dependency map.
[CommandService.AddModule]: xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddModule__1_Discord_Commands_IDependencyMap_
[CommandService.AddModuleAsync ]: xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddModuleAsync__1
### Module Constructors
@@ -87,18 +157,27 @@ that are placed in the constructor must be injected into an
@Discord.Commands.IDependencyMap. Alternatively, you may accept an
IDependencyMap as an argument and extract services yourself.
### Command Groups
### Module Groups
Command Groups allow you to create a module where commands are prefixed.
To create a group, create a new module and flag it 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
>[!NOTE]
>Groups do not _need_ to be modules. Only classes with commands should
>inherit from ModuleBase. If you plan on using a group for strictly
>organizational purposes, there is no reason to make it a module.
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 are modules that reside within another module. Typically,
submodules are used to create nested groups (although not required to
create nested groups).
[!code-csharp[Groups and Submodules](samples/groups.cs)]
## With Builders
[!code-csharp[Groups Sample](samples/groups.cs)]
**TODO**
## Dependency Injection
@@ -143,34 +222,13 @@ can be as complex as you want them to be.
## Bundled Preconditions
@Discord.Commands ships with two built-in preconditions,
@Discord.Commands.RequireContextAttribute and
@Discord.Commands.RequirePermissionAttribute.
### RequireContext
@Discord.Commands.RequireContextAttribute is a precondition that
requires your command to be executed in the specified context.
You may require three different types of context:
* Guild
* DM
* Group
Since these are `Flags`, you may OR them together.
[!code-csharp[RequireContext](samples/require_context.cs)]
Commands ships with four bundled preconditions; you may view their
usages on their API page.
### RequirePermission
@Discord.Commands.RequirePermissionAttribute is a precondition that
allows you to quickly specfiy that a user must poesess a permission
to execute a command.
You may require either a @Discord.GuildPermission or
@Discord.ChannelPermission
[!code-csharp[RequireContext](samples/require_permission.cs)]
- @Discord.Commands.RequireContextAttribute
- @Discord.Commands.RequireOwnerAttribute
- @Discord.Commands.RequireBotPermissionAttribute
- @Discord.Commands.RequireUserPermissionAttribute
## Custom Preconditions
@@ -178,15 +236,20 @@ To write your own preconditions, create a new class that inherits from
@Discord.Commands.PreconditionAttribute
In order for your precondition to function, you will need to override
`CheckPermissions`, which is a `Task<PreconditionResult>`.
[CheckPermissions].
Your IDE should provide an option to fill this in for you.
Return `PreconditionResult.FromSuccess()` if the context met the
required parameters, otherwise return `PreconditionResult.FromError()` ,
Return [PreconditionResult.FromSuccess] if the context met the
required parameters, otherwise return [PreconditionResult.FromError] ,
optionally including an error message.
[!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 allow you to parse different types of arguments in
@@ -194,24 +257,26 @@ your commands.
By default, the following Types are supported arguments:
- string
- bool
- char
- sbyte/byte
- ushort/short
- uint/int
- ulong/long
- float, double, decimal
- DateTime/DateTimeOffset
- IUser/IGuildUser
- string
- DateTime/DateTimeOffset/TimeSpan
- IMessage/IUserMessage
- IChannel/IGuildChannel/ITextChannel/IVoiceChannel/IGroupChannel
- IUser/IGuildUser/IGroupUser
- IRole
- IMessage/IUserMessage
### Creating a Type Readers
To create a TypeReader, create a new class that imports @Discord and
@Discord.Commands. Ensure your class inherits from @Discord.Commands.TypeReader
Next, satisfy the `TypeReader` class by overriding `Task<TypeReaderResult> Read(CommandContext context, string input)` .
Next, satisfy the `TypeReader` class by overriding [Read] .
>[!NOTE]
>In many cases, Visual Studio can fill this in for you, using the
@@ -223,6 +288,8 @@ Finally, return a `TypeReaderResult`. If you were able to successfully
parse the input, return `TypeReaderResult.FromSuccess(parsedInput)`.
Otherwise, return `TypeReaderResult.FromError`.
[Read]: xref:Discord.Commands.TypeReader#Discord_Commands_TypeReader_Read_Discord_Commands_CommandContext_System_String_
#### Sample
[!code-csharp[TypeReaders](samples/typereader.cs)]