@@ -1,10 +0,0 @@ | |||
############### | |||
# folder # | |||
############### | |||
/**/DROP/ | |||
/**/TEMP/ | |||
/**/packages/ | |||
/**/bin/ | |||
/**/obj/ | |||
_site |
@@ -1,46 +0,0 @@ | |||
# Contributing to Docs | |||
I don't really have any strict conditions for writing documentation, | |||
but just keep these few guidelines in mind: | |||
* Keep code samples in the `guides/samples` folder | |||
* When referencing an object in the API, link to it's page in the | |||
API documentation. | |||
* Documentation should be written in clear and proper English* | |||
\* If anyone is interested in translating documentation into other | |||
languages, please open an issue or contact me on | |||
Discord (`foxbot#0282`). | |||
### Layout | |||
Documentation should be written in a FAQ/Wiki style format. | |||
Recommended reads: | |||
* http://docs.microsoft.com | |||
* http://flask.pocoo.org/docs/0.12/ | |||
Style consistencies: | |||
* Use a ruler set at 70 characters | |||
* Links should use long syntax | |||
* Pages should be short and concise, not broad and long | |||
Example of long link syntax: | |||
``` | |||
Please consult the [API Documentation] for more information. | |||
[API Documentation]: xref:System.String | |||
``` | |||
### Compiling | |||
Documentation is compiled into a static site using [DocFx]. | |||
We currently use the most recent build off the dev branch. | |||
After making changes, compile your changes into the static site with | |||
`docfx`. You can also view your changes live with `docfx --serve`. | |||
[DocFx]: https://dotnet.github.io/docfx/ |
@@ -1,16 +0,0 @@ | |||
# Instructions for Building Documentation | |||
The documentation for the Discord.NET library uses [DocFX][docfx-main]. [Instructions for installing this tool can be found here.][docfx-installing] | |||
1. Navigate to the root of the repository. | |||
2. (Optional) If you intend to target a specific version, ensure that you | |||
have the correct version checked out. | |||
3. Build the library. Run `dotnet build` in the root of this repository. | |||
Ensure that the build passes without errors. | |||
4. Build the docs using `docfx .\docs\docfx.json`. Add the `--serve` parameter | |||
to preview the site locally. Some elements of the page may appear incorrect | |||
when not hosted by a server. | |||
- Remarks: According to the docfx website, this tool does work on Linux under mono. | |||
[docfx-main]: https://dotnet.github.io/docfx/ | |||
[docfx-installing]: https://dotnet.github.io/docfx/tutorial/docfx_getting_started.html |
@@ -1,6 +0,0 @@ | |||
############### | |||
# temp file # | |||
############### | |||
*.yml | |||
.manifest |
@@ -1,13 +0,0 @@ | |||
# API Documentation | |||
This is where you will find documentation for all members and objects in Discord.Net | |||
__Commonly Used Entities__ | |||
* @Discord.WebSocket | |||
* @Discord.WebSocket.DiscordSocketClient | |||
* @Discord.WebSocket.SocketGuildChannel | |||
* @Discord.WebSocket.SocketGuildUser | |||
* @Discord.WebSocket.SocketMessage | |||
* @Discord.WebSocket.SocketRole |
@@ -1,74 +0,0 @@ | |||
{ | |||
"metadata": [ | |||
{ | |||
"src": [ | |||
{ | |||
"src": "..", | |||
"files": [ | |||
"src/**/*.cs" | |||
], | |||
"exclude": [ | |||
"**/obj/**", | |||
"**/bin/**", | |||
"_site/**" | |||
] | |||
} | |||
], | |||
"dest": "api", | |||
"filter": "filterConfig.yml" | |||
} | |||
], | |||
"build": { | |||
"content": [ | |||
{ | |||
"files": [ | |||
"api/**.yml", | |||
"api/index.md" | |||
] | |||
}, | |||
{ | |||
"files": [ | |||
"guides/**.md", | |||
"guides/**/toc.yml", | |||
"toc.yml", | |||
"*.md" | |||
], | |||
"exclude": [ | |||
"obj/**", | |||
"_site/**" | |||
] | |||
} | |||
], | |||
"resource": [ | |||
{ | |||
"files": [ | |||
"**/images/**", | |||
"**/samples/**" | |||
], | |||
"exclude": [ | |||
"obj/**", | |||
"_site/**" | |||
] | |||
} | |||
], | |||
"overwrite": [ | |||
{ | |||
"files": [ | |||
"apidoc/**.md" | |||
], | |||
"exclude": [ | |||
"obj/**", | |||
"_site/**" | |||
] | |||
} | |||
], | |||
"dest": "_site", | |||
"template": [ | |||
"default" | |||
], | |||
"globalMetadata": { | |||
"_appFooter": "Discord.Net (c) 2015-2018 2.0.0-beta" | |||
}, | |||
"noLangKeyword": false | |||
} | |||
} |
@@ -1,11 +0,0 @@ | |||
apiRules: | |||
- exclude: | |||
uidRegex: ^Discord\.API$ | |||
- exclude: | |||
uidRegex: ^Discord\.API.*$ | |||
- exclude: | |||
uidRegex: ^Discord\.Net\.Converters$ | |||
- exclude: | |||
uidRegex: ^Discord\.Net.*$ | |||
- exclude: | |||
uidRegex: ^RegexAnalyzer$ |
@@ -1,343 +0,0 @@ | |||
# The Command Service | |||
[Discord.Commands](xref:Discord.Commands) provides an Attribute-based | |||
command parser. | |||
## Setup | |||
To use Commands, you must create a [Command Service] and a Command | |||
Handler. | |||
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` 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 | |||
values. | |||
[!code-csharp[Command Handler](samples/command_handler.cs)] | |||
[Command Service]: xref:Discord.Commands.CommandService | |||
[CommandServiceConfig]: xref:Discord.Commands.CommandServiceConfig | |||
## With Attributes | |||
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. | |||
### Modules | |||
The first step to creating Commands is to create a _module_. | |||
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 is being invoked. | |||
**Avoid using long-running code** in your modules wherever possible. | |||
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`. | |||
>[!NOTE] | |||
>[ModuleBase] is an _abstract_ class, meaning that you may extend it | |||
>or override it as you see fit. Your module may inherit from any | |||
>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 | |||
[Dependency Injection]: https://msdn.microsoft.com/en-us/library/ff921152.aspx | |||
[ModuleBase]: xref:Discord.Commands.ModuleBase`1 | |||
### Adding 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. | |||
Adding parameters to a Command is done by adding parameters to the | |||
parent Task. | |||
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 | |||
execute it with `!favoritefood "Key Lime Pie"`. | |||
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. | |||
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 to your Commands, and the Command parser will | |||
automatically pick up on it. | |||
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. | |||
The `Priority` attributes are sorted in ascending order; the higher | |||
priority will be called first. | |||
### Command Context | |||
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, 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 | |||
[ReplyAsync]: xref:Discord.Commands.ModuleBase`1#Discord_Commands_ModuleBase_1_ReplyAsync_System_String_System_Boolean_Discord_Embed_Discord_RequestOptions_ | |||
### Example Module | |||
At this point, your module should look comparable to this example: | |||
[!code-csharp[Example Module](samples/module.cs)] | |||
#### Loading Modules Automatically | |||
The Command Service can automatically discover all classes in an | |||
Assembly that inherit [ModuleBase] and load them. | |||
To opt a module out of auto-loading, flag it with | |||
[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_ | |||
#### 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. | |||
[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 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 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 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 one. Typically, | |||
submodules are used to create nested groups (although not required to | |||
create nested groups). | |||
[!code-csharp[Groups and Submodules](samples/groups.cs)] | |||
## With Builders | |||
**TODO** | |||
## Dependency Injection | |||
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 one if you wish. | |||
Next, add the dependencies that your modules will use to the 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 that you've passed into `LoadAssembly`. | |||
Any publicly settable properties will also be filled in the same | |||
manner. | |||
>[!NOTE] | |||
> 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` 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 | |||
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 | |||
## 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 [CheckPermissions] 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)] | |||
[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_ | |||
# 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 | |||
- IMessage/IUserMessage | |||
- IChannel/IGuildChannel/ITextChannel/IVoiceChannel/IGroupChannel | |||
- IUser/IGuildUser/IGroupUser | |||
- 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 [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. | |||
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#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 | |||
[!code-csharp[TypeReaders](samples/typereader.cs)] | |||
### Installing TypeReaders | |||
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_ |
@@ -1,63 +0,0 @@ | |||
using System; | |||
using System.Threading.Tasks; | |||
using System.Reflection; | |||
using Discord; | |||
using Discord.WebSocket; | |||
using Discord.Commands; | |||
using Microsoft.Extensions.DependencyInjection; | |||
public class Program | |||
{ | |||
private CommandService _commands; | |||
private DiscordSocketClient _client; | |||
private IServiceProvider _services; | |||
private static void Main(string[] args) => new Program().StartAsync().GetAwaiter().GetResult(); | |||
public async Task StartAsync() | |||
{ | |||
_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() | |||
.AddSingleton(_client) | |||
.AddSingleton(_commands) | |||
.BuildServiceProvider(); | |||
await InstallCommandsAsync(); | |||
await _client.LoginAsync(TokenType.Bot, token); | |||
await _client.StartAsync(); | |||
await Task.Delay(-1); | |||
} | |||
public async Task InstallCommandsAsync() | |||
{ | |||
// Hook the MessageReceived Event into our Command Handler | |||
_client.MessageReceived += HandleCommandAsync; | |||
// Discover all of the commands in this assembly and load them. | |||
await _commands.AddModulesAsync(Assembly.GetEntryAssembly()); | |||
} | |||
private async Task HandleCommandAsync(SocketMessage messageParam) | |||
{ | |||
// Don't process the command if it was a System Message | |||
var message = messageParam as SocketUserMessage; | |||
if (message == null) return; | |||
// 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; | |||
// Create a Command Context | |||
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, _services); | |||
if (!result.IsSuccess) | |||
await context.Channel.SendMessageAsync(result.ErrorReason); | |||
} | |||
} |
@@ -1,18 +0,0 @@ | |||
private IServiceProvider _services; | |||
private CommandService _commands; | |||
public async Task InstallAsync(DiscordSocketClient client) | |||
{ | |||
// 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,40 +0,0 @@ | |||
using Discord; | |||
using Discord.Commands; | |||
using Discord.WebSocket; | |||
public class ModuleA : ModuleBase | |||
{ | |||
private readonly DatabaseService _database; | |||
// Dependencies can be injected via the constructor | |||
public ModuleA(DatabaseService database) | |||
{ | |||
_database = database; | |||
} | |||
public async Task ReadFromDb() | |||
{ | |||
var x = _database.getX(); | |||
await ReplyAsync(x); | |||
} | |||
} | |||
public class ModuleB | |||
{ | |||
// Public settable properties will be injected | |||
public AnnounceService { get; set; } | |||
// Public properties without setters will not | |||
public CommandService Commands { get; } | |||
// Public properties annotated with [DontInject] will not | |||
[DontInject] | |||
public NotificationService { get; set; } | |||
public ModuleB(CommandService commands) | |||
{ | |||
Commands = commands; | |||
} | |||
} |
@@ -1,6 +0,0 @@ | |||
using Discord.Commands; | |||
public class InfoModule : ModuleBase<SocketCommandContext> | |||
{ | |||
} |
@@ -1,18 +0,0 @@ | |||
[Group("admin")] | |||
public class AdminModule : ModuleBase<SocketCommandContext> | |||
{ | |||
[Group("clean")] | |||
public class CleanModule : ModuleBase<SocketCommandContext> | |||
{ | |||
// ~admin clean 15 | |||
[Command] | |||
public async Task Default(int count = 10) => Messages(count); | |||
// ~admin clean messages 15 | |||
[Command("messages")] | |||
public async Task Messages(int count = 10) { } | |||
} | |||
// ~admin ban foxbot#0282 | |||
[Command("ban")] | |||
public async Task Ban(IGuildUser user) { } | |||
} |
@@ -1,41 +0,0 @@ | |||
// Create a module with no prefix | |||
public class Info : ModuleBase<SocketCommandContext> | |||
{ | |||
// ~say hello -> hello | |||
[Command("say")] | |||
[Summary("Echoes 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<SocketCommandContext> | |||
{ | |||
// ~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 @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 UserInfoAsync([Summary("The (optional) user to get info for")] SocketUser user = null) | |||
{ | |||
var userInfo = user ?? Context.Client.CurrentUser; | |||
await ReplyAsync($"{userInfo.Username}#{userInfo.Discriminator}"); | |||
} | |||
} |
@@ -1,24 +0,0 @@ | |||
// (Note: This precondition is obsolete, it is recommended to use the RequireOwnerAttribute that is bundled with Discord.Commands) | |||
using Discord.Commands; | |||
using Discord.WebSocket; | |||
using Microsoft.Extensions.DependencyInjection; | |||
using System; | |||
using System.Threading.Tasks; | |||
// Inherit from PreconditionAttribute | |||
public class RequireOwnerAttribute : PreconditionAttribute | |||
{ | |||
// Override the CheckPermissions method | |||
public async override Task<PreconditionResult> CheckPermissions(ICommandContext context, CommandInfo command, IServiceProvider services) | |||
{ | |||
// Get the ID of the bot's owner | |||
var ownerId = (await services.GetService<DiscordSocketClient>().GetApplicationInfoAsync()).Owner.Id; | |||
// If this command was executed by that user, return a success | |||
if (context.User.Id == ownerId) | |||
return PreconditionResult.FromSuccess(); | |||
// Since it wasn't, fail | |||
else | |||
return PreconditionResult.FromError("You must be the owner of the bot to run this command."); | |||
} | |||
} |
@@ -1,15 +0,0 @@ | |||
// Note: This example is obsolete, a boolean type reader is bundled with Discord.Commands | |||
using Discord; | |||
using Discord.Commands; | |||
public class BooleanTypeReader : TypeReader | |||
{ | |||
public override Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider services) | |||
{ | |||
bool result; | |||
if (bool.TryParse(input, out result)) | |||
return Task.FromResult(TypeReaderResult.FromSuccess(result)); | |||
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Input could not be parsed as a boolean.")); | |||
} | |||
} |
@@ -1,58 +0,0 @@ | |||
--- | |||
title: Managing Connections | |||
--- | |||
In Discord.Net, once a client has been started, it will automatically | |||
maintain a connection to Discord's gateway, until it is manually | |||
stopped. | |||
### Usage | |||
To start a connection, invoke the `StartAsync` method on a client that | |||
supports a WebSocket connection. | |||
These clients include the [DiscordSocketClient] and | |||
[DiscordRpcClient], as well as Audio clients. | |||
To end a connection, invoke the `StopAsync` method. This will | |||
gracefully close any open WebSocket or UdpSocket connections. | |||
Since the Start/Stop methods only signal to an underlying connection | |||
manager that a connection needs to be started, **they return before a | |||
connection is actually made.** | |||
As a result, you will need to hook into one of the connection-state | |||
based events to have an accurate representation of when a client is | |||
ready for use. | |||
All clients provide a `Connected` and `Disconnected` event, which is | |||
raised respectively when a connection opens or closes. In the case of | |||
the DiscordSocketClient, this does **not** mean that the client is | |||
ready to be used. | |||
A separate event, `Ready`, is provided on DiscordSocketClient, which | |||
is raised only when the client has finished guild stream or guild | |||
sync, and has a complete guild cache. | |||
[DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient | |||
[DiscordRpcClient]: xref:Discord.Rpc.DiscordRpcClient | |||
### Samples | |||
[!code-csharp[Connection Sample](samples/events.cs)] | |||
### Tips | |||
Avoid running long-running code on the gateway! If you deadlock the | |||
gateway (as explained in [events]), the connection manager will be | |||
unable to recover and reconnect. | |||
Assuming the client disconnected because of a fault on Discord's end, | |||
and not a deadlock on your end, we will always attempt to reconnect | |||
and resume a connection. | |||
Don't worry about trying to maintain your own connections, the | |||
connection manager is designed to be bulletproof and never fail - if | |||
your client doesn't manage to reconnect, you've found a bug! | |||
[events]: events.md |
@@ -1,71 +0,0 @@ | |||
--- | |||
title: Entities | |||
--- | |||
>[!NOTE] | |||
This article is written with the Socket variants of entities in mind, | |||
not the general interfaces or Rest/Rpc entities. | |||
Discord.Net provides a versatile entity system for navigating the | |||
Discord API. | |||
### Inheritance | |||
Due to the nature of the Discord API, some entities are designed with | |||
multiple variants; for example, `SocketUser` and `SocketGuildUser`. | |||
All models will contain the most detailed version of an entity | |||
possible, even if the type is less detailed. | |||
For example, in the case of the `MessageReceived` event, a | |||
`SocketMessage` is passed in with a channel property of type | |||
`SocketMessageChannel`. All messages come from channels capable of | |||
messaging, so this is the only variant of a channel that can cover | |||
every single case. | |||
But that doesn't mean a message _can't_ come from a | |||
`SocketTextChannel`, which is a message channel in a guild. To | |||
retrieve information about a guild from a message entity, you will | |||
need to cast its channel object to a `SocketTextChannel`. | |||
### Navigation | |||
All socket entities have navigation properties on them, which allow | |||
you to easily navigate to an entity's parent or children. As explained | |||
above, you will sometimes need to cast to a more detailed version of | |||
an entity to navigate to its parent. | |||
### Accessing Entities | |||
The most basic forms of entities, `SocketGuild`, `SocketUser`, and | |||
`SocketChannel` can be pulled from the DiscordSocketClient's global | |||
cache, and can be retrieved using the respective `GetXXX` method on | |||
DiscordSocketClient. | |||
>[!TIP] | |||
It is **vital** that you use the proper IDs for an entity when using | |||
a GetXXX method. It is recommended that you enable Discord's | |||
_developer mode_ to allow easy access to entity IDs, found in | |||
Settings > Appearance > Advanced | |||
More detailed versions of entities can be pulled from the basic | |||
entities, e.g. `SocketGuild.GetUser`, which returns a | |||
`SocketGuildUser`, or `SocketGuild.GetChannel`, which returns a | |||
`SocketGuildChannel`. Again, you may need to cast these objects to get | |||
a variant of the type that you need. | |||
### Samples | |||
[!code-csharp[Entity Sample](samples/entities.cs)] | |||
### Tips | |||
Avoid using boxing-casts to coerce entities into a variant, use the | |||
[`as`] keyword, and a null-conditional operator instead. | |||
This allows you to write safer code and avoid [InvalidCastExceptions]. | |||
For example, `(message.Author as SocketGuildUser)?.Nickname`. | |||
[`as`]: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/as | |||
[InvalidCastExceptions]: https://msdn.microsoft.com/en-us/library/system.invalidcastexception(v=vs.110).aspx |
@@ -1,84 +0,0 @@ | |||
--- | |||
title: Working with Events | |||
--- | |||
Events in Discord.Net are consumed in a similar manner to the standard | |||
convention, with the exception that every event must be of the type | |||
`System.Threading.Tasks.Task` and instead of using `EventArgs`, the | |||
event's parameters are passed directly into the handler. | |||
This allows for events to be handled in an async context directly | |||
instead of relying on `async void`. | |||
### Usage | |||
To receive data from an event, hook into it using C#'s delegate | |||
event pattern. | |||
You may either opt to hook an event to an anonymous function (lambda) | |||
or a named function. | |||
### Safety | |||
All events are designed to be thread-safe; events are executed | |||
synchronously off the gateway task in the same context as the gateway | |||
task. | |||
As a side effect, this makes it possible to deadlock the gateway task | |||
and kill a connection. As a general rule of thumb, any task that takes | |||
longer than three seconds should **not** be awaited directly in the | |||
context of an event, but should be wrapped in a `Task.Run` or | |||
offloaded to another task. | |||
This also means that you should not await a task that requests data | |||
from Discord's gateway in the same context of an event. Since the | |||
gateway will wait on all invoked event handlers to finish before | |||
processing any additional data from the gateway, this will create | |||
a deadlock that will be impossible to recover from. | |||
Exceptions in commands will be swallowed by the gateway and logged out | |||
through the client's log method. | |||
### Common Patterns | |||
As you may know, events in Discord.Net are only given a signature of | |||
`Func<T1, ..., Task>`. There is no room for predefined argument names, | |||
so you must either consult IntelliSense, or view the API documentation | |||
directly. | |||
That being said, there are a variety of common patterns that allow you | |||
to infer what the parameters in an event mean. | |||
#### Entity, Entity | |||
An event handler with a signature of `Func<Entity, Entity, Task>` | |||
typically means that the first object will be a clone of the entity | |||
_before_ a change was made, and the latter object will be an attached | |||
model of the entity _after_ the change was made. | |||
This pattern is typically only found on `EntityUpdated` events. | |||
#### Cacheable | |||
An event handler with a signature of `Func<Cacheable, Entity, Task>` | |||
means that the `before` state of the entity was not provided by the | |||
API, so it can either be pulled from the client's cache or | |||
downloaded from the API. | |||
See the documentation for [Cacheable] for more information on this | |||
object. | |||
[Cacheable]: xref:Discord.Cacheable`2 | |||
### Samples | |||
[!code-csharp[Event Sample](samples/events.cs)] | |||
### Tips | |||
Many events relating to a Message entity (i.e. `MessageUpdated` and | |||
`ReactionAdded`) rely on the client's message cache, which is | |||
**not** enabled by default. Set the `MessageCacheSize` flag in | |||
[DiscordSocketConfig] to enable it. | |||
[DiscordSocketConfig]: xref:Discord.WebSocket.DiscordSocketConfig |
@@ -1,47 +0,0 @@ | |||
--- | |||
title: Logging | |||
--- | |||
Discord.Net's clients provide a [Log] event that all messages will be | |||
disbatched over. | |||
For more information about events in Discord.Net, see the [Events] | |||
section. | |||
[Log]: xref:Discord.Rest.BaseDiscordClient#Discord_Rest_BaseDiscordClient_Log | |||
[Events]: events.md | |||
### Usage | |||
To receive log events, simply hook the discord client's log method | |||
to a `Task` with a single parameter of type [LogMessage]. | |||
It is recommended that you use an established function instead of a | |||
lambda for handling logs, because most addons accept a reference | |||
to a logging function to write their own messages. | |||
[LogMessage]: xref:Discord.LogMessage | |||
### Usage in Commands | |||
Discord.Net's [CommandService] also provides a log event, identical | |||
in signature to other log events. | |||
Data logged through this event is typically coupled with a | |||
[CommandException], where information about the command's context | |||
and error can be found and handled. | |||
[CommandService]: xref:Discord.Commands.CommandService | |||
[CommandException]: xref:Discord.Commands.CommandException | |||
#### Samples | |||
[!code-csharp[Logging Sample](samples/logging.cs)] | |||
#### Tips | |||
Due to the nature of Discord.Net's event system, all log event | |||
handlers will be executed synchronously on the gateway thread. If your | |||
log output will be dumped to a Web API (e.g. Sentry), you are advised | |||
to wrap your output in a `Task.Run` so the gateway thread does not | |||
become blocked while waiting for logging data to be written. |
@@ -1,23 +0,0 @@ | |||
using Discord; | |||
using Discord.WebSocket; | |||
public class Program | |||
{ | |||
private DiscordSocketClient _client; | |||
static void Main(string[] args) => new Program().MainAsync().GetAwaiter().GetResult(); | |||
public async Task MainAsync() | |||
{ | |||
_client = new DiscordSocketClient(); | |||
await _client.LoginAsync(TokenType.Bot, "bot token"); | |||
await _client.StartAsync(); | |||
Console.WriteLine("Press any key to exit..."); | |||
Console.ReadKey(); | |||
await _client.StopAsync(); | |||
// Wait a little for the client to finish disconnecting before allowing the program to return | |||
await Task.Delay(500); | |||
} | |||
} |
@@ -1,13 +0,0 @@ | |||
public string GetChannelTopic(ulong id) | |||
{ | |||
var channel = client.GetChannel(81384956881809408) as SocketTextChannel; | |||
if (channel == null) return ""; | |||
return channel.Topic; | |||
} | |||
public string GuildOwner(SocketChannel channel) | |||
{ | |||
var guild = (channel as SocketGuildChannel)?.Guild; | |||
if (guild == null) return ""; | |||
return Context.Guild.Owner.Username; | |||
} |
@@ -1,36 +0,0 @@ | |||
using Discord; | |||
using Discord.WebSocket; | |||
public class Program | |||
{ | |||
private DiscordSocketClient _client; | |||
static void Main(string[] args) => new Program().MainAsync().GetAwaiter().GetResult(); | |||
public async Task MainAsync() | |||
{ | |||
// When working with events that have Cacheable<IMessage, ulong> parameters, | |||
// you must enable the message cache in your config settings if you plan to | |||
// use the cached message entity. | |||
var _config = new DiscordSocketConfig { MessageCacheSize = 100 }; | |||
_client = new DiscordSocketClient(_config); | |||
await _client.LoginAsync(TokenType.Bot, "bot token"); | |||
await _client.StartAsync(); | |||
_client.MessageUpdated += MessageUpdated; | |||
_client.Ready += () => | |||
{ | |||
Console.WriteLine("Bot is connected!"); | |||
return Task.CompletedTask; | |||
} | |||
await Task.Delay(-1); | |||
} | |||
private async Task MessageUpdated(Cacheable<IMessage, ulong> before, SocketMessage after, ISocketMessageChannel channel) | |||
{ | |||
// If the message was not in the cache, downloading it will result in getting a copy of `after`. | |||
var message = await before.GetOrDownloadAsync(); | |||
Console.WriteLine($"{message} -> {after}"); | |||
} | |||
} |
@@ -1,29 +0,0 @@ | |||
using Discord; | |||
using Discord.WebSocket; | |||
public class Program | |||
{ | |||
private DiscordSocketClient _client; | |||
static void Main(string[] args) => new Program().MainAsync().GetAwaiter().GetResult(); | |||
public async Task MainAsync() | |||
{ | |||
_client = new DiscordSocketClient(new DiscordSocketConfig | |||
{ | |||
LogLevel = LogSeverity.Info | |||
}); | |||
_client.Log += Log; | |||
await _client.LoginAsync(TokenType.Bot, "bot token"); | |||
await _client.StartAsync(); | |||
await Task.Delay(-1); | |||
} | |||
private Task Log(LogMessage message) | |||
{ | |||
Console.WriteLine(message.ToString()); | |||
return Task.CompletedTask; | |||
} | |||
} |
@@ -1,146 +0,0 @@ | |||
--- | |||
title: Installing Discord.Net | |||
--- | |||
Discord.Net is distributed through the NuGet package manager, and it | |||
is recommended to use NuGet to get started. | |||
Optionally, you may compile from source and install 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 | |||
required. When using .NET Framework, it is suggested to target | |||
`.NET Framework 4.6.1` or higher. | |||
[.NET Standard]: https://docs.microsoft.com/en-us/dotnet/articles/standard/library | |||
[.NET Core]: https://docs.microsoft.com/en-us/dotnet/articles/core/ | |||
[additional steps]: #installing-on-net-standard-11 | |||
# Installing with NuGet | |||
Release builds of Discord.Net 1.0 will be published to the | |||
[official NuGet feed]. | |||
Development builds of Discord.Net 1.0, as well as addons *(TODO)* are | |||
published to our development [MyGet feed]. | |||
Direct feed link: `https://www.myget.org/F/discord-net/api/v3/index.json` | |||
Not sure how to add a direct feed? See how [with Visual Studio] or | |||
[without Visual Studio]. | |||
[official NuGet feed]: https://nuget.org | |||
[MyGet feed]: https://www.myget.org/feed/Packages/discord-net | |||
[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 | |||
> [!TIP] | |||
>Don't forget to change your package source if you're installing from | |||
the developer feed. | |||
>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 | |||
bot's project. | |||
3. Right click on "Dependencies", and select "Manage NuGet packages." | |||
 | |||
4. In the "Browse" tab, search for `Discord.Net`. | |||
5. Install the `Discord.Net` package. | |||
 | |||
## Using JetBrains Rider | |||
> [!TIP] | |||
Make sure to check the "Prerelease" box if installing a dev build! | |||
1. Create a new solution for your bot. | |||
2. Open the NuGet window (Tools > NuGet > Manage NuGet packages for | |||
Solution). | |||
 | |||
3. In the "Packages" tab, search for `Discord.Net`. | |||
 | |||
4. Install by adding the package to your project. | |||
 | |||
## Using Visual Studio Code | |||
> [!TIP] | |||
Don't forget to add the package source to a [NuGet.Config file] if | |||
you're installing from the developer feed. | |||
1. Create a new project for your bot. | |||
2. Add `Discord.Net` to your .csproj. | |||
[!code-xml[Sample .csproj](samples/project.csproj)] | |||
[NuGet.Config file]: #configuring-nuget-without-visual-studio | |||
# Compiling from Source | |||
In order to compile Discord.Net, you require the following: | |||
### Using Visual Studio | |||
- [Visual Studio 2017](https://www.visualstudio.com/) | |||
- [.NET Core SDK 1.0](https://www.microsoft.com/net/download/core#/sdk) | |||
The .NET Core and Docker (Preview) workload is required during Visual | |||
Studio installation. | |||
### Using Command Line | |||
- [.NET Core SDK 1.0](https://www.microsoft.com/net/download/core#/sdk) | |||
# Additional Information | |||
## 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 | |||
installed and configured. | |||
First, install the following packages through NuGet, or compile | |||
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. | |||
To do this, set the `WebSocketProvider` and the optional | |||
`UdpSocketProvider` properties on the [DiscordSocketConfig] that you | |||
are passing into your client. | |||
[!code-csharp[NET Standard 1.1 Example](samples/netstd11.cs)] | |||
[DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient | |||
[DiscordSocketConfig]: xref:Discord.WebSocket.DiscordSocketConfig | |||
## 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. | |||
Paste the following snippets into this configuration file, adding any | |||
additional feeds as necessary. | |||
[!code-xml[NuGet Configuration](samples/nuget.config)] |
@@ -1,237 +0,0 @@ | |||
--- | |||
title: Getting Started | |||
--- | |||
# 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. | |||
## Creating a Discord Bot | |||
Before you can begin writing your bot, it is necessary to create a bot | |||
account on Discord. | |||
1. Visit the [Discord Applications Portal]. | |||
2. Create a New Application. | |||
3. Give the application a name (this will be the bot's initial | |||
username). | |||
4. Create the Application. | |||
 | |||
5. In the application review page, click **Create a Bot User**. | |||
 | |||
6. Confirm the popup. | |||
7. If this bot will be public, check "Public Bot." **Do not tick any | |||
other options!** | |||
[Discord Applications Portal]: https://discordapp.com/developers/applications/me | |||
## Adding your bot to a server | |||
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**. | |||
 | |||
3. Create an OAuth2 authorization URL | |||
`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. | |||
>[!NOTE] | |||
Only servers where you have the `MANAGE_SERVER` permission will be | |||
present in this list. | |||
 | |||
## Connecting to Discord | |||
If you have not already created a project and installed Discord.Net, | |||
do that now. (see the [Installing](installing.md) section) | |||
### Async | |||
Discord.Net uses .NET's [Task-based Asynchronous Pattern (TAP)] | |||
extensively - nearly every operation is asynchronous. | |||
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 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)] | |||
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 | |||
correct async implementation. | |||
>[!TIP] | |||
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. | |||
[Task-based Asynchronous Pattern (TAP)]: https://docs.microsoft.com/en-us/dotnet/articles/csharp/async | |||
### Creating a logging method | |||
Before we create and configure a Discord client, we will add a method | |||
to handle Discord.Net's log events. | |||
To allow agnostic support of as many log providers as possible, we | |||
log information through a `Log` event with a proprietary `LogMessage` | |||
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. | |||
[!code-csharp[Async Context](samples/intro/logging.cs)] | |||
[API Documentation]: xref:Discord.Rest.BaseDiscordClient#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](terminology.md) if you're 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 | |||
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. | |||
Next, you will need to "login to Discord" with the `LoginAsync` | |||
method. | |||
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]). | |||
 | |||
>[!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 | |||
the source code for your bot. | |||
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!** | |||
Any methods that rely on the client's state should go in an event | |||
handler. | |||
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. | |||
The following lines can now be added: | |||
[!code-csharp[Create client](samples/intro/client.cs)] | |||
At this point, feel free to start your program and see your bot come | |||
online in Discord. | |||
>[!TIP] | |||
Encountering a `PlatformNotSupportedException` when starting your bot? | |||
This means that you are targeting a platform where .NET's default | |||
WebSocket client is not supported. Refer to the [installation guide] | |||
for how to fix this. | |||
[DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient | |||
[installation guide]: installing.md#installing-on-net-standard-11 | |||
### Handling a 'ping' | |||
>[!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. | |||
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!". | |||
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 | |||
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`. | |||
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 | |||
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!". | |||
You should have now added the following lines: | |||
[!code-csharp[Message](samples/intro/message.cs)] | |||
Now 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]. | |||
[MessageReceived]: xref:Discord.WebSocket.DiscordSocketClient#Discord_WebSocket_DiscordSocketClient_MessageReceived | |||
[SocketMessage]: xref:Discord.WebSocket.SocketMessage | |||
[SocketMessageChannel]: xref:Discord.WebSocket.ISocketMessageChannel | |||
[completed program]: samples/intro/complete.cs | |||
[Command Guide]: ../commands/commands.md | |||
# Building a bot with commands | |||
This section will show you how to write a program that is ready for | |||
[Commands](../commands/commands.md). Note that we will not be | |||
explaining _how_ to write Commands or Services, it will only be | |||
covering the general structure. | |||
For reference, view an [annotated example] of this structure. | |||
[annotated example]: samples/intro/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). | |||
**todo:** diagram of bot structure |
@@ -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,17 +0,0 @@ | |||
// 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! | |||
await _client.LoginAsync(TokenType.Bot, token); | |||
await _client.StartAsync(); | |||
// Block this task until the program is closed. | |||
await Task.Delay(-1); | |||
} |
@@ -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,14 +0,0 @@ | |||
public async Task MainAsync() | |||
{ | |||
// client.Log ... | |||
_client.MessageReceived += MessageReceived; | |||
// ... | |||
} | |||
private async Task MessageReceived(SocketMessage message) | |||
{ | |||
if (message.Content == "!ping") | |||
{ | |||
await message.Channel.SendMessageAsync("Pong!"); | |||
} | |||
} |
@@ -1,173 +0,0 @@ | |||
using System; | |||
using System.Reflection; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using Microsoft.Extensions.DependencyInjection; | |||
using Discord; | |||
using Discord.Commands; | |||
using Discord.WebSocket; | |||
class Program | |||
{ | |||
// Program entry point | |||
static void Main(string[] args) | |||
{ | |||
// Call the Program constructor, followed by the | |||
// MainAsync method and wait until it finishes (which should be never). | |||
new Program().MainAsync().GetAwaiter().GetResult(); | |||
} | |||
private readonly DiscordSocketClient _client; | |||
// Keep the CommandService and DI container around for use with commands. | |||
// These two types require you install the Discord.Net.Commands package. | |||
private readonly CommandService _commands; | |||
private readonly IServiceProvider _services; | |||
private Program() | |||
{ | |||
_client = new DiscordSocketClient(new DiscordSocketConfig | |||
{ | |||
// How much logging do you want to see? | |||
LogLevel = LogSeverity.Info, | |||
// If you or another service needs to do anything with messages | |||
// (eg. checking Reactions, checking the content of edited/deleted messages), | |||
// you must set the MessageCacheSize. You may adjust the number as needed. | |||
//MessageCacheSize = 50, | |||
// If your platform doesn't have native websockets, | |||
// add Discord.Net.Providers.WS4Net from NuGet, | |||
// add the `using` at the top, and uncomment this line: | |||
//WebSocketProvider = WS4NetProvider.Instance | |||
}); | |||
_commands = new CommandService(new CommandServiceConfig | |||
{ | |||
// Again, log level: | |||
LogLevel = LogSeverity.Info, | |||
// There's a few more properties you can set, | |||
// for example, case-insensitive commands. | |||
CaseSensitiveCommands = false, | |||
}); | |||
// Subscribe the logging handler to both the client and the CommandService. | |||
_client.Log += Log; | |||
_commands.Log += Log; | |||
// Setup your DI container. | |||
_services = ConfigureServices(), | |||
} | |||
// If any services require the client, or the CommandService, or something else you keep on hand, | |||
// pass them as parameters into this method as needed. | |||
// If this method is getting pretty long, you can seperate it out into another file using partials. | |||
private static IServiceProvider ConfigureServices() | |||
{ | |||
var map = new ServiceCollection() | |||
// Repeat this for all the service classes | |||
// and other dependencies that your commands might need. | |||
.AddSingleton(new SomeServiceClass()); | |||
// When all your required services are in the collection, build the container. | |||
// Tip: There's an overload taking in a 'validateScopes' bool to make sure | |||
// you haven't made any mistakes in your dependency graph. | |||
return map.BuildServiceProvider(); | |||
} | |||
// Example of a logging handler. This can be re-used by addons | |||
// that ask for a Func<LogMessage, Task>. | |||
private static Task Log(LogMessage message) | |||
{ | |||
switch (message.Severity) | |||
{ | |||
case LogSeverity.Critical: | |||
case LogSeverity.Error: | |||
Console.ForegroundColor = ConsoleColor.Red; | |||
break; | |||
case LogSeverity.Warning: | |||
Console.ForegroundColor = ConsoleColor.Yellow; | |||
break; | |||
case LogSeverity.Info: | |||
Console.ForegroundColor = ConsoleColor.White; | |||
break; | |||
case LogSeverity.Verbose: | |||
case LogSeverity.Debug: | |||
Console.ForegroundColor = ConsoleColor.DarkGray; | |||
break; | |||
} | |||
Console.WriteLine($"{DateTime.Now,-19} [{message.Severity,8}] {message.Source}: {message.Message} {message.Exception}"); | |||
Console.ResetColor(); | |||
// If you get an error saying 'CompletedTask' doesn't exist, | |||
// your project is targeting .NET 4.5.2 or lower. You'll need | |||
// to adjust your project's target framework to 4.6 or higher | |||
// (instructions for this are easily Googled). | |||
// If you *need* to run on .NET 4.5 for compat/other reasons, | |||
// the alternative is to 'return Task.Delay(0);' instead. | |||
return Task.CompletedTask; | |||
} | |||
private async Task MainAsync() | |||
{ | |||
// Centralize the logic for commands into a separate method. | |||
await InitCommands(); | |||
// Login and connect. | |||
await _client.LoginAsync(TokenType.Bot, /* <DON'T HARDCODE YOUR TOKEN> */); | |||
await _client.StartAsync(); | |||
// Wait infinitely so your bot actually stays connected. | |||
await Task.Delay(Timeout.Infinite); | |||
} | |||
private async Task InitCommands() | |||
{ | |||
// Either search the program and add all Module classes that can be found. | |||
// Module classes MUST be marked 'public' or they will be ignored. | |||
// You also need to pass your 'IServiceProvider' instance now, | |||
// so make sure that's done before you get here. | |||
await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services); | |||
// Or add Modules manually if you prefer to be a little more explicit: | |||
await _commands.AddModuleAsync<SomeModule>(_services); | |||
// Note that the first one is 'Modules' (plural) and the second is 'Module' (singular). | |||
// Subscribe a handler to see if a message invokes a command. | |||
_client.MessageReceived += HandleCommandAsync; | |||
} | |||
private async Task HandleCommandAsync(SocketMessage arg) | |||
{ | |||
// Bail out if it's a System Message. | |||
var msg = arg as SocketUserMessage; | |||
if (msg == null) return; | |||
// We don't want the bot to respond to itself or other bots. | |||
if (msg.Author.Id == _client.CurrentUser.Id || msg.Author.IsBot) return; | |||
// Create a number to track where the prefix ends and the command begins | |||
int pos = 0; | |||
// Replace the '!' with whatever character | |||
// you want to prefix your commands with. | |||
// Uncomment the second half if you also want | |||
// commands to be invoked by mentioning the bot instead. | |||
if (msg.HasCharPrefix('!', ref pos) /* || msg.HasMentionPrefix(_client.CurrentUser, ref pos) */) | |||
{ | |||
// Create a Command Context. | |||
var context = new SocketCommandContext(_client, msg); | |||
// 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, pos); | |||
// Uncomment the following lines if you want the bot | |||
// to send a message if it failed. | |||
// This does not catch errors from commands with 'RunMode.Async', | |||
// subscribe a handler for '_commands.CommandExecuted' to see those. | |||
//if (!result.IsSuccess && result.Error != CommandError.UnknownCommand) | |||
// await msg.Channel.SendMessageAsync(result.ErrorReason); | |||
} | |||
} | |||
} |
@@ -1,9 +0,0 @@ | |||
using Discord.Providers.WS4Net; | |||
using Discord.Providers.UDPClient; | |||
using Discord.WebSocket; | |||
// ... | |||
var client = new DiscordSocketClient(new DiscordSocketConfig | |||
{ | |||
WebSocketProvider = WS4NetProvider.Instance, | |||
UdpSocketProvider = UDPClientProvider.Instance, | |||
}); |
@@ -1,6 +0,0 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<configuration> | |||
<packageSources> | |||
<add key="discord.net ci feed" value="https://www.myget.org/F/discord-net/api/v3/index.json" /> | |||
</packageSources> | |||
</configuration> |
@@ -1,13 +0,0 @@ | |||
<Project Sdk="Microsoft.NET.Sdk"> | |||
<PropertyGroup> | |||
<OutputType>Exe</OutputType> | |||
<TargetFramework>netcoreapp1.1</TargetFramework> | |||
<NoWin32Manifest>true</NoWin32Manifest> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Discord.Net" Version="1.*" /> | |||
</ItemGroup> | |||
</Project> |
@@ -1,40 +0,0 @@ | |||
--- | |||
uid: Terminology | |||
title: Terminology | |||
--- | |||
# Terminology | |||
## Preface | |||
Most terms for objects remain the same between 0.9 and 1.0. The major | |||
difference is that the ``Server`` is now called ``Guild`` to stay in | |||
line with Discord internally. | |||
## Implementation Specific Entities | |||
Discord.Net 1.0 is split into a core library and three different | |||
implementations - `Discord.Net.Core`, `Discord.Net.Rest`, | |||
`Discord.Net.Rpc`, and `Discord.Net.WebSockets`. | |||
As a bot developer, you will only need to use `Discord.Net.WebSockets`, | |||
but you should be aware of the differences between them. | |||
`Discord.Net.Core` provides a set of interfaces that models Discord's | |||
API. These interfaces are consistent throughout all implementations of | |||
Discord.Net, and if you are writing an implementation-agnostic library | |||
or addon, you can rely on the core interfaces to ensure that your | |||
addon will run on all platforms. | |||
`Discord.Net.Rest` provides a set of concrete classes to be used | |||
**strictly** with the REST portion of Discord's API. Entities in this | |||
implementation are prefixed with `Rest` (e.g. `RestChannel`). | |||
`Discord.Net.Rpc` provides a set of concrete classes that are used | |||
with Discord's RPC API. Entities in this implementation are prefixed | |||
with `Rpc` (e.g. `RpcChannel`). | |||
`Discord.Net.WebSocket` provides a set of concrete classes that are | |||
used primarily with Discord's WebSocket API or entities that are kept | |||
in cache. When developing bots, you will be using this implementation. | |||
All entities are prefixed with `Socket` (e.g. `SocketChannel`). |
@@ -1,61 +0,0 @@ | |||
# Migrating from 0.9 | |||
**1.0.0 is the biggest breaking change the library has gone through, due to massive | |||
changes in the design of the library.** | |||
>A medium to advanced understanding is recommended when working with this library. | |||
It is recommended to familiarize yourself with the entities in 1.0 before continuing. | |||
Feel free to look through the library's source directly, look through IntelliSense, or | |||
look through our hosted [API Documentation](xref:Discord). | |||
## Entities | |||
Most API models function _similarly_ to 0.9, however their names have been changed. | |||
You should also keep in mind that we now separate different types of Channels and Users. | |||
Before proceeding, please read over @Terminology to understand the naming behind some objects. | |||
Below is a table that compares most common 0.9 entities to their 1.0 counterparts. | |||
>This should be used mostly for migration purposes. Please take some time to consider whether | |||
>or not you are using the right "tool for the job" when working with 1.0 | |||
| 0.9 | 1.0 | Notice | | |||
| --- | --- | ------ | | |||
| Server | @Discord.WebSocket.SocketGuild | | |||
| Channel | @Discord.WebSocket.SocketGuildChannel | Applies only to channels that are members of a Guild | | |||
| Channel.IsPrivate | @Discord.WebSocket.SocketDMChannel | |||
| ChannelType.Text | @Discord.WebSocket.SocketTextChannel | This applies only to Text Channels in Guilds | |||
| ChannelType.Voice | @Discord.WebSocket.SocketVoiceChannel | This applies only to Voice Channels in Guilds | |||
| User | @Discord.WebSocket.SocketGuildUser | This applies only to users belonging to a Guild* | |||
| Profile | @Discord.WebSocket.SocketGuildUser | |||
| Message | @Discord.WebSocket.SocketUserMessage | |||
\* To retrieve an @Discord.WebSocket.SocketGuildUser, you must retrieve the user from an @Discord.WebSocket.SocketGuild. | |||
## Event Registration | |||
Prior to 1.0, events were registered using the standard c# `Handler(EventArgs)` pattern. In 1.0, | |||
events are delegates, but are still registered the same. | |||
For example, let's look at [DiscordSocketClient.MessageReceived](xref:Discord.WebSocket.DiscordSocketClient#Discord_WebSocket_DiscordSocketClient_MessageReceived) | |||
To hook an event into MessageReceived, we now use the following code: | |||
[!code-csharp[Event Registration](samples/event.cs)] | |||
> **All Event Handlers in 1.0 MUST return Task!** | |||
If your event handler is marked as `async`, it will automatically return `Task`. However, | |||
if you do not need to execute asynchronus code, do _not_ mark your handler as `async`, and instead, | |||
stick a `return Task.CompletedTask` at the bottom. | |||
[!code-csharp[Sync Event Registration](samples/sync_event.cs)] | |||
**Event handlers no longer require a sender.** The only arguments your event handler needs to accept | |||
are the parameters used by the event. It is recommended to look at the event in IntelliSense or on the | |||
API docs before implementing it. | |||
## Async | |||
Nearly everything in 1.0 is an async Task. You should always await any tasks you invoke. |
@@ -1,4 +0,0 @@ | |||
_client.MessageReceived += async (msg) => | |||
{ | |||
await msg.Channel.SendMessageAsync(msg.Content); | |||
} |
@@ -1,5 +0,0 @@ | |||
_client.Log += (msg) => | |||
{ | |||
Console.WriteLine(msg.ToString()); | |||
return Task.CompletedTask; | |||
} |
@@ -1,27 +0,0 @@ | |||
- name: Getting Started | |||
items: | |||
- name: Installation | |||
href: getting_started/installing.md | |||
- name: Your First Bot | |||
href: getting_started/intro.md | |||
- name: Terminology | |||
href: getting_started/terminology.md | |||
- name: Basic Concepts | |||
items: | |||
- name: Logging Data | |||
href: concepts/logging.md | |||
- name: Working with Events | |||
href: concepts/events.md | |||
- name: Managing Connections | |||
href: concepts/connections.md | |||
- name: Entities | |||
href: concepts/entities.md | |||
- name: The Command Service | |||
items: | |||
- name: Command Guide | |||
href: commands/commands.md | |||
- name: Voice | |||
items: | |||
- name: Voice Guide | |||
href: voice/sending-voice.md | |||
- name: Migrating from 0.9 |
@@ -1,10 +0,0 @@ | |||
private Process CreateStream(string path) | |||
{ | |||
return Process.Start(new ProcessStartInfo | |||
{ | |||
FileName = "ffmpeg", | |||
Arguments = $"-hide_banner -loglevel panic -i \"{path}\" -ac 2 -f s16le -ar 48000 pipe:1", | |||
UseShellExecute = false, | |||
RedirectStandardOutput = true, | |||
}); | |||
} |
@@ -1,11 +0,0 @@ | |||
private async Task SendAsync(IAudioClient client, string path) | |||
{ | |||
// Create FFmpeg using the previous example | |||
using (var ffmpeg = CreateStream(path)) | |||
using (var output = ffmpeg.StandardOutput.BaseStream) | |||
using (var discord = client.CreatePCMStream(AudioApplication.Mixed)) | |||
{ | |||
try { await output.CopyToAsync(discord); } | |||
finally { await discord.FlushAsync(); } | |||
} | |||
} |
@@ -1,10 +0,0 @@ | |||
[Command("join")] | |||
public async Task JoinChannel(IVoiceChannel channel = null) | |||
{ | |||
// Get the audio channel | |||
channel = channel ?? (msg.Author as IGuildUser)?.VoiceChannel; | |||
if (channel == null) { await msg.Channel.SendMessageAsync("User must be in a voice channel, or a voice channel must be passed as an argument."); return; } | |||
// For the next step with transmitting audio, you would want to pass this Audio Client in to a service. | |||
var audioClient = await channel.ConnectAsync(); | |||
} |
@@ -1,101 +0,0 @@ | |||
--- | |||
title: Sending Voice | |||
--- | |||
**Information on this page is subject to change!** | |||
>[!WARNING] | |||
>This article is out of date, and has not been rewritten yet. | |||
Information is not guaranteed to be accurate. | |||
## Installing | |||
Audio requires two native libraries, `libsodium` and `opus`. | |||
Both of these libraries must be placed in the runtime directory of your | |||
bot. (When developing on .NET Framework, this would be `bin/debug`, | |||
when developing on .NET Core, this is where you execute `dotnet run` | |||
from; typically the same directory as your csproj). | |||
For Windows Users, precompiled binaries are available for your | |||
convienence [here](https://discord.foxbot.me/binaries/). | |||
For Linux Users, you will need to compile [Sodium] and [Opus] from | |||
source, or install them from your package manager. | |||
[Sodium]: https://download.libsodium.org/libsodium/releases/ | |||
[Opus]: http://downloads.xiph.org/releases/opus/ | |||
## Joining a Channel | |||
Joining a channel is the first step to sending audio, and will return | |||
an [IAudioClient] to send data with. | |||
To join a channel, simply await [ConnectAsync] on any instance of an | |||
@Discord.IAudioChannel. | |||
[!code-csharp[Joining a Channel](samples/joining_audio.cs)] | |||
The client will sustain a connection to this channel until it is | |||
kicked, disconnected from Discord, or told to disconnect. | |||
It should be noted that voice connections are created on a per-guild | |||
basis; only one audio connection may be open by the bot in a single | |||
guild. To switch channels within a guild, invoke [ConnectAsync] on | |||
another voice channel in the guild. | |||
[IAudioClient]: xref:Discord.Audio.IAudioClient | |||
[ConnectAsync]: xref:Discord.IAudioChannel#Discord_IAudioChannel_ConnectAsync_Action_IAudioClient__ | |||
## Transmitting Audio | |||
### With FFmpeg | |||
[FFmpeg] is an open source, highly versatile AV-muxing tool. This is | |||
the recommended method of transmitting audio. | |||
Before you begin, you will need to have a version of FFmpeg downloaded | |||
and placed somewhere in your PATH (or alongside the bot, in the same | |||
location as libsodium and opus). Windows binaries are available on | |||
[FFmpeg's download page]. | |||
[FFmpeg]: https://ffmpeg.org/ | |||
[FFmpeg's download page]: https://ffmpeg.org/download.html | |||
First, you will need to create a Process that starts FFmpeg. An | |||
example of how to do this is included below, though it is important | |||
that you return PCM at 48000hz. | |||
>[!NOTE] | |||
>As of the time of this writing, Discord.Audio struggles significantly | |||
>with processing audio that is already opus-encoded; you will need to | |||
>use the PCM write streams. | |||
[!code-csharp[Creating FFmpeg](samples/audio_create_ffmpeg.cs)] | |||
Next, to transmit audio from FFmpeg to Discord, you will need to | |||
pull an [AudioOutStream] from your [IAudioClient]. Since we're using | |||
PCM audio, use [IAudioClient.CreatePCMStream]. | |||
The sample rate argument doesn't particularly matter, so long as it is | |||
a valid rate (120, 240, 480, 960, 1920, or 2880). For the sake of | |||
simplicity, I recommend using 1920. | |||
Channels should be left at `2`, unless you specified a different value | |||
for `-ac 2` when creating FFmpeg. | |||
[AudioOutStream]: xref:Discord.Audio.AudioOutStream | |||
[IAudioClient.CreatePCMStream]: xref:Discord.Audio.IAudioClient#Discord_Audio_IAudioClient_CreateDirectPCMStream_Discord_Audio_AudioApplication_System_Nullable_System_Int32__System_Int32_ | |||
Finally, audio will need to be piped from FFmpeg's stdout into your | |||
AudioOutStream. This step can be as complex as you'd like it to be, but | |||
for the majority of cases, you can just use [Stream.CopyToAsync], as | |||
shown below. | |||
[Stream.CopyToAsync]: https://msdn.microsoft.com/en-us/library/hh159084(v=vs.110).aspx | |||
If you are implementing a queue for sending songs, it's likely that | |||
you will want to wait for audio to stop playing before continuing on | |||
to the next song. You can await `AudioOutStream.FlushAsync` to wait for | |||
the audio client's internal buffer to clear out. | |||
[!code-csharp[Sending Audio](samples/audio_ffmpeg.cs)] |
@@ -1,13 +0,0 @@ | |||
# Discord.Net Documentation | |||
Discord.Net is an asynchronous, multiplatform .NET Library used to interface with the [Discord API](https://discordapp.com/). | |||
If this is your first time using Discord.Net, you should refer to the [Intro](guides/getting_started/intro.md) for tutorials. | |||
More experienced users might refer to the [API Documentation](api/index.md) for a breakdown of the individuals objects in the library. | |||
For additional resources: | |||
- [Discord API Guild](https://discord.gg/discord-api) - Look for `#dotnet_discord-net` | |||
- [GitHub](https://github.com/RogueException/Discord.Net/tree/dev) | |||
- [NuGet](https://www.nuget.org/packages/Discord.Net/) | |||
- [MyGet Feed](https://www.myget.org/feed/Packages/discord-net) - Addons and nightly builds |
@@ -1,6 +0,0 @@ | |||
- name: Guides | |||
href: guides/ | |||
- name: API Documentation | |||
href: api/ | |||
homepage: api/index.md |