forked from DSharpPlus/DSharpPlus
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
26 changed files
with
280 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 1,83 @@ | ||
# Augmenting commands - custom argument converters | ||
|
||
Suppose you want to augment an existing argument converter, or introduce a new one. The Argument Converter system in | ||
CommandsNext allows you to modify or register converters for arguments passed to commands. An argument converter for | ||
type `T` is a class which implements @DSharpPlus.CommandsNext.Converters.IArgumentConverter`1 and has an | ||
implementation for @DSharpPlus.CommandsNext.Converters.IArgumentConverter`1.TryConvert(System.String,DSharpPlus.CommandsNext.CommandContext,`0@) method. | ||
|
||
Here's we'll be creating an augmented boolean converter. | ||
|
||
## 1. Creating a converter | ||
|
||
Create a new class, call it `EnhancedBoolConverter`, and make it implement @DSharpPlus.CommandsNext.Converters.IArgumentConverter`1 | ||
with generic argument set to bool (`IArgumentConverter<bool>`). | ||
|
||
In the `TryConvert` method, you will want to add code which checks if the `value` is equal to `"yes"` or `"no"`, and return | ||
appropriate value. Otherwise it should fallback to default bool parser. It should look more or less like this: | ||
|
||
```cs | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Text; | ||
using DSharpPlus.CommandsNext; | ||
using DSharpPlus.CommandsNext.Converters; | ||
|
||
namespace MyFirstBot | ||
{ | ||
public class EnhancedBoolConverter : IArgumentConverter<bool> | ||
{ | ||
public bool TryConvert(string value, CommandContext ctx, out bool result) | ||
{ | ||
switch (value.ToLowerInvariant()) | ||
{ | ||
case "yes": | ||
result = true; | ||
return true; | ||
|
||
case "no": | ||
result = false; | ||
return true; | ||
} | ||
|
||
return bool.TryParse(value, out result); | ||
} | ||
} | ||
} | ||
``` | ||
|
||
## 2. Registering the converter | ||
|
||
Once your converter is created, you need to register it with CommandsNext. You can do that by invoking | ||
@DSharpPlus.CommandsNext.CommandsNextExtension.RegisterConverter``1(IArgumentConverter{``0}) with the converter instance: | ||
|
||
```cs | ||
commands.RegisterConverter(new EnhancedBoolConverter()); | ||
``` | ||
|
||
You need to call the method before you register your commands. | ||
|
||
## 3. Making use of the converter | ||
|
||
Create a new command which takes a bool argument to test the converter: | ||
|
||
```cs | ||
[Command("yesno")] | ||
public async Task YesNo(CommandContext ctx, bool arg) | ||
{ | ||
await ctx.RespondAsync($"Your pick: {arg ? "Yes" : "No"}"); | ||
} | ||
``` | ||
|
||
You can now invoke it as `;;yesno yes` or `;;yesno no`. | ||
|
||
## 4. Further notes | ||
|
||
This particular example replaces an existing converter with a new one. You can also register converters for other types | ||
(including custom ones). All you need is a converter instance for it. | ||
|
||
You can also give types a user-friendly name (that is used for help) by invoking the @DSharpPlus.CommandsNext.CommandsNextExtension.RegisterUserFriendlyTypeName``1(System.String) | ||
method, e.g.: | ||
|
||
```cs | ||
commands.RegisterUserFriendlyTypeName<MyType>("my custom data"); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 1,85 @@ | ||
# Dependency injection - passing data around | ||
|
||
In a situation where you need to pass objects in and out of your command modules, you need a way to access that data. | ||
Dependency injection provides a convenient and safe (not to mention the only correct) way of passing data to your | ||
modules. | ||
|
||
You use dependency injection by first creating a service provider, then supplying it to your @DSharpPlus.CommandsNext.CommandsNextConfiguration | ||
instance via @DSharpPlus.CommandsNext.CommandsNextConfiguration.Services property. The objects you placed in the | ||
service provider will then be injected into your modules when they are instantiated. During injection, CommandsNext | ||
first injects objects via constructor (i.e. it will try to match any constructor parameter to service types in the | ||
provider. If it fails, it will throw. Up next, any public writable properties are populated with services from the | ||
provider. If a suitable service is not found, the property is not injected. The process is then repeated for public | ||
writable fields. To prevent specific properties or fields from being injected, you can put the @DSharpPlus.CommandsNext.Attributes.DontInjectAttribute | ||
over them. | ||
|
||
This is useful in a scenario when you have any kind of data that you need to be persistent or accessible from command | ||
modules, such as settings classes, entity framework database contexts, and so on. | ||
|
||
## 1. Creating a service provider | ||
|
||
If you go back to the basic CommandsNext example, you will remember the `random` command. Let's amend it, and make | ||
use of shared `Random` instance (note that reusing `Random` instances is generally not a good idea; here it's done for | ||
the sake of the example). | ||
|
||
Before you enable your CommandsNext module, you will need to create a new `ServiceCollection` instance, then add a | ||
singleton `Random` instance to it, and finally, build a service provider out of it. You can do it like so: | ||
|
||
```cs | ||
var deps = new ServiceCollection() | ||
.AddSingleton(new Random()) | ||
.BuildServiceProvider(); | ||
``` | ||
|
||
Don't forget to add `using Microsoft.Extensions.DependencyInjection;` to your usings. | ||
|
||
You then need to pass the resulting provider to your CommandsNext configuration. Amend it like so: | ||
|
||
```cs | ||
commands = discord.UseCommandsNext(new CommandsNextConfiguration | ||
{ | ||
StringPrefix = ";;", | ||
Services = deps | ||
}); | ||
``` | ||
|
||
## 2. Amending the command module | ||
|
||
Go to your command module, and give it a read-only property called Rng, of type Random: | ||
|
||
```cs | ||
public Random Rng { get; } | ||
``` | ||
|
||
Now create a constructor for the module, which takes an instance of Random as an argument: | ||
|
||
```cs | ||
public MyCommands(Random rng) | ||
{ | ||
Rng = rng; | ||
} | ||
``` | ||
|
||
And finally edit the `Random` command to look like this: | ||
|
||
```cs | ||
[Command("random")] | ||
public async Task Random(CommandContext ctx, int min, int max) | ||
{ | ||
await ctx.RespondAsync($"🎲 Your random number is: {Rng.Next(min, max)}"); | ||
} | ||
``` | ||
|
||
When you invoke `;;random 1 10` now, it will use the shared `Random` instance. | ||
|
||
## 3. Further notes | ||
|
||
While the service collection can hold singletons, it can hold transient and scoped instances as well. The difference is | ||
that singleton instances are instantantiated once (when added to the collection), scoped are instantiated once per | ||
module instantiation, and transients are instantiated every time they are requested. | ||
|
||
Combined with transient module lifespans, injecting an entity framework database context as a transient or scoped | ||
service makes working with databases easier, as an example. | ||
|
||
Note that if a module has singleton lifespan, all services will be injected to it once. Only transient modules take | ||
advantage of scoped and transient services. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 1,12 @@ | ||
# Command module lifespans - preventing module reuse | ||
|
||
There are situations where you don't want to reuse a module instance, for one reason or another, usually due to | ||
thread-safety concerns. | ||
|
||
CommandsNext allows command modules 2 have to lifespan modes: singleton (default), and transient. Singleton modules, as | ||
the name implies, are instantiated once per the entire CNext extension's lifetime. Transient modules, on the other | ||
hand, are instantiated for each command call. This enables you to make use of transient and scoped modules. If you're | ||
unsure what that means, familiarize yourself with the [dependency injection](/articles/commands/dependency_injection.html "dependency injection") | ||
guide. | ||
|
||
## TODO |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.