Browse Source

Split command service guide into various articles

+ Each section has gone through a thorough proof-reading and cleanup
+ This should help with readability.
pull/988/head
Hsu Still 7 years ago
parent
commit
84c42f5e28
No known key found for this signature in database GPG Key ID: 8601A145FDA95209
10 changed files with 186 additions and 142 deletions
  1. +1
    -2
      docs/_overwrites/Commands/DontInjectAttribute.Overwrite.md
  2. +1
    -1
      docs/faq/commands/Commands.md
  3. +6
    -2
      docs/guides/commands/dependency-injection.md
  4. +8
    -114
      docs/guides/commands/intro.md
  5. +53
    -0
      docs/guides/commands/preconditions.md
  6. +12
    -22
      docs/guides/commands/samples/dependency_module.cs
  7. +2
    -0
      docs/guides/commands/samples/empty-module.cs
  8. +29
    -0
      docs/guides/commands/samples/typereader-register.cs
  9. +69
    -0
      docs/guides/commands/typereaders.md
  10. +5
    -1
      docs/guides/toc.yml

+ 1
- 2
docs/_overwrites/Commands/DontInjectAttribute.Overwrite.md View File

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


+ 1
- 1
docs/faq/commands/Commands.md View File

@@ -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.



+ 6
- 2
docs/guides/commands/dependency-injection.md View File

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

docs/guides/commands/commands.md → docs/guides/commands/intro.md View File

@@ -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)]

+ 53
- 0
docs/guides/commands/preconditions.md View File

@@ -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*

+ 12
- 22
docs/guides/commands/samples/dependency_module.cs View File

@@ -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;
}

}
}

+ 2
- 0
docs/guides/commands/samples/empty-module.cs View File

@@ -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>
{

+ 29
- 0
docs/guides/commands/samples/typereader-register.cs View File

@@ -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)
{
// ...
}
}

+ 69
- 0
docs/guides/commands/typereaders.md View File

@@ -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)]

+ 5
- 1
docs/guides/toc.yml View File

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


Loading…
Cancel
Save