From bb9568607839c89cb4f2c61215fb3c62416b8f4f Mon Sep 17 00:00:00 2001 From: james7132 Date: Fri, 10 Feb 2017 22:11:06 +0000 Subject: [PATCH] Update docs to reflect the change. --- docs/guides/commands.md | 199 ++++++++++++----------- docs/guides/samples/dependency_module.cs | 20 ++- 2 files changed, 120 insertions(+), 99 deletions(-) diff --git a/docs/guides/commands.md b/docs/guides/commands.md index 4c5100946..71fdc58e9 100644 --- a/docs/guides/commands.md +++ b/docs/guides/commands.md @@ -5,16 +5,16 @@ ## Setup -To use Commands, you must create a [Commands Service] and a +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 +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. -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 +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. [!code-csharp[Command Handler](samples/command_handler.cs)] @@ -22,38 +22,38 @@ values. [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 +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 +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_. -Modules are an organizational pattern that allow you to write your -commands in different classes, and have them automatically loaded. +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 +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; +**Avoid using long-running code** in your modules whereever possible. +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]. +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 +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 +>[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: @@ -63,36 +63,36 @@ By now, your module should look like this: [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 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 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 +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 +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[]`. +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 +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 +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 @@ -100,52 +100,52 @@ module group - see below). ### Command Overloads -You may add overloads of your commands, and the command parser will +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. +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 +Priority's are sorted in ascending order; the higher priority will be called first. -### CommandContext +### 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. +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. -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. You +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. You will not need to cast them. -To reply to messages, you may also invoke [ReplyAsync], instead of +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 >![WARNING] ->Contexts should **NOT** be mixed! You cannot have one module that +>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)] #### Loading Modules Automatically -The Command Service can automatically discover all classes in an +The Command Service can automatically discover all classes in an Assembly that inherit [ModuleBase], and load them. -To opt a module out of auto-loading, flag it with +To opt a module out of auto-loading, flag it with [DontAutoLoadAttribute] -Invoke [CommandService.AddModulesAsync] to discover modules and +Invoke [CommandService.AddModulesAsync] to discover modules and install them. [DontAutoLoadAttribute]: xref:Discord.Commands.DontAutoLoadAttribute @@ -153,33 +153,38 @@ install them. #### Loading Modules Manually -To manually load a module, invoke [CommandService.AddModuleAsync], -by passing in the generic type of your module, and optionally +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 -Modules are constructed using Dependency Injection. Any parameters -that are placed in the constructor must be injected into an -@Discord.Commands.IDependencyMap. Alternatively, you may accept an +Modules are constructed using Dependency Injection. Any parameters +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. +### Module Properties + +Modules with public settable properties will be have them injected after +construction. + ### Module Groups -Module Groups allow you to create a module where commands are prefixed. -To create a group, flag a module with the +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 +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 +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)] @@ -190,40 +195,46 @@ create nested groups). ## Dependency Injection -The commands service is bundled with a very barebones Dependency -Injection service for your convienence. It is recommended that +The commands service is bundled with a very barebones Dependency +Injection service for your convienence. It is recommended that you use DI when writing your modules. ### Setup -First, you need to create an @Discord.Commands.IDependencyMap. -The library includes @Discord.Commands.DependencyMap to help with -this, however you may create your own IDependencyMap if you wish. +First, you need to create an @Discord.Commands.IDependencyMap. +The library includes @Discord.Commands.DependencyMap to help with +this, however you may create your own IDependencyMap if you wish. -Next, add the dependencies your modules will use to the map. +Next, add the dependencies your modules will use to the map. -Finally, pass the map into the `LoadAssembly` method. +Finally, pass the map into the `LoadAssembly` method. Your modules will automatically be loaded with this dependency map. [!code-csharp[DependencyMap Setup](samples/dependency_map_setup.cs)] ### Usage in Modules -In the constructor of your module, any parameters will be filled in by +In the constructor of your module, any parameters will be filled in by the @Discord.Commands.IDependencyMap you pass into `LoadAssembly`. +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] ->If you accept `CommandService` or `IDependencyMap` as a parameter in -your constructor, these parameters will be filled by the -CommandService the module was loaded from, and the DependencyMap passed -into it, respectively. +>If you accept `CommandService` or `IDependencyMap` as a parameter in +your constructor or as an injectable property, these entries will be filled +by the CommandService the module was loaded from, and the DependencyMap passed +into it, respectively. [!code-csharp[DependencyMap in Modules](samples/dependency_module.cs)] # Preconditions -Preconditions serve as a permissions system for your commands. Keep in -mind, however, that they are not limited to _just_ permissions, and +Preconditions 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] @@ -231,7 +242,7 @@ can be as complex as you want them to be. ## Bundled Preconditions -Commands ships with four bundled preconditions; you may view their +Commands ships with four bundled preconditions; you may view their usages on their API page. - @Discord.Commands.RequireContextAttribute @@ -244,13 +255,13 @@ usages on their API page. 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 +In order for your precondition to function, you will need to override [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)] @@ -261,7 +272,7 @@ optionally including an error message. # Type Readers -Type Readers allow you to parse different types of arguments in +Type Readers allow you to parse different types of arguments in your commands. By default, the following Types are supported arguments: @@ -282,19 +293,19 @@ By default, the following Types are supported arguments: ### Creating a Type Readers -To create a TypeReader, create a new class that imports @Discord and +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 [Read]. >[!NOTE] ->In many cases, Visual Studio can fill this in for you, using the +>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. -Finally, return a `TypeReaderResult`. If you were able to successfully -parse the input, return `TypeReaderResult.FromSuccess(parsedInput)`. +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_ @@ -305,5 +316,5 @@ Otherwise, return `TypeReaderResult.FromError`. ### Installing TypeReaders -TypeReaders are not automatically discovered by the Command Service, +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_). diff --git a/docs/guides/samples/dependency_module.cs b/docs/guides/samples/dependency_module.cs index 2e1d662f8..0d65a1eea 100644 --- a/docs/guides/samples/dependency_module.cs +++ b/docs/guides/samples/dependency_module.cs @@ -6,6 +6,7 @@ public class ModuleA : ModuleBase { private readonly DatabaseService _database; + // Dependencies can be injected via the constructor public ModuleA(DatabaseService database) { _database = database; @@ -20,12 +21,21 @@ public class ModuleA : ModuleBase public class ModuleB { - private CommandService _commands; - private NotificationService _notification; - + + // Public settable properties will be injected + public AnnounceService { get; set; } + + // Public properties without setters will not + public CommandService Commands { get; } + + // Public properties annotated with [DontInject] will not + [DontInject] + public NotificationService { get; set; } + public ModuleB(CommandService commands, IDependencyMap map) { - _commands = commands; + Commands = commands; _notification = map.Get(); } -} \ No newline at end of file + +}