+ 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 | |||