Improve library documentation (#826)
* Improve the Command Service documentation
The following changes have been added to this PR:
• Fix minor grammatical errors.
• Capitalize terms such as Commands, Modules and such, as the context is specific to the lib.
• Wrap methods and properties in code blocks.
The docs page currently has several issues that remains to be fixed.
1.
```md
>[!WARNING]
>This article is out of date and has not been rewritten yet.
Information is not guaranteed to be accurate.
```
The docs doesn't necessarily seem "out of date" as the warning claims. The basics seem pretty relevant to the latest version of the lib.
2.
>“To manually load a module, invoke [CommandService.AddModuleAsync], by passing in the generic type of your module and optionally, a dependency map.”
The latter part of the sentence seems off. Where should the user pass the dependency map to? It seems to suggest that `AddModuleAsync` has an argument to pass the dependency to. If it is referring to `AddModuleAsync(Type type)`, then I feel like it should be clarified here - or perhaps change the wording of the sentence.
3.
>“First, you need to create an @System.IServiceProvider You may create your own IServiceProvider if you wish.”
Any mention of @System.IServiceProvider is currently broken on the docs.
4.
>“Submodules are Modules that reside within another one. Typically, submodules are used to create nested groups (although not required to create nested groups).”
Clarification on the part after "although?"
5.
>“Finally, pass the map into the LoadAssembly method. Your modules will automatically be loaded with this dependency map.”
Where is this `LoadAssembly` method?
6.
```md
>[!NOTE]
>Preconditions can be applied to Modules, Groups, or Commands.
```
The docs should mention `ParameterPreconditionAttribute`'s existence.
* Update line breaks to comply with docs standard
* Change "you should..." to "instead, ..."
* Trim trailing spaces
* Change "inherits" to "inherit"
* Fix Context warning note and add ReplyAsync xref
* Fix broken xrefs
* Fix [Command Service] xref
* Fix consistency between TypeReaders and Preconditions returns
* Add missing semi-colons in ServiceProvider sample
* Change CommandContext to SocketCommandContext & change variable naming
* Cleanup TypeReader section
* Wrap [DontInject] in code block
* Fix commands docs linking in intro
* Improve Getting Started - Installation
- Fix character misalignment to comply with docs standard.
- Fix image numbering issues by moving the tooltips above some of the steps.
- Add codeblocks to search terms like `Discord.Net`.
- Remove broken `addons` reference.
- Specify `.NET 4.6.1` as `.NET Framework 4.6.1`.
- Minor cross-reference cleanup.
* Fix Getting Started - Intro
- Minor grammartical fixes.
- Wrap mentions of the methods, properties, and events in code block.
- Replace `Discord.Net` to `Discord.NET`.
- Fix steps numbering under `Creating a Discord Bot` and `Adding your bot to a server`.
- Change `Task-based Asynchronous Pattern ([TAP])` linking to mark the entire term instead.
- Change code block of `Pong!` to quotation mark instead.
* Fix cross references in Sending Voice
* Mention parameter precondition attribute
* Change `Discord.NET` to `Discord.Net` for consistency
* Wrap project names in code blocks & minor fixes in Terminology
* Change `add-ons` to `addons` for consistency
* Fix cross references in Logging
* Fix minor grammatical issues in "Working with Events"
* Missed a tilda
* Remove out-of-date warning in Commands
* Minor grammatical fixes for Entities
* Fix broken xref in Logging
* Adjust service collection sample
...according to f89aecb7bf (r141530227)
* Update Command Handler sample
- Update Main for C# 7.1.
- Inject CommandService and DiscordSocketClient into the service collection.
- Add Async suffix to asynchronous methods.
* Minor grammatical fixes in Events
* Revert 2 incorrect grammar corrections
* Revert async Main sample
* Add hardcode token notice in sample
* Fix missing method for Command Handler
* Modify module samples to use SocketCommandContext instead
* Emphasize CommandContext and SocketCommandContext
* Fix formatting for module sample
* Add SocketCommandContext for Groups sample
* Remove comma
* Fix DepMap sample formatting
* Replace [DontInject] with DontInjectAttribute with cross reference
* Remove connection logic note
There is no reason that this note should still be here since Ready event exists.
* Add a new warning message informing the users the existence of CommandService
* Make command handler private
excellent change
This commit is contained in:
@@ -1,24 +1,20 @@
|
||||
# The Command Service
|
||||
|
||||
>[!WARNING]
|
||||
>This article is out of date, and has not been rewritten yet.
|
||||
Information is not guaranteed to be accurate.
|
||||
|
||||
[Discord.Commands](xref:Discord.Commands) provides an Attribute-based
|
||||
Command Parser.
|
||||
command parser.
|
||||
|
||||
## Setup
|
||||
|
||||
To use Commands, you must create a [Commands Service] and a
|
||||
Command Handler.
|
||||
To use Commands, you must create a [Command 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.
|
||||
Included below is a very barebone 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],
|
||||
The `CommandService` will optionally 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
|
||||
look over the properties in [CommandServiceConfig] and their default
|
||||
values.
|
||||
|
||||
[!code-csharp[Command Handler](samples/command_handler.cs)]
|
||||
@@ -28,32 +24,32 @@ values.
|
||||
|
||||
## With Attributes
|
||||
|
||||
In 1.0, Commands can be defined ahead of time, with attributes, or
|
||||
at runtime, with builders.
|
||||
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
|
||||
is the recommended method of defining commands.
|
||||
For most bots, ahead-of-time Commands should be all you need, and this
|
||||
is the recommended method of defining Commands.
|
||||
|
||||
### Modules
|
||||
|
||||
The first step to creating commands is to create a _module_.
|
||||
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.
|
||||
A Module is an organizational pattern that allows 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 invoked.
|
||||
ASP.NET Core's Controller pattern. This means that the lifetime of a
|
||||
module instance is only as long as the Command is being invoked.
|
||||
|
||||
**Avoid using long-running code** in your modules wherever possible.
|
||||
You should **not** be implementing very much logic into your modules;
|
||||
outsource to a service for that.
|
||||
You should **not** be implementing very much logic into your modules,
|
||||
instead, 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 begin, create a new class somewhere in your project, and
|
||||
inherit the class from [ModuleBase]. This class **must** be `public`.
|
||||
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
|
||||
@@ -61,6 +57,7 @@ inherit the class from [ModuleBase]. This class **must** be `public`.
|
||||
>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
|
||||
@@ -69,72 +66,75 @@ By now, your module should look like this:
|
||||
|
||||
### Adding Commands
|
||||
|
||||
The next step to creating commands, is actually creating commands.
|
||||
The next step to creating Commands is actually creating the 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
|
||||
default can be found in the below section on _Type Readers_.
|
||||
For example, to take an integer as an argument from the user, add `int
|
||||
arg`; to take a user as an argument from the user, 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
|
||||
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,
|
||||
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.
|
||||
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).
|
||||
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
|
||||
You may add overloads to your Commands, and the Command parser will
|
||||
automatically pick up on it.
|
||||
|
||||
If, for whatever reason, you have too commands which are ambiguous to
|
||||
If for whatever reason, you have two 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.
|
||||
The `Priority` attributes are sorted in ascending order; the higher
|
||||
priority will be called first.
|
||||
|
||||
### CommandContext
|
||||
### Command Context
|
||||
|
||||
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]. `ICommandContext` allows you to access the
|
||||
message, channel, guild, and user that the Command was invoked from,
|
||||
as well as the underlying Discord client that 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
|
||||
of [ModuleBase]. When using a [SocketCommandContext], for example, the
|
||||
properties on this context will already be Socket entities, so you
|
||||
will not need to cast them.
|
||||
|
||||
To reply to messages, you may also invoke [ReplyAsync], instead of
|
||||
accessing the channel through the [Context] and sending a message.
|
||||
|
||||
> [!WARNING]
|
||||
>Contexts should **NOT** be mixed! You cannot have one module that
|
||||
>uses `CommandContext` and another that uses `SocketCommandContext`.
|
||||
|
||||
[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
|
||||
>uses CommandContext, and another that uses SocketCommandContext.
|
||||
[ReplyAsync]: xref:Discord.Commands.ModuleBase`1#Discord_Commands_ModuleBase_1_ReplyAsync_System_String_System_Boolean_Discord_Embed_Discord_RequestOptions_
|
||||
|
||||
### Example Module
|
||||
|
||||
@@ -144,50 +144,50 @@ At this point, your module should look comparable to this example:
|
||||
#### Loading Modules Automatically
|
||||
|
||||
The Command Service can automatically discover all classes in an
|
||||
Assembly that inherit [ModuleBase], and load them.
|
||||
Assembly that inherit [ModuleBase] and load them.
|
||||
|
||||
To opt a module out of auto-loading, flag it with
|
||||
[DontAutoLoadAttribute]
|
||||
[DontAutoLoadAttribute].
|
||||
|
||||
Invoke [CommandService.AddModulesAsync] to discover modules and
|
||||
install them.
|
||||
|
||||
[DontAutoLoadAttribute]: xref:Discord.Commands.DontAutoLoadAttribute
|
||||
[CommandService.AddModulesAsync]: xref:Discord_Commands_CommandService#Discord_Commands_CommandService_AddModulesAsync_Assembly_
|
||||
[CommandService.AddModulesAsync]: xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddModulesAsync_Assembly_
|
||||
|
||||
#### Loading Modules Manually
|
||||
|
||||
To manually load a module, invoke [CommandService.AddModuleAsync],
|
||||
by passing in the generic type of your module, and optionally
|
||||
a dependency map.
|
||||
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
|
||||
@System.IServiceProvider. Alternatively, you may accept an
|
||||
IServiceProvider as an argument and extract services yourself.
|
||||
that are placed in the Module's constructor must be injected into an
|
||||
@System.IServiceProvider first. Alternatively, you may accept an
|
||||
`IServiceProvider` as an argument and extract services yourself.
|
||||
|
||||
### Module Properties
|
||||
|
||||
Modules with public settable properties will have them injected after module
|
||||
construction.
|
||||
Modules with `public` settable properties will have the dependencies
|
||||
injected after the construction of the Module.
|
||||
|
||||
### Module Groups
|
||||
|
||||
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 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
|
||||
command will inherit the name of the group it belongs to.
|
||||
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 Modules that reside within another one. Typically,
|
||||
submodules are used to create nested groups (although not required to
|
||||
create nested groups).
|
||||
|
||||
@@ -199,54 +199,62 @@ create nested groups).
|
||||
|
||||
## Dependency Injection
|
||||
|
||||
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.
|
||||
The Command Service is bundled with a very barebone Dependency
|
||||
Injection service for your convenience. It is recommended that you use
|
||||
DI when writing your modules.
|
||||
|
||||
### Setup
|
||||
|
||||
First, you need to create an @System.IServiceProvider
|
||||
You may create your own IServiceProvider if you wish.
|
||||
First, you need to create an @System.IServiceProvider; you may create
|
||||
your own one if you wish.
|
||||
|
||||
Next, add the dependencies your modules will use to the map.
|
||||
Next, add the dependencies that your modules will use to the map.
|
||||
|
||||
Finally, pass the map into the `LoadAssembly` method.
|
||||
Your modules will automatically be loaded with this dependency map.
|
||||
Finally, pass the map into the `LoadAssembly` method. Your modules
|
||||
will be automatically loaded with this dependency map.
|
||||
|
||||
[!code-csharp[IServiceProvider Setup](samples/dependency_map_setup.cs)]
|
||||
|
||||
### Usage in Modules
|
||||
|
||||
In the constructor of your module, any parameters will be filled in by
|
||||
the @System.IServiceProvider you pass into `LoadAssembly`.
|
||||
In the constructor of your Module, any parameters will be filled in by
|
||||
the @System.IServiceProvider that you've passed into `LoadAssembly`.
|
||||
|
||||
Any publicly settable properties will also be filled in the same manner.
|
||||
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.
|
||||
> Annotating a property with a [DontInjectAttribute] attribute will prevent the
|
||||
property from being injected.
|
||||
|
||||
>[!NOTE]
|
||||
>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 the module was loaded from, and the ServiceProvider passed
|
||||
into it, respectively.
|
||||
>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.
|
||||
|
||||
[!code-csharp[ServiceProvider in Modules](samples/dependency_module.cs)]
|
||||
|
||||
[DontInjectAttribute]: xref:Discord.Commands.DontInjectAttribute
|
||||
|
||||
# Preconditions
|
||||
|
||||
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.
|
||||
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]
|
||||
>Preconditions can be applied to Modules, Groups, or Commands.
|
||||
>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 ships with four bundled preconditions; you may view their
|
||||
usages on their API page.
|
||||
Commands ship with four bundled Preconditions; you may view their
|
||||
usages on their respective API pages.
|
||||
|
||||
- @Discord.Commands.RequireContextAttribute
|
||||
- @Discord.Commands.RequireOwnerAttribute
|
||||
@@ -255,21 +263,23 @@ usages on their API page.
|
||||
|
||||
## Custom Preconditions
|
||||
|
||||
To write your own preconditions, create a new class that inherits from
|
||||
@Discord.Commands.PreconditionAttribute
|
||||
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
|
||||
[CheckPermissions].
|
||||
In order for your Precondition to function, you will need to override
|
||||
the [CheckPermissions] method.
|
||||
|
||||
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],
|
||||
optionally including an error message.
|
||||
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)]
|
||||
|
||||
[CheckPermissions]: xref:Discord.Commands.PreconditionAttribute#Discord_Commands_PreconditionAttribute_CheckPermissions_Discord_Commands_CommandContext_Discord_Commands_CommandInfo_Discord_Commands_IDependencyMap_
|
||||
[CheckPermissions]: xref:Discord.Commands.PreconditionAttribute#Discord_Commands_PreconditionAttribute_CheckPermissions_Discord_Commands_ICommandContext_Discord_Commands_CommandInfo_IServiceProvider_
|
||||
[PreconditionResult.FromSuccess]: xref:Discord.Commands.PreconditionResult#Discord_Commands_PreconditionResult_FromSuccess
|
||||
[PreconditionResult.FromError]: xref:Discord.Commands.PreconditionResult#Discord_Commands_PreconditionResult_FromError_System_String_
|
||||
|
||||
@@ -296,22 +306,28 @@ By default, the following Types are supported arguments:
|
||||
|
||||
### 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
|
||||
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 [Read].
|
||||
Next, satisfy the `TypeReader` class by overriding the [Read] 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.
|
||||
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)`.
|
||||
Otherwise, return `TypeReaderResult.FromError`.
|
||||
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.
|
||||
|
||||
[Read]: xref:Discord.Commands.TypeReader#Discord_Commands_TypeReader_Read_Discord_Commands_CommandContext_System_String_
|
||||
[TypeReaderResult]: xref:Discord.Commands.TypeReaderResult
|
||||
[TypeReaderResult.FromSuccess]: xref:Discord.Commands.TypeReaderResult#Discord_Commands_TypeReaderResult_FromSuccess_Discord_Commands_TypeReaderValue_
|
||||
[TypeReaderResult.FromError]: xref:Discord.Commands.TypeReaderResult#Discord_Commands_TypeReaderResult_FromError_Discord_Commands_CommandError_System_String_
|
||||
[Read]: xref:Discord.Commands.TypeReader#Discord_Commands_TypeReader_Read_Discord_Commands_ICommandContext_System_String_IServiceProvider_
|
||||
|
||||
#### Sample
|
||||
|
||||
@@ -319,5 +335,9 @@ Otherwise, return `TypeReaderResult.FromError`.
|
||||
|
||||
### Installing TypeReaders
|
||||
|
||||
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_).
|
||||
TypeReaders are not automatically discovered by the Command Service
|
||||
and must be explicitly added.
|
||||
|
||||
To install a TypeReader, invoke [CommandService.AddTypeReader].
|
||||
|
||||
[CommandService.AddTypeReader]: xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddTypeReader__1_Discord_Commands_TypeReader_
|
||||
@@ -8,39 +8,42 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
public class Program
|
||||
{
|
||||
private CommandService commands;
|
||||
private DiscordSocketClient client;
|
||||
private IServiceProvider services;
|
||||
private CommandService _commands;
|
||||
private DiscordSocketClient _client;
|
||||
private IServiceProvider _services;
|
||||
|
||||
static void Main(string[] args) => new Program().Start().GetAwaiter().GetResult();
|
||||
private static void Main(string[] args) => new Program().StartAsync().GetAwaiter().GetResult();
|
||||
|
||||
public async Task Start()
|
||||
public async Task StartAsync()
|
||||
{
|
||||
client = new DiscordSocketClient();
|
||||
commands = new CommandService();
|
||||
_client = new DiscordSocketClient();
|
||||
_commands = new CommandService();
|
||||
|
||||
// Avoid hard coding your token. Use an external source instead in your code.
|
||||
string token = "bot token here";
|
||||
|
||||
services = new ServiceCollection()
|
||||
.BuildServiceProvider();
|
||||
_services = new ServiceCollection()
|
||||
.AddSingleton(_client)
|
||||
.AddSingleton(_commands)
|
||||
.BuildServiceProvider();
|
||||
|
||||
await InstallCommands();
|
||||
await InstallCommandsAsync();
|
||||
|
||||
await client.LoginAsync(TokenType.Bot, token);
|
||||
await client.StartAsync();
|
||||
await _client.LoginAsync(TokenType.Bot, token);
|
||||
await _client.StartAsync();
|
||||
|
||||
await Task.Delay(-1);
|
||||
}
|
||||
|
||||
public async Task InstallCommands()
|
||||
public async Task InstallCommandsAsync()
|
||||
{
|
||||
// Hook the MessageReceived Event into our Command Handler
|
||||
client.MessageReceived += HandleCommand;
|
||||
_client.MessageReceived += HandleCommandAsync;
|
||||
// Discover all of the commands in this assembly and load them.
|
||||
await commands.AddModulesAsync(Assembly.GetEntryAssembly());
|
||||
await _commands.AddModulesAsync(Assembly.GetEntryAssembly());
|
||||
}
|
||||
|
||||
public async Task HandleCommand(SocketMessage messageParam)
|
||||
private async Task HandleCommandAsync(SocketMessage messageParam)
|
||||
{
|
||||
// Don't process the command if it was a System Message
|
||||
var message = messageParam as SocketUserMessage;
|
||||
@@ -48,13 +51,13 @@ public class Program
|
||||
// Create a number to track where the prefix ends and the command begins
|
||||
int argPos = 0;
|
||||
// Determine if the message is a command, based on if it starts with '!' or a mention prefix
|
||||
if (!(message.HasCharPrefix('!', ref argPos) || message.HasMentionPrefix(client.CurrentUser, ref argPos))) return;
|
||||
if (!(message.HasCharPrefix('!', ref argPos) || message.HasMentionPrefix(_client.CurrentUser, ref argPos))) return;
|
||||
// Create a Command Context
|
||||
var context = new CommandContext(client, message);
|
||||
var context = new SocketCommandContext(_client, message);
|
||||
// Execute the command. (result does not indicate a return value,
|
||||
// rather an object stating if the command executed successfully)
|
||||
var result = await commands.ExecuteAsync(context, argPos, service);
|
||||
var result = await _commands.ExecuteAsync(context, argPos, _services);
|
||||
if (!result.IsSuccess)
|
||||
await context.Channel.SendMessageAsync(result.ErrorReason);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using foxboat.Services;
|
||||
private IServiceProvider _services;
|
||||
private CommandService _commands;
|
||||
|
||||
public class Commands
|
||||
public async Task InstallAsync(DiscordSocketClient client)
|
||||
{
|
||||
public async Task Install(DiscordSocketClient client)
|
||||
{
|
||||
// Here, we will inject the ServiceProvider with
|
||||
// all of the services our client will use.
|
||||
_serviceCollection.AddSingleton(client)
|
||||
_serviceCollection.AddSingleton(new NotificationService())
|
||||
_serviceCollection.AddSingleton(new DatabaseService())
|
||||
// ...
|
||||
await _commands.AddModulesAsync(Assembly.GetEntryAssembly());
|
||||
}
|
||||
}
|
||||
// Here, we will inject the ServiceProvider with
|
||||
// all of the services our client will use.
|
||||
_services = new ServiceCollection()
|
||||
.AddSingleton(client)
|
||||
.AddSingleton(_commands)
|
||||
// You can pass in an instance of the desired type
|
||||
.AddSingleton(new NotificationService())
|
||||
// ...or by using the generic method.
|
||||
.AddSingleton<DatabaseService>()
|
||||
.BuildServiceProvider();
|
||||
// ...
|
||||
await _commands.AddModulesAsync(Assembly.GetEntryAssembly());
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using Discord.Commands;
|
||||
|
||||
public class InfoModule : ModuleBase
|
||||
public class InfoModule : ModuleBase<SocketCommandContext>
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
[Group("admin")]
|
||||
public class AdminModule : ModuleBase
|
||||
public class AdminModule : ModuleBase<SocketCommandContext>
|
||||
{
|
||||
[Group("clean")]
|
||||
public class CleanModule : ModuleBase
|
||||
public class CleanModule : ModuleBase<SocketCommandContext>
|
||||
{
|
||||
// ~admin clean 15
|
||||
[Command]
|
||||
|
||||
@@ -1,42 +1,41 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
|
||||
// Create a module with no prefix
|
||||
public class Info : ModuleBase
|
||||
public class Info : ModuleBase<SocketCommandContext>
|
||||
{
|
||||
// ~say hello -> hello
|
||||
[Command("say"), Summary("Echos a message.")]
|
||||
public async Task Say([Remainder, Summary("The text to echo")] string echo)
|
||||
{
|
||||
// ReplyAsync is a method on ModuleBase
|
||||
await ReplyAsync(echo);
|
||||
}
|
||||
// ~say hello -> hello
|
||||
[Command("say")]
|
||||
[Summary("Echos a message.")]
|
||||
public async Task SayAsync([Remainder] [Summary("The text to echo")] string echo)
|
||||
{
|
||||
// ReplyAsync is a method on ModuleBase
|
||||
await ReplyAsync(echo);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a module with the 'sample' prefix
|
||||
[Group("sample")]
|
||||
public class Sample : ModuleBase
|
||||
public class Sample : ModuleBase<SocketCommandContext>
|
||||
{
|
||||
// ~sample square 20 -> 400
|
||||
[Command("square"), Summary("Squares a number.")]
|
||||
public async Task Square([Summary("The number to square.")] int num)
|
||||
{
|
||||
// We can also access the channel from the Command Context.
|
||||
await Context.Channel.SendMessageAsync($"{num}^2 = {Math.Pow(num, 2)}");
|
||||
}
|
||||
// ~sample square 20 -> 400
|
||||
[Command("square")]
|
||||
[Summary("Squares a number.")]
|
||||
public async Task SquareAsync([Summary("The number to square.")] int num)
|
||||
{
|
||||
// We can also access the channel from the Command Context.
|
||||
await Context.Channel.SendMessageAsync($"{num}^2 = {Math.Pow(num, 2)}");
|
||||
}
|
||||
|
||||
// ~sample userinfo --> foxbot#0282
|
||||
// ~sample userinfo --> foxbot#0282
|
||||
// ~sample userinfo @Khionu --> Khionu#8708
|
||||
// ~sample userinfo Khionu#8708 --> Khionu#8708
|
||||
// ~sample userinfo Khionu --> Khionu#8708
|
||||
// ~sample userinfo 96642168176807936 --> Khionu#8708
|
||||
// ~sample whois 96642168176807936 --> Khionu#8708
|
||||
[Command("userinfo"), Summary("Returns info about the current user, or the user parameter, if one passed.")]
|
||||
[Alias("user", "whois")]
|
||||
public async Task UserInfo([Summary("The (optional) user to get info for")] IUser user = null)
|
||||
{
|
||||
var userInfo = user ?? Context.Client.CurrentUser;
|
||||
await ReplyAsync($"{userInfo.Username}#{userInfo.Discriminator}");
|
||||
}
|
||||
}
|
||||
// ~sample whois 96642168176807936 --> Khionu#8708
|
||||
[Command("userinfo")]
|
||||
[Summary("Returns info about the current user, or the user parameter, if one passed.")]
|
||||
[Alias("user", "whois")]
|
||||
public async Task UserInfoAsync([Summary("The (optional) user to get info for")] SocketUser user = null)
|
||||
{
|
||||
var userInfo = user ?? Context.Client.CurrentUser;
|
||||
await ReplyAsync($"{userInfo.Username}#{userInfo.Discriminator}");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user