+ Each section has gone through a thorough proof-reading and cleanup + This should help with readability.pull/988/head
@@ -6,8 +6,7 @@ remarks: *content | |||||
The attribute can be applied to a public settable property inside a | The attribute can be applied to a public settable property inside a | ||||
@Discord.Commands.ModuleBase based class. By applying this attribute, | @Discord.Commands.ModuleBase based class. By applying this attribute, | ||||
the marked property will not be automatically injected of the | the marked property will not be automatically injected of the | ||||
dependency. See [Dependency Injection](xref:Guides.Commands.Intro#dependency-injection) | |||||
to learn more. | |||||
dependency. See @Guides.Commands.DI to learn more. | |||||
--- | --- | ||||
uid: Discord.Commands.DontInjectAttribute | uid: Discord.Commands.DontInjectAttribute | ||||
@@ -57,7 +57,7 @@ persist throughout execution. Think of it like a chest that holds | |||||
whatever you throw at it that won't be affected by anything unless | whatever you throw at it that won't be affected by anything unless | ||||
you want it to. Note that you should also learn Microsoft's | you want it to. Note that you should also learn Microsoft's | ||||
implementation of [Dependency Injection] \([video]) before proceeding, as well | implementation of [Dependency Injection] \([video]) before proceeding, as well | ||||
as how it works in [Discord.Net](xref:Guides.Commands.Intro#usage-in-modules). | |||||
as how it works in [Discord.Net](xref:Guides.Commands.DI#usage-in-modules). | |||||
A brief example of service and dependency injection can be seen below. | A brief example of service and dependency injection can be seen below. | ||||
@@ -16,6 +16,8 @@ DI when writing your modules. | |||||
to use in the modules. | to use in the modules. | ||||
3. Pass the service collection into `AddModulesAsync`. | 3. Pass the service collection into `AddModulesAsync`. | ||||
### Example - Setting up Injection | |||||
[!code-csharp[IServiceProvider Setup](samples/dependency_map_setup.cs)] | [!code-csharp[IServiceProvider Setup](samples/dependency_map_setup.cs)] | ||||
## Usage in Modules | ## Usage in Modules | ||||
@@ -34,8 +36,10 @@ manner. | |||||
> If you accept `CommandService` or `IServiceProvider` as a parameter | > If you accept `CommandService` or `IServiceProvider` as a parameter | ||||
> in your constructor or as an injectable property, these entries will | > in your constructor or as an injectable property, these entries will | ||||
> be filled by the `CommandService` that the module is loaded from and | > be filled by the `CommandService` that the module is loaded from and | ||||
> the `ServiceProvider` that is passed into it respectively. | |||||
> the `IServiceProvider` that is passed into it respectively. | |||||
### Example - Injection in Modules | |||||
[!code-csharp[ServiceProvider in Modules](samples/dependency_module.cs)] | |||||
[!code-csharp[IServiceProvider in Modules](samples/dependency_module.cs)] | |||||
[DontInjectAttribute]: xref:Discord.Commands.DontInjectAttribute | [DontInjectAttribute]: xref:Discord.Commands.DontInjectAttribute |
@@ -99,19 +99,20 @@ For example: | |||||
* ...etc. | * ...etc. | ||||
Starting from 1.0, a command can accept nearly any type of argument; | Starting from 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](#type-readers). | |||||
a full list of types that are parsed by default can | |||||
be found in @Guides.Commands.TypeReaders. | |||||
[CommandAttribute]: xref:Discord.Commands.CommandAttribute | [CommandAttribute]: xref:Discord.Commands.CommandAttribute | ||||
#### Optional Parameters | #### Optional Parameters | ||||
Parameters, by default, are always required. To make a parameter | Parameters, by default, are always required. To make a parameter | ||||
optional, give it a default value (i.e. `int num = 0`). To accept a comma-separated list, | |||||
set the parameter to `params Type[]`. | |||||
optional, give it a default value (i.e. `int num = 0`). | |||||
#### Parameters with Spaces | #### Parameters with Spaces | ||||
To accept a comma-separated list, set the parameter to `params Type[]`. | |||||
Should a parameter include spaces, the parameter **must** be | Should a parameter include spaces, the parameter **must** be | ||||
wrapped in quotes. For example, for a command with a parameter | wrapped in quotes. For example, for a command with a parameter | ||||
`string food`, you would execute it with | `string food`, you would execute it with | ||||
@@ -198,7 +199,8 @@ that are placed in the Module's constructor must be injected into an | |||||
### Module Properties | ### Module Properties | ||||
Modules with `public` settable properties will have the dependencies | Modules with `public` settable properties will have the dependencies | ||||
injected after the construction of the Module. | |||||
injected after the construction of the module. See @Guides.Commands.DI | |||||
to learn more. | |||||
### Module Groups | ### Module Groups | ||||
@@ -216,112 +218,4 @@ Submodules are "modules" that reside within another one. Typically, | |||||
submodules are used to create nested groups (although not required to | submodules are used to create nested groups (although not required to | ||||
create nested groups). | create nested groups). | ||||
[!code-csharp[Groups and Submodules](samples/groups.cs)] | |||||
# 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. | |||||
> [!NOTE] | |||||
> There are two types of Preconditions. | |||||
> [PreconditionAttribute] can be applied to Modules, Groups, or Commands; | |||||
> [ParameterPreconditionAttribute] can be applied to Parameters. | |||||
[PreconditionAttribute]: xref:Discord.Commands.PreconditionAttribute | |||||
[ParameterPreconditionAttribute]: xref:Discord.Commands.ParameterPreconditionAttribute | |||||
## Bundled Preconditions | |||||
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 | |||||
* @Discord.Commands.RequireNsfwAttribute | |||||
## Custom Preconditions | |||||
To write your own Precondition, create a new class that inherits from | |||||
either [PreconditionAttribute] or [ParameterPreconditionAttribute] | |||||
depending on your use. | |||||
In order for your Precondition to function, you will need to override | |||||
the [CheckPermissionsAsync] method. | |||||
Your IDE should provide an option to fill this in for you. | |||||
If the context meets the required parameters, return | |||||
[PreconditionResult.FromSuccess], otherwise return | |||||
[PreconditionResult.FromError] and include an error message if | |||||
necessary. | |||||
[!code-csharp[Custom Precondition](samples/require_owner.cs)] | |||||
[CheckPermissionsAsync]: xref:Discord.Commands.PreconditionAttribute.CheckPermissionsAsync* | |||||
[PreconditionResult.FromSuccess]: xref:Discord.Commands.PreconditionResult.FromSuccess* | |||||
[PreconditionResult.FromError]: xref:Discord.Commands.PreconditionResult.FromError* | |||||
# Type Readers | |||||
Type Readers allow you to parse different types of arguments in | |||||
your commands. | |||||
By default, the following Types are supported arguments: | |||||
* `bool` | |||||
* `char` | |||||
* `sbyte`/`byte` | |||||
* `ushort`/`short` | |||||
* `uint`/`int` | |||||
* `ulong`/`long` | |||||
* `float`, `double`, `decimal` | |||||
* `string` | |||||
* `DateTime`/`DateTimeOffset`/`TimeSpan` | |||||
* `Nullable<T>` where applicible | |||||
* Any implementation of `IChannel`/`IMessage`/`IUser`/`IRole` | |||||
## 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. | |||||
Next, satisfy the `TypeReader` class by overriding the [ReadAsync] 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. | |||||
If you are able to successfully parse the input, return | |||||
[TypeReaderResult.FromSuccess] with the parsed input, otherwise return | |||||
[TypeReaderResult.FromError] and include an error message if | |||||
necessary. | |||||
[TypeReaderResult]: xref:Discord.Commands.TypeReaderResult | |||||
[TypeReaderResult.FromSuccess]: xref:Discord.Commands.TypeReaderResult.FromSuccess* | |||||
[TypeReaderResult.FromError]: xref:Discord.Commands.TypeReaderResult.FromError* | |||||
[ReadAsync]: xref:Discord.Commands.TypeReader.ReadAsync* | |||||
## Registering TypeReaders | |||||
TypeReaders are not automatically discovered by the Command Service | |||||
and must be explicitly added. | |||||
To register a TypeReader, invoke [CommandService.AddTypeReader]. | |||||
> [!WARNING] | |||||
> TypeReaders must be added prior to module discovery, otherwise your | |||||
> TypeReaders may not work! | |||||
[CommandService.AddTypeReader]: xref:Discord.Commands.CommandService.AddTypeReader* | |||||
### Sample | |||||
[!code-csharp[TypeReaders](samples/typereader.cs)] | |||||
[!code-csharp[Groups and Submodules](samples/groups.cs)] |
@@ -0,0 +1,53 @@ | |||||
--- | |||||
uid: Guides.Commands.Preconditions | |||||
title: 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. | |||||
There are two types of Preconditions you can use: | |||||
* [PreconditionAttribute] can be applied to Modules, Groups, or Commands. | |||||
* [ParameterPreconditionAttribute] can be applied to Parameters. | |||||
You may visit their respective API documentation to find out more. | |||||
[PreconditionAttribute]: xref:Discord.Commands.PreconditionAttribute | |||||
[ParameterPreconditionAttribute]: xref:Discord.Commands.ParameterPreconditionAttribute | |||||
## Bundled Preconditions | |||||
@Discord.Commands ship with several bundled Preconditions; you may | |||||
view their usages on their respective API pages. | |||||
* @Discord.Commands.RequireContextAttribute | |||||
* @Discord.Commands.RequireOwnerAttribute | |||||
* @Discord.Commands.RequireBotPermissionAttribute | |||||
* @Discord.Commands.RequireUserPermissionAttribute | |||||
* @Discord.Commands.RequireNsfwAttribute | |||||
## Custom Preconditions | |||||
To write your own Precondition, create a new class that inherits from | |||||
either [PreconditionAttribute] or [ParameterPreconditionAttribute] | |||||
depending on your use. | |||||
In order for your Precondition to function, you will need to override | |||||
the [CheckPermissionsAsync] method. | |||||
Your IDE should provide an option to fill this in for you. | |||||
If the context meets the required parameters, return | |||||
[PreconditionResult.FromSuccess], otherwise return | |||||
[PreconditionResult.FromError] and include an error message if | |||||
necessary. | |||||
[!code-csharp[Custom Precondition](samples/require_owner.cs)] | |||||
[CheckPermissionsAsync]: xref:Discord.Commands.PreconditionAttribute.CheckPermissionsAsync* | |||||
[PreconditionResult.FromSuccess]: xref:Discord.Commands.PreconditionResult.FromSuccess* | |||||
[PreconditionResult.FromError]: xref:Discord.Commands.PreconditionResult.FromError* |
@@ -1,40 +1,30 @@ | |||||
using Discord; | |||||
using Discord.Commands; | |||||
using Discord.WebSocket; | |||||
public class ModuleA : ModuleBase<SocketCommandContext> | |||||
public class DatabaseModule : ModuleBase<SocketCommandContext> | |||||
{ | { | ||||
private readonly DatabaseService _database; | private readonly DatabaseService _database; | ||||
// Dependencies can be injected via the constructor | // Dependencies can be injected via the constructor | ||||
public ModuleA(DatabaseService database) | |||||
public DatabaseModule(DatabaseService database) | |||||
{ | { | ||||
_database = database; | _database = database; | ||||
} | } | ||||
public async Task ReadFromDb() | |||||
[Command("read")] | |||||
public async Task ReadFromDbAsync() | |||||
{ | { | ||||
var x = _database.getX(); | |||||
await ReplyAsync(x); | |||||
await ReplyAsync(_database.GetData()); | |||||
} | } | ||||
} | } | ||||
public class ModuleB : ModuleBase<SocketCommandContext> | |||||
public class MixModule : ModuleBase<SocketCommandContext> | |||||
{ | { | ||||
// Public settable properties will be injected. | |||||
public AnnounceService Announce { get; set; } | |||||
// Public settable properties will be injected | |||||
public AnnounceService AnnounceService { get; set; } | |||||
// Public properties without setters will not be injected. | |||||
public CommandService Commands { get; } | |||||
// Public properties without setters will not be injected | |||||
public ImageService ImageService { get; } | |||||
// Public properties annotated with [DontInject] will not | // Public properties annotated with [DontInject] will not | ||||
// be injected. | |||||
// be injected | |||||
[DontInject] | [DontInject] | ||||
public NotificationService NotificationService { get; set; } | public NotificationService NotificationService { get; set; } | ||||
public ModuleB(CommandService commands) | |||||
{ | |||||
Commands = commands; | |||||
} | |||||
} | |||||
} |
@@ -1,5 +1,7 @@ | |||||
using Discord.Commands; | using Discord.Commands; | ||||
// Keep in mind your module **must** be public and inherit ModuleBase. | |||||
// If it isn't, it will not be discovered by AddModulesAsync! | |||||
public class InfoModule : ModuleBase<SocketCommandContext> | public class InfoModule : ModuleBase<SocketCommandContext> | ||||
{ | { | ||||
@@ -0,0 +1,29 @@ | |||||
public class CommandHandler | |||||
{ | |||||
private readonly CommandService _commands; | |||||
private readonly DiscordSocketClient _client; | |||||
private readonly IServiceProvider _services; | |||||
public CommandHandler(CommandService commands, DiscordSocketClient client, IServiceProvider services) | |||||
{ | |||||
_commands = commands; | |||||
_client = client; | |||||
_services = services; | |||||
} | |||||
public async Task SetupAsync() | |||||
{ | |||||
_client.MessageReceived += CommandHandleAsync; | |||||
// Add BooleanTypeReader to type read for the type "bool" | |||||
_commands.AddTypeReader(typeof(bool), new BooleanTypeReader()); | |||||
// Then register the modules | |||||
await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services); | |||||
} | |||||
public async Task CommandHandleAsync(SocketMessage msg) | |||||
{ | |||||
// ... | |||||
} | |||||
} |
@@ -0,0 +1,69 @@ | |||||
--- | |||||
uid: Guides.Commands.TypeReaders | |||||
title: Type Readers | |||||
--- | |||||
# Type Readers | |||||
Type Readers allow you to parse different types of arguments in | |||||
your commands. | |||||
By default, the following Types are supported arguments: | |||||
* `bool` | |||||
* `char` | |||||
* `sbyte`/`byte` | |||||
* `ushort`/`short` | |||||
* `uint`/`int` | |||||
* `ulong`/`long` | |||||
* `float`, `double`, `decimal` | |||||
* `string` | |||||
* `DateTime`/`DateTimeOffset`/`TimeSpan` | |||||
* `Nullable<T>` where applicible | |||||
* Any implementation of `IChannel`/`IMessage`/`IUser`/`IRole` | |||||
## Creating a Type Reader | |||||
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 [ReadAsync] method. | |||||
Inside this Task, add whatever logic you need to parse the input | |||||
string. | |||||
If you are able to successfully parse the input, return | |||||
[TypeReaderResult.FromSuccess] with the parsed input, otherwise return | |||||
[TypeReaderResult.FromError] and include an error message if | |||||
necessary. | |||||
> [!NOTE] | |||||
> Visual Studio can help you implement missing members | |||||
> from the abstract class by using the "Implement Abstract Class" | |||||
> IntelliSense hint. | |||||
[TypeReaderResult]: xref:Discord.Commands.TypeReaderResult | |||||
[TypeReaderResult.FromSuccess]: xref:Discord.Commands.TypeReaderResult.FromSuccess* | |||||
[TypeReaderResult.FromError]: xref:Discord.Commands.TypeReaderResult.FromError* | |||||
[ReadAsync]: xref:Discord.Commands.TypeReader.ReadAsync* | |||||
### Example - Creating a Type Reader | |||||
[!code-csharp[TypeReaders](samples/typereader.cs)] | |||||
## Registering a Type Reader | |||||
TypeReaders are not automatically discovered by the Command Service | |||||
and must be explicitly added. | |||||
To register a TypeReader, invoke [CommandService.AddTypeReader]. | |||||
> [!IMPORTANT] | |||||
> TypeReaders must be added prior to module discovery, otherwise your | |||||
> TypeReaders may not work! | |||||
[CommandService.AddTypeReader]: xref:Discord.Commands.CommandService.AddTypeReader* | |||||
### Example - Adding a Type Reader | |||||
[!code-csharp[Adding TypeReaders](samples/typereader-register.cs)] |
@@ -20,8 +20,12 @@ | |||||
topicUid: Guides.Concepts.Entities | topicUid: Guides.Concepts.Entities | ||||
- name: The Command Service | - name: The Command Service | ||||
items: | items: | ||||
- name: Introduction to Command Service | |||||
- name: Introduction | |||||
topicUid: Guides.Commands.Intro | topicUid: Guides.Commands.Intro | ||||
- name: TypeReaders | |||||
topicUid: Guides.Commands.TypeReaders | |||||
- name: Preconditions | |||||
topicUid: Guides.Commands.Preconditions | |||||
- name: Dependency Injection | - name: Dependency Injection | ||||
topicUid: Guides.Commands.DI | topicUid: Guides.Commands.DI | ||||
- name: Post-execution Handling | - name: Post-execution Handling | ||||