@@ -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.DI) | |||
to learn more. | |||
dependency. See @Guides.Commands.DI to learn more. | |||
--- | |||
uid: Discord.Commands.DontInjectAttribute | |||
@@ -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 |
@@ -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)] |
@@ -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* |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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> | |||
{ | |||
@@ -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) | |||
{ | |||
// ... | |||
} | |||
} |
@@ -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)] |
@@ -1,6 +1,6 @@ | |||
--- | |||
uid: Guides.Concepts.Logging | |||
title: Logging | |||
title: Logging Events/Data | |||
--- | |||
# Logging in Discord.Net | |||
@@ -5,14 +5,15 @@ title: Start making a bot | |||
# Making a Ping-Pong bot | |||
One of the first steps to getting started with the Discord API is to | |||
write a basic ping-pong bot. We will expand on this to create more | |||
diverse commands later, but for now, it is a good starting point. | |||
One of ways to get started with the Discord API is to write a basic | |||
ping-pong bot. This bot will respond to a simple command "ping." | |||
We will expand on this to create more diverse commands later, but for | |||
now, it is a good starting point. | |||
## Creating a Discord Bot | |||
Before you can begin writing your bot, it is necessary to create a bot | |||
account on Discord. | |||
Before writing your bot, it is necessary to create a bot account via the | |||
Discord Applications Portal first. | |||
1. Visit the [Discord Applications Portal]. | |||
2. Create a New Application. | |||
@@ -37,12 +38,14 @@ Bots **cannot** use invite links; they must be explicitly invited | |||
through the OAuth2 flow. | |||
1. Open your bot's application on the [Discord Applications Portal]. | |||
2. Retrieve the app's **Client ID**. | |||
2. Retrieve the application's **Client ID**. | |||
 | |||
3. Create an OAuth2 authorization URL | |||
`https://discordapp.com/oauth2/authorize?client_id=<CLIENT ID>&scope=bot` | |||
- `https://discordapp.com/oauth2/authorize?client_id=<CLIENT ID>&scope=bot` | |||
4. Open the authorization URL in your browser. | |||
5. Select a server. | |||
6. Click on authorize. | |||
@@ -56,36 +59,38 @@ through the OAuth2 flow. | |||
## Connecting to Discord | |||
If you have not already created a project and installed Discord.Net, | |||
do that now. (see the [Installing](xref:Guides.GettingStarted.Installation) section) | |||
do that now. | |||
For more information, see @Guides.GettingStarted.Installation. | |||
### Async | |||
Discord.Net uses .NET's [Task-based Asynchronous Pattern (TAP)] | |||
extensively - nearly every operation is asynchronous. | |||
extensively - nearly every operation is asynchronous. It is highly | |||
recommended that these operations are awaited in a | |||
properly established async context whenever possible. | |||
It is highly recommended that these operations are awaited in a | |||
properly established async context whenever possible. Establishing an | |||
async context can be problematic, but not hard. | |||
To establish an async context, we will be creating an async main method | |||
in your console application, and rewriting the static main method to | |||
invoke the new async main. | |||
To do so, we will be creating an async main in your console | |||
application, and rewriting the static main method to invoke the new | |||
async main. | |||
[!code-csharp[Async Context](samples/intro/async-context.cs)] | |||
[!code-csharp[Async Context](samples/first-bot/async-context.cs)] | |||
As a result of this, your program will now start and immediately | |||
jump into an async context. This will allow us to create a connection | |||
to Discord later on without needing to worry about setting up the | |||
to Discord later on without having to worry about setting up the | |||
correct async implementation. | |||
> [!TIP] | |||
> [!WARNING] | |||
> If your application throws any exceptions within an async context, | |||
> they will be thrown all the way back up to the first non-async method; | |||
> since our first non-async method is the program's `Main` method, this | |||
> means that **all** unhandled exceptions will be thrown up there, which | |||
> will crash your application. Discord.Net will prevent exceptions in | |||
> event handlers from crashing your program, but any exceptions in your | |||
> async main **will** cause the application to crash. | |||
> will crash your application. | |||
> | |||
> Discord.Net will prevent exceptions in event handlers from crashing | |||
> your program, but any exceptions in your async main **will** cause | |||
> the application to crash. | |||
[Task-based Asynchronous Pattern (TAP)]: https://docs.microsoft.com/en-us/dotnet/articles/csharp/async | |||
@@ -100,57 +105,61 @@ parameter. See the [API Documentation] for this event. | |||
If you are using your own logging framework, this is where you would | |||
invoke it. For the sake of simplicity, we will only be logging to | |||
the Console. | |||
the console. | |||
You may learn more about this concept in @Guides.Concepts.Logging. | |||
[!code-csharp[Async Context](samples/intro/logging.cs)] | |||
[!code-csharp[Async Context](samples/first-bot/logging.cs)] | |||
[API Documentation]: xref:Discord.Rest.BaseDiscordClient.Log | |||
### Creating a Discord Client | |||
Finally, we can create a connection to Discord. Since we are writing | |||
a bot, we will be using a [DiscordSocketClient] along with socket | |||
entities. See the [terminology](xref:Guides.GettingStarted.Terminology) if you're unsure of | |||
the differences. | |||
Finally, we can create a new connection to Discord. | |||
Since we are writing a bot, we will be using a [DiscordSocketClient] | |||
along with socket entities. See @Guides.GettingStarted.Terminology | |||
if you are unsure of the differences. | |||
To do so, create an instance of [DiscordSocketClient] in your async | |||
main, passing in a configuration object only if necessary. For most | |||
To establish a new connection, we will create an instance of | |||
[DiscordSocketClient] in the new async main. You may pass in an | |||
optional @Discord.WebSocket.DiscordSocketConfig if necessary. For most | |||
users, the default will work fine. | |||
Before connecting, we should hook the client's `Log` event to the | |||
log handler that was just created. Events in Discord.Net work | |||
similarly to other events in C#, so hook this event the way that | |||
you typically would. | |||
log handler that we had just created. Events in Discord.Net work | |||
similarly to any other events in C#. | |||
Next, you will need to "login to Discord" with the `LoginAsync` | |||
method. | |||
Next, you will need to "login to Discord" with the [LoginAsync] | |||
method with the application's "token." | |||
You may create a variable to hold your bot's token (this can be found | |||
on your bot's application page on the [Discord Applications Portal]). | |||
> [!NOTE] | |||
> Pay attention to what you are copying from the developer portal! | |||
> A token is not the same as the application's "client secret." | |||
 | |||
> [!IMPORTANT] | |||
> Your bot's token can be used to gain total access to your bot, so | |||
> **do __NOT__ share this token with anyone else!** It may behoove you | |||
> to store this token in an external file if you plan on distributing | |||
> to store this token in an external source if you plan on distributing | |||
> the source code for your bot. | |||
We may now invoke the client's `StartAsync` method, which will | |||
We may now invoke the client's [StartAsync] method, which will | |||
start connection/reconnection logic. It is important to note that | |||
**this method returns as soon as connection logic has been started!** | |||
**this method will return as soon as connection logic has been started!** | |||
Any methods that rely on the client's state should go in an event | |||
handler. | |||
handler. This means that you should **not** directly be interacting with | |||
the client before it is fully ready. | |||
Finally, we will want to block the async main method from returning | |||
until after the application is exited. To do this, we can await an | |||
infinite delay or any other blocking method, such as reading from | |||
the console. | |||
when running the application. To do this, we can await an infinite delay | |||
or any other blocking method, such as reading from the console. | |||
The following lines can now be added: | |||
[!code-csharp[Create client](samples/intro/client.cs)] | |||
[!code-csharp[Create client](samples/first-bot/client.cs)] | |||
At this point, feel free to start your program and see your bot come | |||
online in Discord. | |||
@@ -162,6 +171,8 @@ online in Discord. | |||
> for how to fix this. | |||
[DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient | |||
[LoginAsync]: xref:Discord.Rest.BaseDiscordClient.LoginAsync* | |||
[StartAsync]: xref:Discord.WebSocket.DiscordSocketClient.StartAsync* | |||
[installation guide]: xref:Guides.GettingStarted.Installation#installing-on-net-standard-11 | |||
### Handling a 'ping' | |||
@@ -169,18 +180,18 @@ online in Discord. | |||
> [!WARNING] | |||
> Please note that this is *not* a proper way to create a command. | |||
> Use the `CommandService` provided by the library instead, as explained | |||
> in the [Command Guide] section. | |||
> in the [Command Guide](xref:Guides.Commands.Intro) section. | |||
Now that we have learned how to open a connection to Discord, we can | |||
begin handling messages that users are sending. To start out, our bot | |||
will listen for any message where the content is equal to `!ping` and | |||
respond back with "Pong!". | |||
Now that we have learned to open a connection to Discord, we can | |||
begin handling messages that the users are sending. To start out, our | |||
bot will listen for any message whose content is equal to `!ping` and | |||
will respond back with "Pong!". | |||
Since we want to listen for new messages, the event to hook into | |||
is [MessageReceived]. | |||
In your program, add a method that matches the signature of the | |||
`MessageReceived` event - it must be a method (`Func`) that returns | |||
`MessageReceived` event - it must be a method (`Func`) that returns | |||
the type `Task` and takes a single parameter, a [SocketMessage]. Also, | |||
since we will be sending data to Discord in this method, we will flag | |||
it as `async`. | |||
@@ -189,45 +200,49 @@ In this method, we will add an `if` block to determine if the message | |||
content fits the rules of our scenario - recall that it must be equal | |||
to `!ping`. | |||
Inside the branch of this condition, we will want to send a message | |||
back to the channel from which the message comes from - "Pong!". To | |||
find the channel, look for the `Channel` property on the message | |||
Inside the branch of this condition, we will want to send a message, | |||
`Pong!`, back to the channel from which the message comes from. To | |||
find the channel, look for the `Channel` property on the message | |||
parameter. | |||
Next, we will want to send a message to this channel. Since the | |||
channel object is of type [SocketMessageChannel], we can invoke the | |||
`SendMessageAsync` instance method. For the message content, send back | |||
a string containing "Pong!". | |||
channel object is of type [ISocketMessageChannel], we can invoke the | |||
[SendMessageAsync] instance method. For the message content, send back | |||
a string, "Pong!". | |||
You should have now added the following lines, | |||
[!code-csharp[Message](samples/intro/message.cs)] | |||
[!code-csharp[Message](samples/first-bot/message.cs)] | |||
Now your first bot is complete. You may continue to add on to this | |||
Now that your first bot is complete. You may continue to add on to this | |||
if you desire, but for any bots that will be carrying out multiple | |||
commands, it is strongly recommended to use the command framework as | |||
shown below. | |||
For your reference, you may view the [completed program]. | |||
> [!NOTE] | |||
> For your reference, you may view the [completed program]. | |||
[MessageReceived]: xref:Discord.WebSocket.BaseSocketClient.MessageReceived | |||
[SocketMessage]: xref:Discord.WebSocket.SocketMessage | |||
[SocketMessageChannel]: xref:Discord.WebSocket.ISocketMessageChannel | |||
[completed program]: samples/intro/complete.cs | |||
[Command Guide]: xref:Guides.Commands.Intro | |||
[SendMessageAsync]: xref:Discord.WebSocket.ISocketMessageChannel.SendMessageAsync* | |||
[completed program]: samples/first-bot/complete.cs | |||
# Building a bot with commands | |||
This section will show you how to write a program that is ready for | |||
[Commands](xref:Guides.Commands.Intro). Note that we will not be | |||
explaining _how_ to write Commands or Services, it will only be | |||
covering the general structure. | |||
@Guides.Commands.Intro will guide you through how to setup a program | |||
that is ready for [CommandService], a service that is ready for | |||
advanced command usage. | |||
For reference, view an [annotated example] of this structure. | |||
[annotated example]: samples/intro/structure.cs | |||
[annotated example]: samples/first-bot/structure.cs | |||
It is important to know that the recommended design pattern of bots | |||
should be to separate the program (initialization and command handler), | |||
the modules (handle commands), and the services (persistent storage, | |||
pure functions, data manipulation). | |||
should be to separate... | |||
1. the program (initialization and command handler) | |||
2. the modules (handle commands) | |||
3. the services (persistent storage, pure functions, data manipulation) | |||
[CommandService]: xref:Discord.Commands.CommandService |
@@ -3,19 +3,22 @@ uid: Guides.GettingStarted.Installation | |||
title: Installing Discord.Net | |||
--- | |||
Discord.Net is distributed through the NuGet package manager, and it | |||
is recommended to use NuGet to get started. | |||
# Discord.Net Installation | |||
Optionally, you may compile from source and install yourself. | |||
Discord.Net is distributed through the NuGet package manager, so it is | |||
recommended for you to install the library that way. | |||
# Supported Platforms | |||
Alternatively, you may compile from the source and install the library | |||
yourself. | |||
## Supported Platforms | |||
Currently, Discord.Net targets [.NET Standard] 1.3 and offers support | |||
for .NET Standard 1.1. If your application will be targeting .NET | |||
Standard 1.1, please see the [additional steps]. | |||
Since Discord.Net is built on the .NET Standard, it is also | |||
recommended to create applications using [.NET Core], though not | |||
Since Discord.Net is built on top of .NET Standard, it is also | |||
recommended to create applications using [.NET Core], although it is not | |||
required. When using .NET Framework, it is suggested to target | |||
`.NET Framework 4.6.1` or higher. | |||
@@ -23,13 +26,13 @@ required. When using .NET Framework, it is suggested to target | |||
[.NET Core]: https://docs.microsoft.com/en-us/dotnet/articles/core/ | |||
[additional steps]: #installing-on-net-standard-11 | |||
# Installing with NuGet | |||
## Installing with NuGet | |||
Release builds of Discord.Net will be published to the | |||
[official NuGet feed]. | |||
Development builds of Discord.Net, as well as add-ons are published to | |||
our development [MyGet feed]. | |||
Development builds of Discord.Net, as well as add-ons, will be | |||
published to our [MyGet feed]. | |||
Direct feed link: `https://www.myget.org/F/discord-net/api/v3/index.json` | |||
@@ -41,7 +44,7 @@ Not sure how to add a direct feed? See how [with Visual Studio] or | |||
[with Visual Studio]: https://docs.microsoft.com/en-us/nuget/tools/package-manager-ui#package-sources | |||
[without Visual Studio]: #configuring-nuget-without-visual-studio | |||
## [Using Visual Studio](#tab/vs-install) | |||
### [Using Visual Studio](#tab/vs-install) | |||
> [!TIP] | |||
> Don't forget to change your package source if you're installing from | |||
@@ -49,8 +52,8 @@ Not sure how to add a direct feed? See how [with Visual Studio] or | |||
> Also make sure to check "Enable Prereleases" if installing a dev | |||
> build! | |||
1. Create a solution for your bot. | |||
2. In Solution Explorer, find the "Dependencies" element under your | |||
1. Create a new solution for your bot. | |||
2. In the Solution Explorer, find the "Dependencies" element under your | |||
bot's project. | |||
3. Right click on "Dependencies", and select "Manage NuGet packages." | |||
 | |||
@@ -58,7 +61,7 @@ Not sure how to add a direct feed? See how [with Visual Studio] or | |||
5. Install the `Discord.Net` package. | |||
 | |||
## [Using JetBrains Rider](#tab/rider-install) | |||
### [Using JetBrains Rider](#tab/rider-install) | |||
> [!TIP] | |||
> Make sure to check the "Prerelease" box if installing a dev build! | |||
@@ -72,7 +75,7 @@ Not sure how to add a direct feed? See how [with Visual Studio] or | |||
4. Install by adding the package to your project. | |||
 | |||
## [Using Visual Studio Code](#tab/vs-code) | |||
### [Using Visual Studio Code](#tab/vs-code) | |||
> [!TIP] | |||
> Don't forget to add the package source to a [NuGet.Config file] if | |||
@@ -81,11 +84,11 @@ Not sure how to add a direct feed? See how [with Visual Studio] or | |||
1. Create a new project for your bot. | |||
2. Add `Discord.Net` to your .csproj. | |||
[!code[Sample .csproj](samples/project.csproj)] | |||
[!code[Sample .csproj](samples/project.xml)] | |||
[NuGet.Config file]: #configuring-nuget-without-visual-studio | |||
## [Using dotnet CLI](#tab/dotnet-cli) | |||
### [Using dotnet CLI](#tab/dotnet-cli) | |||
> [!TIP] | |||
> Don't forget to add the package source to a [NuGet.Config file] if | |||
@@ -98,11 +101,11 @@ Not sure how to add a direct feed? See how [with Visual Studio] or | |||
*** | |||
# Compiling from Source | |||
## Compiling from Source | |||
In order to compile Discord.Net, you require the following: | |||
In order to compile Discord.Net, you will need the following: | |||
## Using Visual Studio | |||
### Using Visual Studio | |||
- [Visual Studio 2017](https://www.visualstudio.com/) | |||
- [.NET Core SDK] | |||
@@ -110,31 +113,32 @@ In order to compile Discord.Net, you require the following: | |||
The .NET Core and Docker (Preview) workload is required during Visual | |||
Studio installation. | |||
## Using Command Line | |||
### Using Command Line | |||
- [.NET Core SDK] | |||
[.NET Core SDK]: https://www.microsoft.com/net/download/ | |||
# Additional Information | |||
## Additional Information | |||
## Installing on .NET Standard 1.1 | |||
### Installing on .NET Standard 1.1 | |||
For applications targeting a runtime corresponding with .NET Standard | |||
1.1 or 1.2, the builtin WebSocket and UDP provider will not work. For | |||
applications which utilize a WebSocket connection to Discord | |||
(WebSocket or RPC), third-party provider packages will need to be | |||
1.1 or 1.2, the built-in WebSocket and UDP provider will not work. For | |||
applications which utilize a WebSocket connection to Discord, such as | |||
WebSocket or RPC, third-party provider packages will need to be | |||
installed and configured. | |||
First, install the following packages through NuGet, or compile | |||
> [!NOTE] | |||
> `Discord.Net.Providers.UDPClient` is _only_ required if your | |||
> bot will be utilizing voice chat. | |||
First, install the following packages through NuGet, or compile them | |||
yourself, if you prefer: | |||
- Discord.Net.Providers.WS4Net | |||
- Discord.Net.Providers.UDPClient | |||
Note that `Discord.Net.Providers.UDPClient` is _only_ required if your | |||
bot will be utilizing voice chat. | |||
Next, you will need to configure your [DiscordSocketClient] to use | |||
these custom providers over the default ones. | |||
@@ -147,16 +151,16 @@ are passing into your client. | |||
[DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient | |||
[DiscordSocketConfig]: xref:Discord.WebSocket.DiscordSocketConfig | |||
## Configuring NuGet without Visual Studio | |||
### Configuring NuGet without Visual Studio | |||
If you plan on deploying your bot or developing outside of Visual | |||
Studio, you will need to create a local NuGet configuration file for | |||
your project. | |||
To do this, create a file named `nuget.config` alongside the root of | |||
your application, where the project solution is located. | |||
To do this, create a file named `NuGet.Config` alongside the root of | |||
your application, where the project is located. | |||
Paste the following snippets into this configuration file, adding any | |||
additional feeds as necessary. | |||
additional feeds if necessary. | |||
[!code[NuGet Configuration](samples/nuget.config)] |
@@ -0,0 +1,9 @@ | |||
public class Program | |||
{ | |||
public static void Main(string[] args) | |||
=> new Program().MainAsync().GetAwaiter().GetResult(); | |||
public async Task MainAsync() | |||
{ | |||
} | |||
} |
@@ -1,17 +1,17 @@ | |||
// Program.cs | |||
using Discord.WebSocket; | |||
// ... | |||
private DiscordSocketClient _client; | |||
public async Task MainAsync() | |||
{ | |||
_client = new DiscordSocketClient(); | |||
_client.Log += Log; | |||
string token = "abcdefg..."; // Remember to keep this private! | |||
// Remember to keep this private or to read this | |||
// from an external source! | |||
string token = "abcdefg..."; | |||
await _client.LoginAsync(TokenType.Bot, token); | |||
await _client.StartAsync(); | |||
// Block this task until the program is closed. | |||
await Task.Delay(-1); | |||
} | |||
} |
@@ -0,0 +1,38 @@ | |||
public class Program | |||
{ | |||
private DiscordSocketClient _client; | |||
public static void Main(string[] args) | |||
=> new Program().MainAsync().GetAwaiter().GetResult(); | |||
public async Task MainAsync() | |||
{ | |||
_client = new DiscordSocketClient(); | |||
_client.Log += Log; | |||
_client.MessageReceived += MessageReceivedAsync; | |||
// Remember to keep this private or to read this | |||
// from an external source! | |||
string token = "abcdefg..."; | |||
await _client.LoginAsync(TokenType.Bot, token); | |||
await _client.StartAsync(); | |||
// Block this task until the program is closed. | |||
await Task.Delay(-1); | |||
} | |||
private async Task MessageReceivedAsync(SocketMessage message) | |||
{ | |||
if (message.Content == "!ping") | |||
{ | |||
await message.Channel.SendMessageAsync("Pong!"); | |||
} | |||
} | |||
private Task Log(LogMessage msg) | |||
{ | |||
Console.WriteLine(msg.ToString()); | |||
return Task.CompletedTask; | |||
} | |||
} |
@@ -0,0 +1,5 @@ | |||
private Task Log(LogMessage msg) | |||
{ | |||
Console.WriteLine(msg.ToString()); | |||
return Task.CompletedTask; | |||
} |
@@ -1,6 +1,6 @@ | |||
public async Task MainAsync() | |||
{ | |||
// client.Log ... | |||
// ... | |||
_client.MessageReceived += MessageReceived; | |||
// ... | |||
} |
@@ -1,15 +0,0 @@ | |||
using System; | |||
using System.Threading.Tasks; | |||
namespace MyBot | |||
{ | |||
public class Program | |||
{ | |||
public static void Main(string[] args) | |||
=> new Program().MainAsync().GetAwaiter().GetResult(); | |||
public async Task MainAsync() | |||
{ | |||
} | |||
} | |||
} |
@@ -1,44 +0,0 @@ | |||
using Discord; | |||
using Discord.WebSocket; | |||
using System; | |||
using System.Threading.Tasks; | |||
namespace MyBot | |||
{ | |||
public class Program | |||
{ | |||
private DiscordSocketClient _client; | |||
public static void Main(string[] args) | |||
=> new Program().MainAsync().GetAwaiter().GetResult(); | |||
public async Task MainAsync() | |||
{ | |||
_client = new DiscordSocketClient(); | |||
_client.Log += Log; | |||
_client.MessageReceived += MessageReceived; | |||
string token = "abcdefg..."; // Remember to keep this private! | |||
await _client.LoginAsync(TokenType.Bot, token); | |||
await _client.StartAsync(); | |||
// Block this task until the program is closed. | |||
await Task.Delay(-1); | |||
} | |||
private async Task MessageReceived(SocketMessage message) | |||
{ | |||
if (message.Content == "!ping") | |||
{ | |||
await message.Channel.SendMessageAsync("Pong!"); | |||
} | |||
} | |||
private Task Log(LogMessage msg) | |||
{ | |||
Console.WriteLine(msg.ToString()); | |||
return Task.CompletedTask; | |||
} | |||
} | |||
} |
@@ -1,22 +0,0 @@ | |||
using Discord; | |||
using System; | |||
using System.Threading.Tasks; | |||
namespace MyBot | |||
{ | |||
public class Program | |||
{ | |||
public static void Main(string[] args) | |||
=> new Program().MainAsync().GetAwaiter().GetResult(); | |||
public async Task MainAsync() | |||
{ | |||
} | |||
private Task Log(LogMessage msg) | |||
{ | |||
Console.WriteLine(msg.ToString()); | |||
return Task.CompletedTask; | |||
} | |||
} | |||
} |
@@ -1,4 +1,5 @@ | |||
<Project Sdk="Microsoft.NET.Sdk"> | |||
<!-- | |||
The following may differ depending on the latest version of | |||
.NET Core Framework or Discord.Net. |
@@ -26,7 +26,7 @@ Here are some examples: | |||
> It is not meant to be something that will work out of the box. | |||
[Official template]: https://github.com/foxbot/DiscordBotBase/tree/csharp/src/DiscordBot | |||
[Official quick start guide]: https://github.com/RogueException/Discord.Net/blob/dev/docs/guides/getting_started/samples/intro/structure.cs | |||
[Official quick start guide]: https://github.com/RogueException/Discord.Net/blob/dev/docs/guides/getting_started/samples/first-bot/structure.cs | |||
[Task-based Asynchronous Pattern]: https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap | |||
[polymorphism]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/polymorphism | |||
[interface]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/interfaces/ | |||
@@ -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 | |||