+ 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 | |||
@Discord.Commands.ModuleBase based class. By applying this attribute, | |||
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 | |||
@@ -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 | |||
you want it to. Note that you should also learn Microsoft's | |||
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. | |||
@@ -16,6 +16,8 @@ DI when writing your modules. | |||
to use in the modules. | |||
3. Pass the service collection into `AddModulesAsync`. | |||
### Example - Setting up Injection | |||
[!code-csharp[IServiceProvider Setup](samples/dependency_map_setup.cs)] | |||
## Usage in Modules | |||
@@ -34,8 +36,10 @@ manner. | |||
> 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. | |||
> 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 |
@@ -99,19 +99,20 @@ For example: | |||
* ...etc. | |||
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 | |||
#### Optional Parameters | |||
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 | |||
To accept a comma-separated list, set the parameter to `params Type[]`. | |||
Should a parameter include spaces, the parameter **must** be | |||
wrapped in quotes. For example, for a command with a parameter | |||
`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 | |||
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 | |||
@@ -216,112 +218,4 @@ 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)] | |||
# 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; | |||
// Dependencies can be injected via the constructor | |||
public ModuleA(DatabaseService database) | |||
public DatabaseModule(DatabaseService 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 | |||
// be injected. | |||
// be injected | |||
[DontInject] | |||
public NotificationService NotificationService { get; set; } | |||
public ModuleB(CommandService commands) | |||
{ | |||
Commands = commands; | |||
} | |||
} | |||
} |
@@ -1,5 +1,7 @@ | |||
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> | |||
{ | |||
@@ -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 | |||
- name: The Command Service | |||
items: | |||
- name: Introduction to Command Service | |||
- name: Introduction | |||
topicUid: Guides.Commands.Intro | |||
- name: TypeReaders | |||
topicUid: Guides.Commands.TypeReaders | |||
- name: Preconditions | |||
topicUid: Guides.Commands.Preconditions | |||
- name: Dependency Injection | |||
topicUid: Guides.Commands.DI | |||
- name: Post-execution Handling | |||