Browse Source

Add docs for the Command Service

tags/1.0-rc
Christopher F 8 years ago
parent
commit
c5d63485c1
7 changed files with 202 additions and 2 deletions
  1. +1
    -1
      docs/api/.manifest
  2. +62
    -0
      docs/guides/commands.md
  3. +52
    -0
      docs/guides/samples/command_handler.cs
  4. +16
    -0
      docs/guides/samples/dependency_map_setup.cs
  5. +28
    -0
      docs/guides/samples/dependency_module.cs
  6. +40
    -0
      docs/guides/samples/module.cs
  7. +3
    -1
      docs/guides/toc.yml

+ 1
- 1
docs/api/.manifest
File diff suppressed because it is too large
View File


+ 62
- 0
docs/guides/commands.md View File

@@ -0,0 +1,62 @@
# The Command Service

[Discord.Commands](xref:Discord.Commands) provides an Attribute-based Command Parser.

### Setup

To use Commands, you must create a [Commands Service](xref:Discord.Commands.CommandService) 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.

[!code-csharp[Barebones Command Handler](samples/command_handler.cs)]

## Modules

Modules serve as a host for commands you create.

To create a module, create a class that you will place commands in. Flag this class with the `[Module]` attribute. You may optionally pass in a string to the `Module` attribute to set a prefix for all of the commands inside the module.

### Example:

[!code-csharp[Modules](samples/module.cs)]

### Loading Modules Automatically

The Command Service can automatically discover all classes in an Assembly that are flagged with the `Module` attribute, and load them.

To have a module opt-out of auto-loading, pass `autoload: false` in the Module attribute.

Invoke [CommandService.LoadAssembly](Discord.Commands.CommandService#Discord_Commands_CommandService_LoadAssembly) to discover modules and install them.

### Loading Modules Manually

To manually load a module, invoke [CommandService.Load](Discord.Commands.CommandService#Discord_Commands_CommandService_Load), and pass in an instance of your module.

### Module Constructors

When automatically loading modules, you are limited in your constructor. Using a constructor that accepts _no arguments_, or a constructor that accepts a @Discord.Commands.CommandService will always work.

Alternatively, you can use an @Discord.Commands.IDependencyMap, as shown below.

## Dependency Injection

The Commands Service includes a very basic implementation of Dependency Injection that allows you to have completely custom constructors, within certain limitations.

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

Next, add the dependencies 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.

[!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 the @Discord.Commands.IDependencyMap you pass into `LoadAssembly`.

>[!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.

[!code-csharp[DependencyMap in Modules](samples/dependency_module.cs)]

+ 52
- 0
docs/guides/samples/command_handler.cs View File

@@ -0,0 +1,52 @@
using System.Threading.Tasks;
using System.Reflection;
using Discord;
using Discord.Commands;

public class Program
{
private CommandService commands;
private DiscordSocketClient client;

static void Main(string[] args) => new Program().Start().GetAwaiter().GetResult();

public async Task Start()
{
client = new DiscordSocketClient();
commands = new CommandService();

string token = "bot token here";

await InstallCommands();

await client.LoginAsync(TokenType.Bot, token);
await client.ConnectAsync();

await Task.Delay(-1);
}

public async Task InstallCommands()
{
// Hook the MessageReceived Event into our Command Handler
client.MessageReceived += HandleCommand;
// Discover all of the commands in this assembly and load them.
await commands.LoadAssembly(Assembly.GetEntryAssembly());
}
public async Task HandleCommand(IMessage msg)
{
// Internal integer, marks where the command begins
int argPos = 0;
// Get the current user (used for Mention parsing)
var currentUser = await client.GetCurrentUserAsync();
// Determine if the message is a command, based on if it starts with '!' or a mention prefix
if (msg.HasCharPrefix('!', ref argPos) || msg.HasMentionPrefix(currentUser, ref argPos))
{
// Execute the command. (result does not indicate a return value,
// rather an object stating if the command executed succesfully)
var result = await _commands.Execute(msg, argPos);
if (!result.IsSuccess)
await msg.Channel.SendMessageAsync(result.ErrorReason);
}
}

}

+ 16
- 0
docs/guides/samples/dependency_map_setup.cs View File

@@ -0,0 +1,16 @@
using Discord;
using Discord.Commands;

public class Commands
{
public async Task Install(DiscordSocketClient client)
{
var commands = new CommandService();
var map = new DependencyMap();
map.Add<IDiscordClient>(client);
var self = await client.GetCurrentUserAsync();
map.Add<ISelfUser>(self);
await commands.LoadAssembly(Assembly.GetCurrentAssembly(), map);
}
// ...
}

+ 28
- 0
docs/guides/samples/dependency_module.cs View File

@@ -0,0 +1,28 @@
using Discord;
using Discord.Commands;

[Module]
public class ModuleA
{
private DiscordSocketClient client;
private ISelfUser self;

public ModuleA(IDiscordClient c, ISelfUser s)
{
if (!(c is DiscordSocketClient)) throw new InvalidOperationException("This module requires a DiscordSocketClient");
client = c as DiscordSocketClient;
self = s;
}
}

public class ModuleB
{
private IDiscordClient client;
private CommandService commands;
public ModuleB(CommandService c, IDependencyMap m)
{
commands = c;
client = m.Get<IDiscordClient>();
}
}

+ 40
- 0
docs/guides/samples/module.cs View File

@@ -0,0 +1,40 @@
using Discord.Commands;

// Create a module with no prefix
[Module]
public class Info
{
// ~say hello -> hello
[Command("say"), Description("Echos a message.")]
public async Task Say(IMessage msg,
[Unparsed, Description("The text to echo")] string echo)
{
await msg.Channel.SendMessageAsync(echo);
}
}

// Create a module with the 'sample' prefix
[Module("sample")]
public class Sample
{
// ~sample square 20 ->
[Command("square"), Description("Squares a number.")]
public async Task Square(IMessage msg,
[Description("The number to square.")] int num)
{
await msg.Channel.SendMessageAsync($"{num}^2 = {Math.Pow(num, 2)}");
}

// ~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
[Command("userinfo"), Description("Returns info about the current user, or the user parameter, if one passed.")]
public async Task UserInfo(IMessage msg,
[Description("The (optional) user to get info for")] IUser user = null)
{
var userInfo = user ?? await Program.Client.GetCurrentUserAsync();
await msg.Channel.SendMessageAsync($"{userInfo.Username}#{userInfo.Discriminator}");
}
}

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

@@ -4,4 +4,6 @@
- name: Terminology - name: Terminology
href: terminology.md href: terminology.md
- name: Logging - name: Logging
href: logging.md
href: logging.md
- name: Commands
href: commands.md

Loading…
Cancel
Save