Added docs

This commit is contained in:
Toastie 2024-06-13 17:43:59 +12:00
parent a5ca9149cd
commit 776e2157f5
Signed by: toastie_t0ast
GPG key ID: 27F3B6855AFD40A4
279 changed files with 10469 additions and 0 deletions

10
docs/.gitignore vendored Normal file
View file

@ -0,0 +1,10 @@
###############
# folder #
###############
/**/DROP/
/**/TEMP/
/**/packages/
/**/bin/
/**/obj/
_site

60
docs/CONTRIBUTING.md Normal file
View file

@ -0,0 +1,60 @@
# Contributing to Docs
First of all, thank you for your interest in contributing to our
documentation work. We really appreciate it! That being said,
there are several guidelines you should attempt to follow when adding
to/changing the documentation.
## General Guidelines
* Keep code samples in each section's `samples` folder
* When referencing an object in the API, link to its page in the
API documentation
* Documentation should be written in an FAQ/Wiki-style format
* 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 `foxbot#0282` on
Discord.
## XML Docstrings Guidelines
* When using the `<summary>` tag, use concise verbs. For example:
```cs
/// <summary> Gets or sets the guild user in this object. </summary>
public IGuildUser GuildUser { get; set; }
```
* The `<summary>` tag should not be more than 3 lines long. Consider
simplifying the terms or using the `<remarks>` tag instead.
* When using the `<code>` tag, put the code sample within the
`src/Discord.Net.Examples` project under the corresponding path of
the object and surround the code with a `#region` tag.
* If the remarks you are looking to write are too long, consider
writing a shorter version in the XML docs while keeping the longer
version in the `overwrites` folder using the DocFX overwrites syntax.
* You may find an example of this in the samples provided within
the folder.
## Docs Guide Guidelines
* Use a ruler set at 70 characters (use the docs workspace provided
if you are using Visual Studio Code)
* Links should use long syntax
* Pages should be short and concise, not broad and long
Example of long link syntax:
```md
Please consult the [API Documentation] for more information.
[API Documentation]: xref:System.String
```
## Recommended Reads
* [Microsoft Docs](https://docs.microsoft.com)
* [Flask Docs](https://flask.pocoo.org/docs/1.0/)
* [DocFX Manual](https://dotnet.github.io/docfx/)
* [Sandcastle XML Guide](http://ewsoftware.github.io/XMLCommentsGuide)

View file

@ -0,0 +1,21 @@
{
"folders": [
{
"path": "."
}
],
"settings": {
"editor.rulers": [
70
],
"files.exclude": {
"**/.git": true,
"**/.svn": true,
"**/.hg": true,
"**/CVS": true,
"**/.DS_Store": true,
"obj/": true,
"_site/": true,
}
}
}

17
docs/README.md Normal file
View file

@ -0,0 +1,17 @@
# 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].
> [!IMPORTANT]
> You must use DocFX version **2.76.0** for everything to work correctly.
1. Navigate to the root of the repository.
2. Build the docs using `docfx docs/docfx.json`. Add the `--serve`
parameter to preview the site locally.
Please note that if you intend to target a specific version, ensure
that you have the correct version checked out.
[docfx-main]: https://dotnet.github.io/docfx/
[docfx-installing]: https://dotnet.github.io/docfx/index.html

View file

@ -0,0 +1,31 @@
---
uid: Discord.Commands.CommandException
remarks: *content
---
This @System.Exception class is typically used when diagnosing
an error thrown during the execution of a command. You will find the
thrown exception passed into
[LogMessage.Exception](xref:Discord.LogMessage.Exception), which is
sent to your [CommandService.Log](xref:Discord.Commands.CommandService.Log)
event handler.
---
uid: Discord.Commands.CommandException
example: [*content]
---
You may use this information to handle runtime exceptions after
execution. Below is an example of how you may use this:
```cs
public Task LogHandlerAsync(LogMessage logMessage)
{
// Note that this casting method requires C#7 and up.
if (logMessage?.Exception is CommandException cmdEx)
{
Console.WriteLine($"{cmdEx.GetBaseException().GetType()} was thrown while executing {cmdEx.Command.Aliases.First()} in {cmdEx.Context.Channel} by {cmdEx.Context.User}.");
}
return Task.CompletedTask;
}
```

View file

@ -0,0 +1,22 @@
---
uid: Discord.Commands.DontAutoLoadAttribute
remarks: *content
---
The attribute can be applied to a public class that inherits
@Discord.Commands.ModuleBase. By applying this attribute,
@Discord.Commands.CommandService.AddModulesAsync* will not discover and
add the marked module to the CommandService.
---
uid: Discord.Commands.DontAutoLoadAttribute
example: [*content]
---
```cs
[DontAutoLoad]
public class MyModule : ModuleBase<SocketCommandContext>
{
// ...
}
```

View file

@ -0,0 +1,27 @@
---
uid: Discord.Commands.DontInjectAttribute
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 @Guides.Commands.DI to learn more.
---
uid: Discord.Commands.DontInjectAttribute
example: [*content]
---
```cs
public class MyModule : ModuleBase<SocketCommandContext>
{
[DontInject]
public MyService MyService { get; set; }
public MyModule()
{
MyService = new MyService();
}
}
```

View file

@ -0,0 +1,5 @@
An example of how this class is used the command system can be seen
below:
[!code[Sample module](../../guides/text_commands/samples/intro/empty-module.cs)]
[!code[Command handler](../../guides/text_commands/samples/intro/command_handler.cs)]

View file

@ -0,0 +1,27 @@
---
uid: Discord.Commands.ICommandContext
example: [*content]
---
[!include[Example Section](ICommandContext.Inclusion.md)]
---
uid: Discord.Commands.CommandContext
example: [*content]
---
[!include[Example Section](ICommandContext.Inclusion.md)]
---
uid: Discord.Commands.SocketCommandContext
example: [*content]
---
[!include[Example Section](ICommandContext.Inclusion.md)]
---
uid: Discord.Commands.ShardCommandContext
example: [*content]
---
[!include[Example Section](ICommandContext.Inclusion.md)]

View file

@ -0,0 +1,103 @@
---
uid: Discord.Commands.PreconditionAttribute
remarks: *content
---
This precondition attribute can be applied on module-level or
method-level for a command.
[!include[Additional Remarks](PreconditionAttribute.Remarks.Inclusion.md)]
---
uid: Discord.Commands.ParameterPreconditionAttribute
remarks: *content
---
This precondition attribute can be applied on parameter-level for a
command.
[!include[Additional Remarks](PreconditionAttribute.Remarks.Inclusion.md)]
---
uid: Discord.Commands.PreconditionAttribute
example: [*content]
---
The following example creates a precondition to see if the user has
sufficient role required to access the command.
```cs
public class RequireRoleAttribute : PreconditionAttribute
{
private readonly ulong _roleId;
public RequireRoleAttribute(ulong roleId)
{
_roleId = roleId;
}
public override async Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context,
CommandInfo command, IServiceProvider services)
{
var guildUser = context.User as IGuildUser;
if (guildUser == null)
return PreconditionResult.FromError("This command cannot be executed outside of a guild.");
var guild = guildUser.Guild;
if (guild.Roles.All(r => r.Id != _roleId))
return PreconditionResult.FromError(
$"The guild does not have the role ({_roleId}) required to access this command.");
return guildUser.RoleIds.Any(rId => rId == _roleId)
? PreconditionResult.FromSuccess()
: PreconditionResult.FromError("You do not have the sufficient role required to access this command.");
}
}
```
---
uid: Discord.Commands.ParameterPreconditionAttribute
example: [*content]
---
The following example creates a precondition on a parameter-level to
see if the targeted user has a lower hierarchy than the user who
executed the command.
```cs
public class RequireHierarchyAttribute : ParameterPreconditionAttribute
{
public override async Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context,
ParameterInfo parameter, object value, IServiceProvider services)
{
// Hierarchy is only available under the socket variant of the user.
if (!(context.User is SocketGuildUser guildUser))
return PreconditionResult.FromError("This command cannot be used outside of a guild.");
SocketGuildUser targetUser;
switch (value)
{
case SocketGuildUser targetGuildUser:
targetUser = targetGuildUser;
break;
case ulong userId:
targetUser = await context.Guild.GetUserAsync(userId).ConfigureAwait(false) as SocketGuildUser;
break;
default:
throw new ArgumentOutOfRangeException();
}
if (targetUser == null)
return PreconditionResult.FromError("Target user not found.");
if (guildUser.Hierarchy < targetUser.Hierarchy)
return PreconditionResult.FromError("You cannot target anyone else whose roles are higher than yours.");
var currentUser = await context.Guild.GetCurrentUserAsync().ConfigureAwait(false) as SocketGuildUser;
if (currentUser?.Hierarchy < targetUser.Hierarchy)
return PreconditionResult.FromError("The bot's role is lower than the targeted user.");
return PreconditionResult.FromSuccess();
}
}
```

View file

@ -0,0 +1,6 @@
A "precondition" in the command system is used to determine if a
condition is met before entering the command task. Using a
precondition may aid in keeping a well-organized command logic.
The most common use case being whether a user has sufficient
permission to execute the command.

View file

@ -0,0 +1,29 @@
---
uid: Discord.DiscordComparers.ChannelComparer
summary: *content
---
Gets an [IEqualityComparer](xref:System.Collections.Generic.IEqualityComparer`1)<<xref:Discord.IChannel>> to be used to compare channels.
---
uid: Discord.DiscordComparers.GuildComparer
summary: *content
---
Gets an [IEqualityComparer](xref:System.Collections.Generic.IEqualityComparer`1)<<xref:Discord.IGuild>> to be used to compare guilds.
---
uid: Discord.DiscordComparers.MessageComparer
summary: *content
---
Gets an [IEqualityComparer](xref:System.Collections.Generic.IEqualityComparer`1)<<xref:Discord.IMessage>> to be used to compare messages.
---
uid: Discord.DiscordComparers.RoleComparer
summary: *content
---
Gets an [IEqualityComparer](xref:System.Collections.Generic.IEqualityComparer`1)<<xref:Discord.IRole>> to be used to compare roles.
---
uid: Discord.DiscordComparers.UserComparer
summary: *content
---
Gets an [IEqualityComparer](xref:System.Collections.Generic.IEqualityComparer`1)<<xref:Discord.IUser>> to be used to compare users.

View file

@ -0,0 +1,69 @@
---
uid: Discord.EmbedBuilder
seealso:
- linkId: Discord.EmbedFooterBuilder
- linkId: Discord.EmbedAuthorBuilder
- linkId: Discord.EmbedFieldBuilder
remarks: *content
---
This builder class is used to build an @Discord.Embed (rich embed)
object that will be ready to be sent via @Discord.IMessageChannel.SendMessageAsync*
after @Discord.EmbedBuilder.Build* is called.
---
uid: Discord.EmbedBuilder
example: [*content]
---
#### Basic Usage
The example below builds an embed and sends it to the chat using the
command system.
```cs
[Command("embed")]
public async Task SendRichEmbedAsync()
{
var embed = new EmbedBuilder
{
// Embed property can be set within object initializer
Title = "Hello world!",
Description = "I am a description set by initializer."
};
// Or with methods
embed.AddField("Field title",
"Field value. I also support [hyperlink markdown](https://example.com)!")
.WithAuthor(Context.Client.CurrentUser)
.WithFooter(footer => footer.Text = "I am a footer.")
.WithColor(Color.Blue)
.WithTitle("I overwrote \"Hello world!\"")
.WithDescription("I am a description.")
.WithUrl("https://example.com")
.WithCurrentTimestamp();
//Your embed needs to be built before it is able to be sent
await ReplyAsync(embed: embed.Build());
}
```
![Embed Example](images/embed-example.png)
#### Usage with Local Images
The example below sends an image and has the image embedded in the rich
embed. See @Discord.IMessageChannel.SendFileAsync* for more information
about uploading a file or image.
```cs
[Command("embedimage")]
public async Task SendEmbedWithImageAsync()
{
var fileName = "image.png";
var embed = new EmbedBuilder()
{
ImageUrl = $"attachment://{fileName}"
}.Build();
await Context.Channel.SendFileAsync(fileName, embed: embed);
}
```

View file

@ -0,0 +1,25 @@
The example will build a rich embed with an author field, a footer
field, and 2 normal fields using an @Discord.EmbedBuilder:
```cs
var exampleAuthor = new EmbedAuthorBuilder()
.WithName("I am a bot")
.WithIconUrl("https://discord.com/assets/e05ead6e6ebc08df9291738d0aa6986d.png");
var exampleFooter = new EmbedFooterBuilder()
.WithText("I am a nice footer")
.WithIconUrl("https://discord.com/assets/28174a34e77bb5e5310ced9f95cb480b.png");
var exampleField = new EmbedFieldBuilder()
.WithName("Title of Another Field")
.WithValue("I am an [example](https://example.com).")
.WithInline(true);
var otherField = new EmbedFieldBuilder()
.WithName("Title of a Field")
.WithValue("Notice how I'm inline with that other field next to me.")
.WithInline(true);
var embed = new EmbedBuilder()
.AddField(exampleField)
.AddField(otherField)
.WithAuthor(exampleAuthor)
.WithFooter(exampleFooter)
.Build();
```

View file

@ -0,0 +1,20 @@
---
uid: Discord.EmbedAuthorBuilder
example: [*content]
---
[!include[Embed Object Builder Sample](EmbedObjectBuilder.Inclusion.md)]
---
uid: Discord.EmbedFooterBuilder
example: [*content]
---
[!include[Embed Object Builder Sample](EmbedObjectBuilder.Inclusion.md)]
---
uid: Discord.EmbedFieldBuilder
example: [*content]
---
[!include[Embed Object Builder Sample](EmbedObjectBuilder.Inclusion.md)]

View file

@ -0,0 +1,26 @@
The sample below sends a message and adds an @Discord.Emoji and a custom
@Discord.Emote to the message.
```cs
public async Task SendAndReactAsync(ISocketMessageChannel channel)
{
var message = await channel.SendMessageAsync("I am a message.");
// Creates a Unicode-based emoji based on the Unicode string.
// This is effectively the same as new Emoji("💕").
var heartEmoji = new Emoji("\U0001f495");
// Reacts to the message with the Emoji.
await message.AddReactionAsync(heartEmoji);
// Parses a custom emote based on the provided Discord emote format.
// Please note that this does not guarantee the existence of
// the emote.
var emote = Emote.Parse("<:thonkang:282745590985523200>");
// Reacts to the message with the Emote.
await message.AddReactionAsync(emote);
}
```
#### Result
![React Example](images/react-example.png)

View file

@ -0,0 +1,81 @@
---
uid: Discord.IEmote
seealso:
- linkId: Discord.Emote
- linkId: Discord.Emoji
- linkId: Discord.GuildEmote
- linkId: Discord.IUserMessage
remarks: *content
---
This interface is often used with reactions. It can represent an
unicode-based @Discord.Emoji, or a custom @Discord.Emote.
---
uid: Discord.Emote
seealso:
- linkId: Discord.IEmote
- linkId: Discord.GuildEmote
- linkId: Discord.Emoji
- linkId: Discord.IUserMessage
remarks: *content
---
> [!NOTE]
> A valid @Discord.Emote format is `<:emoteName:emoteId>`. This can be
> obtained by escaping with a `\` in front of the emote using the
> Discord chat client.
This class represents a custom emoji. This type of emoji can be
created via the @Discord.Emote.Parse* or @Discord.Emote.TryParse*
method.
---
uid: Discord.Emoji
seealso:
- linkId: Discord.Emote
- linkId: Discord.GuildEmote
- linkId: Discord.Emoji
- linkId: Discord.IUserMessage
remarks: *content
---
> [!NOTE]
> A valid @Discord.Emoji format is Unicode-based. This means only
> something like `🙃` or `\U0001f643` would work, instead of
> `:upside_down:`.
>
> A Unicode-based emoji can be obtained by escaping with a `\` in
> front of the emote using the Discord chat client or by looking up on
> [Emojipedia](https://emojipedia.org).
This class represents a standard Unicode-based emoji. This type of emoji
can be created by passing the Unicode into the constructor.
---
uid: Discord.IEmote
example: [*content]
---
[!include[Example Section](IEmote.Inclusion.md)]
---
uid: Discord.Emoji
example: [*content]
---
[!include[Example Section](IEmote.Inclusion.md)]
---
uid: Discord.Emote
example: [*content]
---
[!include[Example Section](IEmote.Inclusion.md)]
---
uid: Discord.GuildEmote
example: [*content]
---
[!include[Example Section](IEmote.Inclusion.md)]

View file

@ -0,0 +1,174 @@
---
uid: Discord.GuildChannelProperties
example: [*content]
---
The following example uses @Discord.IGuildChannel.ModifyAsync* to
apply changes specified in the properties,
```cs
var channel = _client.GetChannel(id) as IGuildChannel;
if (channel == null) return;
await channel.ModifyAsync(x =>
{
x.Name = "new-name";
x.Position = channel.Position - 1;
});
```
---
uid: Discord.TextChannelProperties
example: [*content]
---
The following example uses @Discord.ITextChannel.ModifyAsync* to
apply changes specified in the properties,
```cs
var channel = _client.GetChannel(id) as ITextChannel;
if (channel == null) return;
await channel.ModifyAsync(x =>
{
x.Name = "cool-guys-only";
x.Topic = "This channel is only for cool guys and adults!!!";
x.Position = channel.Position - 1;
x.IsNsfw = true;
});
```
---
uid: Discord.VoiceChannelProperties
example: [*content]
---
The following example uses @Discord.IVoiceChannel.ModifyAsync* to
apply changes specified in the properties,
```cs
var channel = _client.GetChannel(id) as IVoiceChannel;
if (channel == null) return;
await channel.ModifyAsync(x =>
{
x.UserLimit = 5;
});
```
---
uid: Discord.EmoteProperties
example: [*content]
---
The following example uses @Discord.IGuild.ModifyEmoteAsync* to
apply changes specified in the properties,
```cs
await guild.ModifyEmoteAsync(x =>
{
x.Name = "blobo";
});
```
---
uid: Discord.MessageProperties
example: [*content]
---
The following example uses @Discord.IUserMessage.ModifyAsync* to
apply changes specified in the properties,
```cs
var message = await channel.SendMessageAsync("boo");
await Task.Delay(TimeSpan.FromSeconds(1));
await message.ModifyAsync(x => x.Content = "boi");
```
---
uid: Discord.GuildProperties
example: [*content]
---
The following example uses @Discord.IGuild.ModifyAsync* to
apply changes specified in the properties,
```cs
var guild = _client.GetGuild(id);
if (guild == null) return;
await guild.ModifyAsync(x =>
{
x.Name = "VERY Fast Discord Running at Incredible Hihg Speed";
});
```
---
uid: Discord.RoleProperties
example: [*content]
---
The following example uses @Discord.IRole.ModifyAsync* to
apply changes specified in the properties,
```cs
var role = guild.GetRole(id);
if (role == null) return;
await role.ModifyAsync(x =>
{
x.Name = "cool boi";
x.Color = Color.Gold;
x.Hoist = true;
x.Mentionable = true;
});
```
---
uid: Discord.GuildUserProperties
example: [*content]
---
The following example uses @Discord.IGuildUser.ModifyAsync* to
apply changes specified in the properties,
```cs
var user = guild.GetUser(id);
if (user == null) return;
await user.ModifyAsync(x =>
{
x.Nickname = "I need healing";
});
```
---
uid: Discord.SelfUserProperties
example: [*content]
---
The following example uses @Discord.ISelfUser.ModifyAsync* to
apply changes specified in the properties,
```cs
await selfUser.ModifyAsync(x =>
{
x.Username = "Mercy";
});
```
---
uid: Discord.WebhookProperties
example: [*content]
---
The following example uses @Discord.IWebhook.ModifyAsync* to
apply changes specified in the properties,
```cs
await webhook.ModifyAsync(x =>
{
x.Name = "very fast fox";
x.ChannelId = newChannelId;
});
```

View file

@ -0,0 +1,24 @@
---
uid: Discord.Commands.OverrideTypeReaderAttribute
remarks: *content
---
This attribute is used to override a command parameter's type reading
behaviour. This may be useful when you have multiple custom
@Discord.Commands.TypeReader and would like to specify one.
---
uid: Discord.Commands.OverrideTypeReaderAttribute
examples: [*content]
---
The following example will override the @Discord.Commands.TypeReader
of @Discord.IUser to `MyUserTypeReader`.
```cs
public async Task PrintUserAsync(
[OverrideTypeReader(typeof(MyUserTypeReader))] IUser user)
{
//...
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

View file

@ -0,0 +1,29 @@
MIT License
Copyright (c) 2018 Still Hsu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
==============================================================================
Humanizer (https://github.com/Humanizr/Humanizer)
The MIT License (MIT)
Copyright (c) .NET Foundation and Contributors
==============================================================================

239
docs/_template/material/public/main.css vendored Normal file
View file

@ -0,0 +1,239 @@
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@100;400;700&display=swap');
:root {
--bs-font-sans-serif: 'Roboto';
--bs-border-radius: 10px;
--border-radius-button: 40px;
--card-box-shadow: 0 1px 2px 0 #3d41440f, 0 1px 3px 1px #3d414429;
--material-yellow-light: #e6dfbf;
--material-yellow-dark: #5a5338;
--material-blue-light: #c4d9f1;
--material-blue-dark: #383e5a;
--material-red-light: #f1c4c4;
--material-red-dark: #5a3838;
--material-warning-header: #f57f171a;
--material-warning-background: #f6e8bd;
--material-warning-background-dark: #57502c;
--material-info-header: #1976d21a;
--material-info-background: #e3f2fd;
--material-info-background-dark: #2c4557;
--material-danger-header: #d32f2f1a;
--material-danger-background: #ffebee;
--material-danger-background-dark: #572c2c;
}
/* HEADINGS */
h1 {
font-weight: 600;
font-size: 32px;
}
h2 {
font-weight: 600;
font-size: 24px;
line-height: 1.8;
}
h3 {
font-weight: 600;
font-size: 20px;
line-height: 1.8;
}
h5 {
font-size: 14px;
padding: 10px 0px;
}
article h2,
article h3,
article h4 {
margin-top: 15px;
margin-bottom: 15px;
}
/* MAKES PROPERTIES BE SEPARATED CLEARLY */
article h3 {
padding-bottom: 8px;
border-bottom: 2px solid #ddd;
}
/** IMAGES **/
img {
border-radius: var(--bs-border-radius);
box-shadow: var(--card-box-shadow);
}
/** NAVBAR **/
.navbar-brand > img {
box-shadow: none;
color: var(--bs-nav-link-color);
margin-right: var(--bs-navbar-brand-margin-end);
}
[data-bs-theme='light'] nav.navbar {
background-color: var(--bs-primary-bg-subtle);
}
[data-bs-theme='dark'] nav.navbar {
background-color: var(--bs-tertiary-bg);
}
.navbar-nav > li > a {
border-radius: var(--border-radius-button);
transition: 200ms;
}
.navbar-nav a.nav-link:focus,
.navbar-nav a.nav-link:hover {
background-color: var(--bs-primary-border-subtle);
}
.navbar-nav .nav-link.active,
.navbar-nav .nav-link.show {
color: var(--bs-link-hover-color);
}
/** SEARCH AND FILTER **/
input.form-control {
border-radius: var(--border-radius-button);
}
form.filter {
margin: 0.3rem;
}
/** ALERTS **/
.alert {
padding: 0;
border: none;
box-shadow: var(--card-box-shadow);
}
.alert > p {
padding: 0.2rem 0.7rem 0.7rem 1rem;
}
.alert > ul {
margin-bottom: 0;
padding: 5px 40px;
}
.alert > h5 {
padding: 0.5rem 0.7rem 0.7rem 1rem;
border-radius: var(--bs-border-radius) var(--bs-border-radius) 0 0;
font-weight: bold;
text-transform: capitalize;
}
.alert-info {
color: var(--material-blue-dark);
background-color: var(--material-info-background);
}
[data-bs-theme='dark'] .alert-info {
color: var(--material-blue-light);
background-color: var(--material-info-background-dark);
}
.alert-info > h5 {
background-color: var(--material-info-header);
}
.alert-warning {
color: var(--material-yellow-dark);
background-color: var(--material-warning-background);
}
[data-bs-theme='dark'] .alert-warning {
color: var(--material-yellow-light);
background-color: var(--material-warning-background-dark);
}
.alert-warning > h5 {
background-color: var(--material-warning-header);
}
.alert-danger {
color: var(--material-red-dark);
background-color: var(--material-danger-background);
}
[data-bs-theme='dark'] .alert-danger {
color: var(--material-red-light);
background-color: var(--material-danger-background-dark);
}
.alert-danger > h5 {
background-color: var(--material-danger-header);
}
/* CODE HIGHLIGHT */
code {
border-radius: var(--bs-border-radius);
margin: 4px 2px;
box-shadow: var(--card-box-shadow);
}
/* MAKES PARAMETERS MORE SPACIOUS */
:not(pre) > code {
padding: 3px;
}
/* MAKES LIST ITEMS BE SLIGHTLY MORE SEPARATED */
/* THIS AVOIDS CODE BLOCK OVERLAP */
ul:not(.navbar-nav) > li:not(:last-child) {
margin-bottom: 4px;
}
/* MAKES NAVBAR LINKS LOOK BETTER IN MOBILE */
.navbar-expand-md .navbar-nav .nav-link {
padding-right: var(--bs-navbar-nav-link-padding-x);
padding-left: var(--bs-navbar-nav-link-padding-x);
}
/* MAKES INHERITANCE LIST READABLE */
:is(dl.typelist.inheritedMembers, dl.typelist.extensionMethods) > dd > div::after {
content: none !important;
}
:is(dl.typelist.inheritedMembers, dl.typelist.extensionMethods) > dd > div {
display: block !important;
}
/* MAKES "IN THIS ARTICLE" MORE READABLE */
.affix h5, .affix .h5 {
font-weight: normal !important;
}
/* MAKES INDEX LOGO VISIBLE ON DIFFERENT THEMES */
article[data-uid="Home.Landing"] img[alt="logo"] {
height: 100pt !important;
box-shadow: none;
}
[data-bs-theme="light"] article[data-uid="Home.Landing"] img[alt="logo"] {
content: url('/marketing/logo/SVG/Combinationmark.svg') !important;
}
[data-bs-theme="dark"] article[data-uid="Home.Landing"] img[alt="logo"] {
content: url('/marketing/logo/SVG/Combinationmark White.svg') !important;
}
article[data-uid="Home.Landing"] img {
border-radius: 0;
}
/* MAKES SIDEBAR LINKS A BIT MORE DISTINGUISHABLE */
.affix ul li a:not(.link-body-emphasis) {
display: block !important;
margin-left: 8px !important;
}

72
docs/_template/material/public/main.js vendored Normal file
View file

@ -0,0 +1,72 @@
export default
{
iconLinks:
[
{
icon: 'github',
href: 'https://github.com/discord-net/Discord.Net',
title: 'GitHub'
},
{
icon: 'box-seam-fill',
href: 'https://www.nuget.org/packages/Discord.Net/',
title: 'NuGet'
},
{
icon: 'discord',
href: 'https://discord.gg/dnet',
title: 'Discord'
}
],
start: () =>
{
// Ugly hack to improve toc filter.
let target = document.getElementById("toc");
if(!target) return;
let config = { attributes: false, childList: true, subtree: true };
let observer = new MutationObserver((list) =>
{
for(const mutation of list)
{
if(mutation.type === "childList" && mutation.target == target)
{
let filter = target.getElementsByClassName("form-control")[0];
let filterValue = localStorage.getItem("tocFilter");
let scrollValue = localStorage.getItem("tocScroll");
if(filterValue && filterValue !== "")
{
filter.value = filterValue;
let inputEvent = new Event("input");
filter.dispatchEvent(inputEvent);
}
// Add event to store scroll pos.
let tocDiv = target.getElementsByClassName("flex-fill")[0];
tocDiv.addEventListener("scroll", (event) =>
{
if (event.target.scrollTop >= 0)
{
localStorage.setItem("tocScroll", event.target.scrollTop);
}
});
if(scrollValue && scrollValue >= 0)
{
tocDiv.scroll(0, scrollValue);
}
observer.disconnect();
break;
}
}
});
observer.observe(target, config);
}
}

6
docs/api/.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
###############
# temp file #
###############
*.yml
.manifest

17
docs/api/index.md Normal file
View file

@ -0,0 +1,17 @@
---
uid: API.Docs
---
# API Documentation
This is where you will find documentation for all members and objects in Discord.Net.
This is automatically generated based on the [dev](https://github.com/discord-net/Discord.Net/tree/dev) branch.
# Commonly Used Entities
* @Discord.WebSocket
* @Discord.WebSocket.DiscordSocketClient
* @Discord.WebSocket.SocketGuildChannel
* @Discord.WebSocket.SocketGuildUser
* @Discord.WebSocket.SocketMessage
* @Discord.WebSocket.SocketRole

69
docs/docfx.json Normal file
View file

@ -0,0 +1,69 @@
{
"metadata": [
{
"src": [
{
"src": "../src",
"files": ["**/*.csproj"],
"exclude": ["Discord.Net.DebugTools/*.csproj"]
}
],
"dest": "api",
"filter": "filterConfig.yml"
}
],
"build": {
"content": [
{
"files": ["api/**.yml", "api/index.md"]
},
{
"files": ["toc.yml", "index.md"]
},
{
"files": ["faq/**.md", "faq/**/toc.yml"]
},
{
"files": ["guides/**.md", "guides/**/toc.yml"]
},
{
"src": "../",
"files": ["CHANGELOG.md"]
}
],
"resource": [
{
"files": [
"**/images/**",
"**/samples/**",
"langwordMapping.yml",
"marketing/logo/**.svg",
"marketing/logo/**.png",
"favicon.png",
"../src/Discord.Net.Examples/**.cs"
]
}
],
"output": "_site",
"template": [
"default",
"modern",
"_template/material",
"_template/description-generator"
],
"postProcessors": [
"ExtractSearchIndex",
"DescriptionPostProcessor"
],
"overwrite": "_overwrites/**/**.md",
"globalMetadata": {
"_appTitle": "Discord.Net Documentation",
"_appName": "Discord.Net",
"_appFooter": "Discord.Net © 2015-2024 3.14.1",
"_enableSearch": true,
"_appLogoPath": "marketing/logo/SVG/Logomark Purple.svg",
"_appFaviconPath": "favicon.png"
},
"xref": ["https://github.com/dotnet/docfx/raw/main/.xrefmap.json"]
}
}

View file

@ -0,0 +1,123 @@
---
uid: FAQ.Basics.BasicOp
title: Basic Operations Questions
---
# Basic Operations Questions
In the following section, you will find commonly asked questions and
answers regarding basic usage of the library, as well as
language-specific tips when using this library.
## How should I safely check a type?
> [!WARNING]
> Direct casting (e.g., `(Type)type`) is **the least recommended**
> way of casting, as it _can_ throw an [InvalidCastException]
> when the object isn't the desired type.
>
> Please refer to [this post] for more details.
In Discord.Net, the idea of polymorphism is used throughout. You may
need to cast the object as a certain type before you can perform any
action.
A good and safe casting example:
[!code-csharp[Casting](samples/cast.cs)]
[invalidcastexception]: https://docs.microsoft.com/en-us/dotnet/api/system.invalidcastexception
[this post]: https://docs.microsoft.com/en-us/dotnet/csharp/how-to/safely-cast-using-pattern-matching-is-and-as-operators
## How do I send a message?
> [!TIP]
> The [GetChannel] method by default returns an [IChannel], allowing
> channel types such as [IVoiceChannel], [ICategoryChannel]
> to be returned; consequently, you cannot send a message
> to channels like those.
Any implementation of [IMessageChannel] has a [SendMessageAsync]
method. You can get the channel via [GetChannel] under the client.
Remember, when using Discord.Net, polymorphism is a common recurring
theme. This means an object may take in many shapes or form, which
means casting is your friend. You should attempt to cast the channel
as an [IMessageChannel] or any other entity that implements it to be
able to message.
[sendmessageasync]: xref:Discord.IMessageChannel.SendMessageAsync*
[getchannel]: xref:Discord.WebSocket.DiscordSocketClient.GetChannel*
## How can I tell if a message is from X, Y, Z channel?
You may check the message channel type. Visit [Glossary] to see the
various types of channels.
[Glossary]: xref:Guides.Entities.Glossary#channels
## How can I get the guild from a message?
There are 2 ways to do this. You can do either of the following,
1. Cast the user as an [IGuildUser] and use its [IGuild] property.
2. Cast the channel as an [IGuildChannel] and use its [IGuild] property.
## How do I add hyperlink text to an embed?
Embeds can use standard [markdown] in the description field as well
as in field values. With that in mind, links can be added with
`[text](link)`.
[markdown]: https://support.discordapp.com/hc/en-us/articles/210298617-Markdown-Text-101-Chat-Formatting-Bold-Italic-Underline-
## How do I add reactions to a message?
Any entity that implements [IUserMessage] has an [AddReactionAsync]
method. This method expects an [IEmote] as a parameter.
In Discord.Net, an Emote represents a custom-image emote, while an
Emoji is a Unicode emoji (standard emoji). Both [Emoji] and [Emote]
implement [IEmote] and are valid options.
# [Adding a reaction to another message](#tab/emoji-others)
[!code-csharp[Emoji](samples/emoji-others.cs)]
# [Adding a reaction to a sent message](#tab/emoji-self)
[!code-csharp[Emoji](samples/emoji-self.cs)]
---
[addreactionasync]: xref:Discord.IMessage.AddReactionAsync*
## What is a "preemptive rate limit?"
A preemptive rate limit is Discord.Net's way of telling you to slow
down before you get hit by the real rate limit. Hitting a real rate
limit might prevent your entire client from sending any requests for
a period of time. This is calculated based on the HTTP header
returned by a Discord response.
## Why am I getting so many preemptive rate limits when I try to add more than one reactions?
This is due to how HTML header works, mistreating
0.25sec/action to 1sec. This causes the lib to throw preemptive rate
limit more frequently than it should for methods such as adding
reactions.
## Can I opt-out of preemptive rate limits?
Unfortunately, not at the moment. See [#401](https://github.com/discord-net/Discord.Net/issues/401).
[IChannel]: xref:Discord.IChannel
[ICategoryChannel]: xref:Discord.ICategoryChannel
[IGuildChannel]: xref:Discord.IGuildChannel
[ITextChannel]: xref:Discord.ITextChannel
[IGuild]: xref:Discord.IGuild
[IVoiceChannel]: xref:Discord.IVoiceChannel
[IGuildUser]: xref:Discord.IGuildUser
[IMessageChannel]: xref:Discord.IMessageChannel
[IUserMessage]: xref:Discord.IUserMessage
[IEmote]: xref:Discord.IEmote
[Emote]: xref:Discord.Emote
[Emoji]: xref:Discord.Emoji

View file

@ -0,0 +1,148 @@
---
uid: FAQ.Basics.ClientBasics
title: Client Basics Questions
---
# Client Basics Questions
In the following section, you will find commonly asked questions and
answers about common issues that you may face when utilizing the
various clients offered by the library.
## I keep having trouble with intents!
As Discord.NET has upgraded from Discord API v6 to API v9,
`GatewayIntents` must now be specified in the socket config, as well as on the [developer portal].
```cs
// Where ever you declared your websocket client.
DiscordSocketClient _client;
...
var config = new DiscordSocketConfig()
{
.. // Other config options can be presented here.
GatewayIntents = GatewayIntents.All
}
_client = new DiscordSocketClient(config);
```
### Common intents:
- AllUnprivileged: This is a group of most common intents, that do NOT require any [developer portal] intents to be enabled.
This includes intents that receive messages such as: `GatewayIntents.GuildMessages, GatewayIntents.DirectMessages`
- GuildMembers: An intent disabled by default, as you need to enable it in the [developer portal].
- GuildPresences: Also disabled by default, this intent together with `GuildMembers` are the only intents not included in `AllUnprivileged`.
- All: All intents, it is ill advised to use this without care, as it _can_ cause a memory leak from presence.
The library will give responsive warnings if you specify unnecessary intents.
> [!NOTE]
> All gateway intents, their Discord API counterpart and their enum value are listed
> [HERE](xref:Discord.GatewayIntents)
### Stacking intents:
It is common that you require several intents together.
The example below shows how this can be done.
```cs
GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.GuildMembers | ..
```
> [!NOTE]
> Further documentation on the ` | ` operator can be found
> [HERE](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/bitwise-and-shift-operators)
[developer portal]: https://discord.com/developers/
## My client keeps returning 401 upon logging in!
> [!WARNING]
> Userbot/selfbot (logging in with a user token) is no
> longer supported with this library starting from 2.0, as
> logging in under a user account may result in account termination.
>
> For more information, see issue [827] & [958], as well as the official
> [Discord API Terms of Service].
There are few possible reasons why this may occur.
1. You are not using the appropriate [TokenType]. If you are using a
bot account created from the Discord Developer portal, you should
be using `TokenType.Bot`.
2. You are not using the correct login credentials. Please keep in
mind that a token is **different** from a *client secret*.
[TokenType]: xref:Discord.TokenType
[827]: https://github.com/discord-net/Discord.Net/issues/827
[958]: https://github.com/discord-net/Discord.Net/issues/958
[Discord API Terms of Service]: https://discord.com/developers/docs/legal
## How do I do X, Y, Z when my bot connects/logs on? Why do I get a `NullReferenceException` upon calling any client methods after connect?
Your bot should **not** attempt to interact in any way with
guilds/servers until the [Ready] event fires. When the bot
connects, it first has to download guild information from
Discord for you to get access to any server
information; the client is not ready at this point.
Technically, the [GuildAvailable] event fires once the data for a
particular guild has downloaded; however, it is best to wait for all
guilds to be downloaded. Once all downloads are complete, the [Ready]
event is triggered, then you can proceed to do whatever you like.
[Ready]: xref:Discord.WebSocket.DiscordSocketClient.Ready
[GuildAvailable]: xref:Discord.WebSocket.BaseSocketClient.GuildAvailable
## How do I get a message's previous content when that message is edited?
If you need to do anything with messages (e.g., checking Reactions,
checking the content of edited/deleted messages), you must set the
[MessageCacheSize] in your [DiscordSocketConfig] settings in order to
use the cached message entity. Read more about it [here](xref:Guides.Concepts.Events#cacheable).
1. Message Cache must be enabled.
2. Hook the MessageUpdated event. This event provides a *before* and
*after* object.
3. Only messages received *after* the bot comes online will be
available in the cache.
[MessageCacheSize]: xref:Discord.WebSocket.DiscordSocketConfig.MessageCacheSize
[DiscordSocketConfig]: xref:Discord.WebSocket.DiscordSocketConfig
[MessageUpdated]: xref:Discord.WebSocket.BaseSocketClient.MessageUpdated
## What is a shard/sharded client, and how is it different from the `DiscordSocketClient`?
As your bot grows in popularity, it is recommended that you should section your bot off into separate processes.
The [DiscordShardedClient] is essentially a class that allows you to easily create and manage multiple [DiscordSocketClient]
instances, with each one serving a different amount of guilds.
There are very few differences from the [DiscordSocketClient] class, and it is very straightforward
to modify your existing code to use a [DiscordShardedClient] when necessary.
1. You can specify the total amount of shards, or shard ids, via [DiscordShardedClient]'s constructors.
If the total shards are not specified then the library will get the recommended shard count via the
[Get Gateway Bot](https://discord.com/developers/docs/topics/gateway#get-gateway-bot) route.
2. The [Connected], [Disconnected], [Ready], and [LatencyUpdated] events
are replaced with [ShardConnected], [ShardDisconnected], [ShardReady], and [ShardLatencyUpdated].
3. Every event handler you apply/remove to the [DiscordShardedClient] is applied/removed to each shard.
If you wish to control a specific shard's events, you can access an individual shard through the `Shards` property.
If you do not wish to use the [DiscordShardedClient] and instead reuse the same [DiscordSocketClient] code and manually shard them,
you can do so by specifying the [ShardId] for the [DiscordSocketConfig] and pass that to the [DiscordSocketClient]'s constructor.
[DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient
[DiscordShardedClient]: xref:Discord.WebSocket.DiscordShardedClient
[Connected]: xref:Discord.WebSocket.DiscordSocketClient.Connected
[Disconnected]: xref:Discord.WebSocket.DiscordSocketClient.Disconnected
[LatencyUpdated]: xref:Discord.WebSocket.DiscordSocketClient.LatencyUpdated
[ShardConnected]: xref:Discord.WebSocket.DiscordShardedClient.ShardConnected
[ShardDisconnected]: xref:Discord.WebSocket.DiscordShardedClient.ShardDisconnected
[ShardReady]: xref:Discord.WebSocket.DiscordShardedClient.ShardReady
[ShardLatencyUpdated]: xref:Discord.WebSocket.DiscordShardedClient.ShardLatencyUpdated
[ShardId]: xref:Discord.WebSocket.DiscordSocketConfig.ShardId

View file

@ -0,0 +1,46 @@
---
uid: FAQ.Basics.DI
title: Questions about Dependency Injection
---
# Dependency Injection-related Questions
In the following section, you will find common questions and answers
to utilizing dependency injection with @Discord.Commands and @Discord.Interactions, as well as
common troubleshooting steps regarding DI.
## What is a service? Why does my module not hold any data after execution?
In Discord.Net, modules are created similarly to ASP.NET, meaning
that they have a transient nature; modules are spawned whenever a
request is received, and are killed from memory when the execution
finishes. In other words, you cannot store persistent
data inside a module. Consider using a service if you wish to
workaround this.
Service is often used to hold data externally so that they persist
throughout execution. Think of it like a chest that holds
whatever you throw at it that won't be affected by anything unless
you want it to. Note that you should also learn Microsoft's
implementation of [Dependency Injection] \([video]) before proceeding.
A brief example of service and dependency injection can be seen below.
[!code-csharp[DI](samples/DI.cs)]
[Dependency Injection]: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection
[video]: https://www.youtube.com/watch?v=QtDTfn8YxXg
## Why is my Command/Interaction Service complaining about a missing dependency?
If you encounter an error similar to `Failed to create MyModule,
dependency MyExternalDependency was not found.`, you may have
forgotten to add the external dependency to the dependency container.
For example, if your module, `MyModule`, requests a `DatabaseService`
in its constructor, the `DatabaseService` must be present in the
[IServiceProvider] when registering `MyModule`.
[!code-csharp[Missing Dependencies](samples/missing-dep.cs)]
[IServiceProvider]: xref:System.IServiceProvider

View file

@ -0,0 +1,96 @@
---
uid: FAQ.Basics.GetStarted
title: Getting Started
---
# Getting Started
In this following section, you will find commonly asked questions and
answers about how to get started with Discord.Net, as well as basic
introduction to the Discord API ecosystem.
## How do I add my bot to my guild?
Inviting your bot can be done by using the OAuth2 url generator provided by the [Discord Developer Portal].
Permissions can be granted by selecting the `bot` scope in the scopes section.
![Scopes](images/scopes.png)
A permissions tab will appear below the scope selection,
from which you can pick any permissions your bot may require to function.
When invited, the role this bot is granted will include these permissions.
If you grant no permissions, no role will be created for your bot upon invitation as there is no need for one.
![Permissions](images/permissions.png)
When done selecting permissions, you can use the link below in your browser to invite the bot
to servers where you have the `Manage Server` permission.
![Invite](images/link.png)
If you are planning to play around with slash/context commands,
make sure to check the `application commands` scope before inviting your bot!
> [!NOTE]
> You do not have to kick and reinvite your bot to update permissions/scopes later on.
> Simply reusing the invite link with provided scopes/perms will update it accordingly.
[Discord Developer Portal]: https://discord.com/developers/applications/
## What is a token?
A token is a credential used to log into an account. This information
should be kept **private** and for your eyes only. Anyone with your
token can log into your account. This risk applies to both user
and bot accounts. That also means that you should **never** hardcode
your token or add it into source control, as your identity may be
stolen by scrape bots on the internet that scours through
constantly to obtain a token.
## What is a client/user/object ID?
Each user and object on Discord has its own snowflake ID generated
based on various conditions.
![Snowflake Generation](images/snowflake.png)
Anyone can see the ID; it is public. It is merely used to
identify an object in the Discord ecosystem. Many things in the
Discord ecosystem require an ID to retrieve or identify the said
object.
There are 2 common ways to obtain the said ID.
### [Discord Developer Mode](#tab/dev-mode)
By enabling the developer mode you can right click on most objects
to obtain their snowflake IDs (please note that this may not apply to
all objects, such as role IDs, or DM channel IDs).
![Developer Mode](images/dev-mode.png)
### [Escape Character](#tab/escape-char)
You can escape an object by using `\` in front the object in the
Discord client. For example, when you do `\@Example#1234` in chat,
it will return the user ID of the aforementioned user.
![Escaping mentions](images/mention-escape.png)
***
## How do I get the role ID?
> [!WARNING]
> Right-clicking on the role and copying the ID will **not** work.
> This will only copy the message ID.
There are several common ways to do this:
1. Right click on the role either in the Server Settings
or in the user's role list (recommended).
![Roles](images/role-copy.png)
2. Make the role mentionable and mention the role, and escape it
using the `\` character in front.
3. Inspect the roles collection within the guild via your debugger.

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

View file

@ -0,0 +1,28 @@
public class MyService
{
public string MyCoolString { get; set; }
}
public class Setup
{
public IServiceProvider BuildProvider() =>
new ServiceCollection()
.AddSingleton<MyService>()
.BuildServiceProvider();
}
public class MyModule : ModuleBase<SocketCommandContext>
{
// Inject via public settable prop
public MyService MyService { get; set; }
// ...or via the module's constructor
// private readonly MyService _myService;
// public MyModule (MyService myService) => _myService = myService;
[Command("string")]
public Task GetOrSetStringAsync(string input)
{
if (string.IsNullOrEmpty(_myService.MyCoolString)) _myService.MyCoolString = input;
return ReplyAsync(_myService.MyCoolString);
}
}

View file

@ -0,0 +1,15 @@
public async Task MessageReceivedHandler(SocketMessage msg)
{
// Option 1:
// Using the `as` keyword, which will return `null` if the object isn't the desired type.
var usermsg = msg as SocketUserMessage;
// We bail when the message isn't the desired type.
if (msg == null) return;
// Option 2:
// Using the `is` keyword to cast (C#7 or above only)
if (msg is SocketUserMessage usermsg)
{
// Do things
}
}

View file

@ -0,0 +1,18 @@
// bail if the message is not a user one (system messages cannot have reactions)
var usermsg = msg as IUserMessage;
if (usermsg == null) return;
// standard Unicode emojis
Emoji emoji = new Emoji("👍");
// or
// Emoji emoji = new Emoji("\uD83D\uDC4D");
// custom guild emotes
Emote emote = Emote.Parse("<:dotnet:232902710280716288>");
// using Emote.TryParse may be safer in regards to errors being thrown;
// please note that the method does not verify if the emote exists,
// it simply creates the Emote object for you.
// add the reaction to the message
await usermsg.AddReactionAsync(emoji);
await usermsg.AddReactionAsync(emote);

View file

@ -0,0 +1,17 @@
// capture the message you're sending in a variable
var msg = await channel.SendMessageAsync("This will have reactions added.");
// standard Unicode emojis
Emoji emoji = new Emoji("👍");
// or
// Emoji emoji = new Emoji("\uD83D\uDC4D");
// custom guild emotes
Emote emote = Emote.Parse("<:dotnet:232902710280716288>");
// using Emote.TryParse may be safer in regards to errors being thrown;
// please note that the method does not verify if the emote exists,
// it simply creates the Emote object for you.
// add the reaction to the message
await msg.AddReactionAsync(emoji);
await msg.AddReactionAsync(emote);

View file

@ -0,0 +1,32 @@
public class MyModule : ModuleBase<SocketCommandContext>
{
private readonly DatabaseService _dbService;
public MyModule(DatabaseService dbService)
=> _dbService = dbService;
}
public class CommandHandler
{
private readonly CommandService _commands;
private readonly IServiceProvider _services;
public CommandHandler(DiscordSocketClient client)
{
_services = new ServiceCollection()
.AddSingleton<CommandService>()
.AddSingleton(client)
// We are missing DatabaseService!
.BuildServiceProvider();
}
public async Task RegisterCommandsAsync()
{
// ...
// The method fails here because DatabaseService is a required
// dependency and cannot be resolved by the dependency
// injection service at runtime since the service is not
// registered in this instance of _services.
await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
// ...
// The same approach applies to the interaction service.
// Make sure to resolve these issues!
}
}

View file

@ -0,0 +1,41 @@
---
uid: FAQ.BuildOverrides.WhatAreThey
title: Build Overrides
---
# Build Overrides
Build overrides are a way for library developers to override the default behavior of the library on the fly. Adding them to your code is really simple.
## Installing the package
The build override package can be installed on nuget [here](https://www.nuget.org/packages/Discord.Net.BuildOverrides) or by using the package manager
```
PM> Install-Package Discord.Net.BuildOverrides
```
## Adding an override
```cs
public async Task MainAsync()
{
// hook into the log function
BuildOverrides.Log += (buildOverride, message) =>
{
Console.WriteLine($"{buildOverride.Name}: {message}");
return Task.CompletedTask;
};
// add your overrides
await BuildOverrides.AddOverrideAsync("example-override-name");
}
```
Overrides are normally built for specific problems, for example if someone is having an issue and we think we might have a fix then we can create a build override for them to test out the fix.
## Security and Transparency
Overrides can only be created and updated by library developers, you should only apply an override if a library developer asks you to.
Code for the overrides server and the overrides themselves can be found [here](https://github.com/discord-net/Discord.Net.BuildOverrides).

View file

@ -0,0 +1,71 @@
---
uid: FAQ.Interactions.Framework
title: Interaction Framework
---
# The Interaction Framework
Common misconceptions and questions about the Interaction Framework.
## How can I restrict some of my commands so only specific users can execute them?
Based on how you want to implement the restrictions, you can use the
built-in `RequireUserPermission` precondition, which allows you to
restrict the command based on the user's current permissions in the
guild or channel (*e.g., `GuildPermission.Administrator`,
`ChannelPermission.ManageMessages`*).
[RequireUserPermission]: xref:Discord.Commands.RequireUserPermissionAttribute
> [!NOTE]
> There are many more preconditions to use, including being able to make some yourself.
> Examples on self-made preconditions can be found
> [here](https://github.com/discord-net/Discord.Net/blob/dev/samples/InteractionFramework/Attributes/RequireOwnerAttribute.cs)
## Why do preconditions not hide my commands?
In the current permission design by Discord,
it is not very straight forward to limit vision of slash/context commands to users.
If you want to hide commands, you should take a look at the commands' `DefaultPermissions` parameter.
## Module dependencies aren't getting populated by Property Injection?
Make sure the properties are publicly accessible and publicly settable.
[!code-csharp[Property Injection](samples/propertyinjection.cs)]
## `InteractionService.ExecuteAsync()` always returns a successful result, how do i access the failed command execution results?
If you are using `RunMode.Async` you need to setup your post-execution pipeline around
`..Executed` events exposed by the Interaction Service.
## How do I check if the executing user has * permission?
Refer to the [documentation about preconditions]
[documentation about preconditions]: xref:Guides.IntFw.Preconditions
## How do I send the HTTP Response from inside the command modules.
Set the `RestResponseCallback` property of [InteractionServiceConfig] with a delegate for handling HTTP Responses and use
`RestInteractionModuleBase` to create your command modules. `RespondWithModalAsync()`, `RespondAsync()` and `DeferAsync()` methods of this module base will use the
`RestResponseCallback` to create interaction responses.
## Is there a cleaner way of creating parameter choices other than using `[Choice]`?
The default `enum` [TypeConverter] of the Interaction Service will
automatically register `enum`s as multiple choice options.
## How do I add an optional `enum` parameter but make the default value not visible to the user?
The default `enum` [TypeConverter] of the Interaction Service comes with `[Hide]` attribute that
can be used to prevent certain enum values from getting registered.
## How does the InteractionService determine the generic TypeConverter to use for a parameter type?
It compares the _target base type_ key of the
[TypeConverter] and chooses the one that sits highest on the inheritance hierarchy.
[TypeConverter]: xref:Discord.Interactions.TypeConverter
[Interactions FAQ]: xref: FAQ.Basics.Interactions
[InteractionServiceConfig]: xref:Discord.Interactions.InteractionServiceConfig

View file

@ -0,0 +1,70 @@
---
uid: FAQ.Interactions.General
title: Interaction Basics
---
# Interaction Basics
This chapter mostly refers to interactions in general,
and will include questions that are common among users of the Interaction Framework
as well as users that register and handle commands manually.
## What's the difference between RespondAsync, DeferAsync and FollowupAsync?
The difference between these 3 functions is in how you handle the command response.
[RespondAsync] and
[DeferAsync] let the API know you have successfully received the command. This is also called 'acknowledging' a command.
DeferAsync will not send out a response, RespondAsync will.
[FollowupAsync] follows up on successful acknowledgement.
> [!WARNING]
> If you have not acknowledged the command FollowupAsync will not work! the interaction has not been responded to, so you cannot follow it up!
[RespondAsync]: xref:Discord.IDiscordInteraction
[DeferAsync]: xref:Discord.IDiscordInteraction
[FollowUpAsync]: xref:Discord.IDiscordInteraction
## Im getting System.TimeoutException: 'Cannot respond to an interaction after 3 seconds!'
This happens because your computer's clock is out of sync or you're trying to respond after 3 seconds.
If your clock is out of sync and you can't fix it, you can set the `UseInteractionSnowflakeDate` to false in the [DiscordSocketConfig].
[!code-csharp[Interaction Sync](samples/interactionsyncing.cs)]
[DiscordClientConfig]: xref:Discord.WebSocket.DiscordSocketConfig
## How do I use this * interaction specific method/property?
If your interaction context holds a down-casted version of the interaction object, you need to up-cast it.
Ideally, use pattern matching to make sure its the type of interaction you are expecting it to be.
> [!NOTE]
> Further documentation on pattern matching can be found [here](xref:Guides.Entities.Casting).
## My interaction commands are not showing up?
- Try to check for any errors in the console, there is a good chance something might have been thrown.
- - Make sure you have setup logging. If you use `InteractionService` hook into [`InteractionService.Log`]) event
- Register your commands after the Ready event in the client. The client is not configured to register commands before this moment.
- Check if no bad form exception is thrown;
- Do you have the application commands scope checked when adding your bot to guilds?
- Try reloading your Discord client. On desktop it's done with `Ctrl+R` key combo.
## Do I need to create commands on startup?
If you are registering your commands for the first time, it is required to create them once.
After this, commands will exist indefinitely until you overwrite them.
Overwriting is only required if you make changes to existing commands, or add new ones.
## I can't see all of my user/message commands, why?
Message and user commands have a limit of 5 per guild, and another 5 globally.
If you have more than 5 guild-only message commands being registered, no more than 5 will actually show up.
You can get up to 10 entries to show if you register 5 per guild, and another 5 globally.
[`InteractionService.Log`]: xref:Discord.Interactions.InteractionService.Log

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -0,0 +1,45 @@
---
uid: FAQ.Interactions.Manual
title: Manual Handling
---
# Manually Handling Interactions
This section talks about the manual building and responding to interactions.
If you are using the interaction framework (highly recommended) this section does not apply to you.
## Bad form Exception when I try to create my commands, why do I get this?
Bad form exceptions are thrown if the slash, user or message command builder has invalid values.
The following options could resolve your error.
#### Is your command name lowercase?
If your command name is not lowercase, it is not seen as a valid command entry.
`Avatar` is invalid; `avatar` is valid.
#### Are your values below or above the required amount? (This also applies to message components)
Discord expects all values to be below maximum allowed.
Going over this maximum amount of characters causes an exception.
> [!NOTE]
> All maximum and minimum value requirements can be found in the [Discord Developer Docs].
> For components, structure documentation is found [here].
[Discord Developer Docs]: https://discord.com/developers/docs/interactions/application-commands#application-commands
[here]: https://discord.com/developers/docs/interactions/message-components#message-components
#### Is your subcommand branching correct?
Branching structure is covered properly here: xref:Guides.SlashCommands.SubCommand
![Scope check](images/scope.png)
## There are many options for creating commands, which do I use?
[!code-csharp[Register examples](samples/registerint.cs)]
> [!NOTE]
> You can use bulkoverwrite even if there are no commands in guild, nor globally.
> The bulkoverwrite method disposes the old set of commands and replaces it with the new.

View file

@ -0,0 +1,32 @@
---
uid: FAQ.Interactions.RespondingSchemes
title: Interaction Response Schemes
---
# Interaction Response Schemes
Working with interactions can appear hard and confusing - you might accidentally miss a cast or use a wrong method. These schemes should help you create efficient interaction response flows.
## Responding to a slash command interaction
Slash command interactions support the most commonly used response methods.
> [!NOTE]
> Same scheme applies to context command interactions.
![Slash command interaction](images/response-scheme-slash.png)
## Responding to a component interaction
Component interactions share a lot of response mwthods with [slash command interactions](#responding-to-a-slash-command-interaction), but they also provide a way to update the message components were attached to.
> [!NOTE]
> Some followup methods change their behavior depending on what initial response you've sent.
![Slash command interaction](images/response-scheme-component.png)
## Responding to a modal interaction
While being similar to [Component Interaction Scheme](#responding-to-a-modal-interaction), modal interactions lack the option of responding with a modal.
![Slash command interaction](images/response-scheme-modal.png)

View file

@ -0,0 +1,6 @@
DiscordSocketConfig config = new()
{
UseInteractionSnowflakeDate = false
};
DiscordSocketclient client = new(config);

View file

@ -0,0 +1,8 @@
public class MyModule
{
// Intended.
public InteractionService Service { get; set; }
// Will not work. A private setter cannot be accessed by the serviceprovider.
private InteractionService Service { get; private set; }
}

View file

@ -0,0 +1,21 @@
private async Task ReadyAsync()
{
// pull your commands from some array, everyone has a different approach for this.
var commands = _builders.ToArray();
// write your list of commands globally in one go.
await _client.Rest.BulkOverwriteGlobalCommands(commands);
// write your array of commands to one guild in one go.
// You can do a foreach (... in _client.Guilds) approach to write to all guilds.
await _client.Rest.BulkOverwriteGuildCommands(commands, /* some guild ID */);
foreach (var c in commands)
{
// Create a global command, repeating usage for multiple commands.
await _client.Rest.CreateGlobalCommand(c);
// Create a guild command, repeating usage for multiple commands.
await _client.Rest.CreateGuildCommand(c, guildId);
}
}

46
docs/faq/misc/legacy.md Normal file
View file

@ -0,0 +1,46 @@
---
uid: FAQ.Legacy
title: Legacy Questions
---
# Legacy Questions
This section refers to legacy library-related questions that do not
apply to the latest or recent version of the Discord.Net library.
## Migrating your commands to application commands.
The new interaction service was designed to act like the previous service for text-based commands.
Your pre-existing code will continue to work, but you will need to migrate your modules and response functions to use the new
interaction service methods. Documentation on this can be found in the [Guides](xref:Guides.IntFw.Intro).
## Gateway event parameters changed, why?
With 3.0, a higher focus on [Cacheable]'s was introduced.
[Cacheable]'s get an entity from cache, rather than making an API call to retrieve it's data.
The entity can be retrieved from cache by calling `GetOrDownloadAsync()` on the [Cacheable] type.
> [!NOTE]
> GetOrDownloadAsync will download the entity if its not available directly from the cache.
[Cacheable]: xref:Discord.Cacheable`2
## X, Y, Z does not work! It doesn't return a valid value anymore.
If you are currently using an older version of the stable branch,
please upgrade to the latest release version to ensure maximum
compatibility. Several features may be broken in older
versions and will likely not be fixed in the version branch due to
their breaking nature.
Visit the repo's [release tag] to see the latest public release.
[release tag]: https://github.com/discord-net/Discord.Net/releases
## I came from an earlier version of Discord.Net 1.0, and DependencyMap doesn't seem to exist anymore in the later revision? What happened to it?
The `DependencyMap` has been replaced with Microsoft's
[DependencyInjection] Abstractions. An example usage can be seen
[here](https://github.com/Discord-Net-Labs/Discord.Net-Labs/blob/release/3.x/samples/InteractionFramework/Program.cs#L66).
[DependencyInjection]: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection

View file

@ -0,0 +1,142 @@
---
uid: FAQ.TextCommands.General
title: General Questions about Text Commands
---
# Chat Command-related Questions
In the following section, you will find commonly asked questions and
answered regarding general command usage when using @Discord.Commands.
## How can I restrict some of my commands so only specific users can execute them?
You can use the built-in `RequireUserPermission` precondition, which allows you to
restrict the command based on the user's current permissions in the
guild or channel (*e.g., `GuildPermission.Administrator`,
`ChannelPermission.ManageMessages`*).
> [!NOTE]
> There are many more preconditions to use, including being able to make some yourself.
> Precondition documentation is covered [here](xref:Guides.TextCommands.Preconditions)
[RequireUserPermission]: xref:Discord.Commands.RequireUserPermissionAttribute
## Why am I getting an error about `Assembly.GetEntryAssembly`?
You may be confusing @Discord.Commands.CommandService.AddModulesAsync*
with @Discord.Commands.CommandService.AddModuleAsync*. The former
is used to add modules via the assembly, while the latter is used to
add a single module.
## What does [Remainder] do in the command signature?
The [RemainderAttribute] leaves the string unparsed, meaning you
do not have to add quotes around the text for the text to be
recognized as a single object. Please note that if your method has
multiple parameters, the remainder attribute can only be applied to
the last parameter.
[!code-csharp[Remainder](samples/Remainder.cs)]
[RemainderAttribute]: xref:Discord.Commands.RemainderAttribute
## Discord.Net keeps saying that a `MessageReceived` handler is blocking the gateway, what should I do?
By default, the library warns the user about any long-running event
handler that persists for **more than 3 seconds**. Any event
handlers that are run on the same thread as the gateway task, the task
in charge of keeping the connection alive, may block the processing of
heartbeat, and thus terminating the connection.
In this case, the library detects that a `MessageReceived`
event handler is blocking the gateway thread. This warning is
typically associated with the command handler as it listens for that
particular event. If the command handler is blocking the thread, then
this **might** mean that you have a long-running command.
> [!NOTE]
> In rare cases, runtime errors can also cause blockage, usually
> associated with Mono, which is not supported by this library.
To prevent a long-running command from blocking the gateway
thread, a flag called [RunMode] is explicitly designed to resolve
this issue.
There are 2 main `RunMode`s.
1. `RunMode.Sync`
2. `RunMode.Async`
`Sync` is the default behavior and makes the command to be run on the
same thread as the gateway one. `Async` will spin the task off to a
different thread from the gateway one.
> [!IMPORTANT]
> While specifying `RunMode.Async` allows the command to be spun off
> to a different thread, keep in mind that by doing so, there will be
> **potentially unwanted consequences**. Before applying this flag,
> please consider whether it is necessary to do so.
>
> Further details regarding `RunMode.Async` can be found below.
You can set the `RunMode` either by specifying it individually via
the `CommandAttribute` or by setting the global default with
the [DefaultRunMode] flag under `CommandServiceConfig`.
# [CommandAttribute](#tab/cmdattrib)
[!code-csharp[Command Attribute](samples/runmode-cmdattrib.cs)]
# [CommandServiceConfig](#tab/cmdconfig)
[!code-csharp[Command Service Config](samples/runmode-cmdconfig.cs)]
***
***
[RunMode]: xref:Discord.Commands.RunMode
[CommandAttribute]: xref:Discord.Commands.CommandAttribute
[DefaultRunMode]: xref:Discord.Commands.CommandServiceConfig.DefaultRunMode
## How does `RunMode.Async` work, and why is Discord.Net *not* using it by default?
`RunMode.Async` works by spawning a new `Task` with an unawaited
[Task.Run], essentially making the task that is used to invoke the
command task to be finished on a different thread. This design means
that [ExecuteAsync] will be forced to return a successful
[ExecuteResult] regardless of the actual execution result.
The following are the known caveats with `RunMode.Async`,
1. You can potentially introduce a race condition.
2. Unnecessary overhead caused by the [async state machine].
3. [ExecuteAsync] will immediately return [ExecuteResult] instead of
other result types (this is particularly important for those who wish
to utilize [RuntimeResult] in 2.0).
4. Exceptions are swallowed in the `ExecuteAsync` result.
However, there are ways to remedy some of these.
For #3, in Discord.Net 2.0, the library introduces a new event called
[CommandService.CommandExecuted], which is raised whenever the command is executed.
This event will be raised regardless of
the `RunMode` type and will return the appropriate execution result
and the associated @Discord.Commands.CommandInfo if applicable.
For #4, exceptions are caught in [CommandService.Log] event under
[LogMessage.Exception] as [CommandException] and in the
[CommandService.CommandExecuted] event under the [IResult] as
[ExecuteResult.Exception].
[Task.Run]: https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.run
[async state machine]: https://www.red-gate.com/simple-talk/dotnet/net-tools/c-async-what-is-it-and-how-does-it-work/
[ExecuteAsync]: xref:Discord.Commands.CommandService.ExecuteAsync*
[ExecuteResult]: xref:Discord.Commands.ExecuteResult
[RuntimeResult]: xref:Discord.Commands.RuntimeResult
[CommandService.CommandExecuted]: xref:Discord.Commands.CommandService.CommandExecuted
[CommandService.Log]: xref:Discord.Commands.CommandService.Log
[LogMessage.Exception]: xref:Discord.LogMessage.Exception*
[ExecuteResult.Exception]: xref:Discord.Commands.ExecuteResult.Exception*
[CommandException]: xref:Discord.Commands.CommandException
[IResult]: xref:Discord.Commands.IResult

View file

@ -0,0 +1,20 @@
// Input:
// !echo Coffee Cake
// Output:
// Coffee Cake
[Command("echo")]
public Task EchoRemainderAsync([Remainder]string text) => ReplyAsync(text);
// Output:
// CommandError.BadArgCount
[Command("echo-hassle")]
public Task EchoAsync(string text) => ReplyAsync(text);
// The message would be seen as having multiple parameters,
// while the method only accepts one.
// Wrapping the message in quotes solves this.
// This way, the system knows the entire message is to be parsed as a
// single String.
// e.g.,
// !echo "Coffee Cake"

View file

@ -0,0 +1,7 @@
[Command("process", RunMode = RunMode.Async)]
public async Task ProcessAsync(string input)
{
// Does heavy calculation here.
await Task.Delay(TimeSpan.FromMinute(1));
await ReplyAsync(input);
}

View file

@ -0,0 +1,10 @@
public class Setup
{
private readonly CommandService _command;
public Setup()
{
var config = new CommandServiceConfig{ DefaultRunMode = RunMode.Async };
_command = new CommandService(config);
}
}

28
docs/faq/toc.yml Normal file
View file

@ -0,0 +1,28 @@
- name: Basic Concepts
items:
- name: Getting Started
topicUid: FAQ.Basics.GetStarted
- name: Basic Operations
topicUid: FAQ.Basics.BasicOp
- name: Client Basics
topicUid: FAQ.Basics.ClientBasics
- name: Dependency Injection
topicUid: FAQ.Basics.DI
- name: Interactions
items:
- name: Starting out
topicUid: FAQ.Interactions.General
- name: Interaction Service/Framework
topicUid: FAQ.Interactions.Framework
- name: Manual handling
topicUid: FAQ.Interactions.Manual
- name: Interaction response schemes
topicUid: FAQ.Interactions.RespondingSchemes
- name: Text Commands
items:
- name: Text Command basics
topicUid: FAQ.TextCommands.General
- name: Legacy Questions
topicUid: FAQ.Legacy
- name: Build Overrides
topicUid: FAQ.BuildOverrides.WhatAreThey

BIN
docs/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

10
docs/filterConfig.yml Normal file
View file

@ -0,0 +1,10 @@
apiRules:
- exclude:
uidRegex: ^Discord\.Net\..*$
type: Namespace
- exclude:
uidRegex: ^Discord\.Analyzers$
type: Namespace
- exclude:
uidRegex: ^Discord\.API$
type: Namespace

View file

@ -0,0 +1,68 @@
---
uid: Guides.BearerToken
title: Working with Bearer token
---
# Working with Bearer token
Some endpoints in Discord API require a Bearer token, which can be obtained through [OAuth2 flow](https://discord.com/developers/docs/topics/oauth2). Discord.Net allows you to interact with these endpoints using the [DiscordRestClient].
## Initializing a new instance of the client
[!code-csharp[Initialize DiscordRestClient](samples/rest_client_init.cs)]
## Getting current user
The [DiscordRestClient] gets the current user when `LoginAsync()` is called. The user object can be found in the `CurrentUser` property.
If you need to fetch the user again, the `GetCurrentUserAsync()` method can be used.
[!code-csharp[Get current user](samples/current_user.cs)]
> [!NOTE]
> Some properties might be `null` depending on which scopes users authorized your app with.
> For example: `email` scope is required to fetch current user's email address.
## Fetching current user's guilds
The `GetGuildSummariesAsync()` method is used to fetch current user's guilds. Since it returns an `IAsyncEnumerable` you need to call `FlattenAsync()` to get a plain `IEnumerable` containing [RestUserGuild] objects.
[!code-csharp[Get current user's guilds](samples/current_user_guilds.cs)]
> [!WARNING]
> This method requires `guilds` scope
## Fetching current user's guild member object
To fetch the current user's guild member object, the `GetCurrentUserGuildMemberAsync()` method can be used.
[!code-csharp[Get current user's guild member](samples/current_user_guild_member.cs)]
> [!WARNING]
> This method requires `guilds.members.read` scope
## Get user connections
The `GetConnectionsAsync` method can be used to fetch current user's connections to other platforms.
[!code-csharp[Get current user's connections](samples/current_user_connections.cs)]
> [!WARNING]
> This method requires `connections` scope
## Application role connection
In addition to previous features, Discord.Net supports fetching & updating user's application role connection metadata values. `GetUserApplicationRoleConnectionAsync()` returns a [RoleConnection] object of the current user for the given application id.
The `ModifyUserApplicationRoleConnectionAsync()` method is used to update current user's role connection metadata values. A new set of values can be created with [RoleConnectionProperties] object.
[!code-csharp[Get current user's connections](samples/app_role_connection.cs)]
> [!WARNING]
> This method requires `role_connections.write` scope
[DiscordRestClient]: xref:Discord.Rest.DiscordRestClient
[RestUserGuild]: xref:Discord.Rest.RestUserGuild
[RoleConnection]: xref:Discord.RoleConnection
[RoleConnectionProperties]: xref:Discord.RoleConnectionProperties

View file

@ -0,0 +1,11 @@
// fetch application role connection of the current user for the app with provided id.
var roleConnection = await client.GetUserApplicationRoleConnectionAsync(applicationid);
// create a new role connection metadata properties object & set some values.
var properties = new RoleConnectionProperties("Discord.Net Docs", "Cool Coding Guy")
.WithNumber("eaten_cookies", 69)
.WithBool("loves_cookies", true)
.WithDate("last_eaten_cookie", DateTimeOffset.UtcNow);
// update current user's values with the given properties.
await client.ModifyUserApplicationRoleConnectionAsync(applicationId, properties);

View file

@ -0,0 +1,5 @@
// gets the user object stored in the DiscordRestClient.
var user = client.CurrentUser;
// fetches the current user with a REST call & updates the CurrentUser property.
var refreshedUser = await client.GetCurrentUserAsync();

View file

@ -0,0 +1,2 @@
// fetches the current user's connections.
var connections = await client.GetConnectionsAsync();

View file

@ -0,0 +1,6 @@
// fetches the current user's guild member object in a guild with provided id.
var member = await client.GetCurrentUserGuildMemberAsync(guildId);
// fetches the current user's guild member object in a RestUserGuild.
var guild = await client.GetGuildSummariesAsync().FlattenAsync().First();
var member = await guild.GetCurrentUserGuildMemberAsync();

View file

@ -0,0 +1,2 @@
// fetches the guilds the current user participate in.
var guilds = await client.GetGuildSummariesAsync().FlattenAsync();

View file

@ -0,0 +1,5 @@
using Discord;
using Discord.Rest;
await using var client = new DiscordRestClient();
await client.LoginAsync(TokenType.Bearer, "bearer token obtained through oauth2 flow");

View file

@ -0,0 +1,53 @@
---
uid: Guides.Concepts.ManageConnections
title: Managing Connections
---
# Managing Connections with Discord.Net
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; to end a connection, invoke the
`StopAsync` method, which gracefully closes 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 made.**
As a result, you 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 completed guild cache.
[DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient
## Reconnection
> [!TIP]
> Avoid running long-running code on the gateway! If you deadlock the
> gateway (as explained in [events]), the connection manager will
> **NOT** be able 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 does not manage to reconnect, you have found a bug!
[events]: xref:Guides.Concepts.Events

View file

@ -0,0 +1,84 @@
---
uid: Guides.Concepts.Events
title: Working with Events
---
# Events in Discord.Net
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 @System.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
> [!NOTE]
> 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
> @Discord.WebSocket.DiscordSocketConfig to enable it.
## Sample
[!code-csharp[Event Sample](samples/events.cs)]

View file

@ -0,0 +1,48 @@
---
uid: Guides.Concepts.Logging
title: Logging Events/Data
---
# Logging in Discord.Net
Discord.Net's clients provide a log event that all messages will be
dispatched over.
For more information about events in Discord.Net, see the [Events]
section.
[Events]: xref:Guides.Concepts.Events
> [!WARNING]
> 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.
## Usage in Client(s)
To receive log events, simply hook the Discord client's @Discord.Rest.BaseDiscordClient.Log
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 @Discord.Commands.CommandService.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
## Sample
[!code-csharp[Logging Sample](samples/logging.cs)]

View file

@ -0,0 +1,49 @@
# Ratelimits
Ratelimits are a core concept of any API - Discords API is no exception. Each verified library must follow the ratelimit guidelines.
### Using the ratelimit callback
There is a new property within `RequestOptions` called RatelimitCallback. This callback is called when a request is made via the rest api. The callback is called with a `IRateLimitInfo` parameter:
| Name | Type | Description |
| ---------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| IsGlobal | bool | Whether or not this ratelimit info is global. |
| Limit | int? | The number of requests that can be made. |
| Remaining | int? | The number of remaining requests that can be made. |
| RetryAfter | int? | The total time (in seconds) of when the current rate limit bucket will reset. Can have decimals to match previous millisecond ratelimit precision. |
| Reset | DateTimeOffset? | The time at which the rate limit resets. |
| ResetAfter | TimeSpan? | The absolute time when this ratelimit resets. |
| Bucket | string | A unique string denoting the rate limit being encountered (non-inclusive of major parameters in the route path). |
| Lag | TimeSpan? | The amount of lag for the request. This is used to denote the precise time of when the ratelimit expires. |
| Endpoint | string | The endpoint that this ratelimit info came from. |
Let's set up a ratelimit callback that will print out the ratelimit info to the console.
```cs
public async Task MyRatelimitCallback(IRateLimitInfo info)
{
Console.WriteLine($"{info.IsGlobal} {info.Limit} {info.Remaining} {info.RetryAfter} {info.Reset} {info.ResetAfter} {info.Bucket} {info.Lag} {info.Endpoint}");
}
```
Let's use this callback in a send message function
```cs
[Command("ping")]
public async Task ping()
{
var options = new RequestOptions()
{
RatelimitCallback = MyRatelimitCallback
};
await Context.Channel.SendMessageAsync("Pong!", options: options);
}
```
Running this produces the following output:
```
False 5 4 2021-09-09 3:48:14 AM +00:00 00:00:05 a06de0de4a08126315431cc0c55ee3dc 00:00:00.9891364 channels/848511736872828929/messages
```

View file

@ -0,0 +1,34 @@
using Discord;
using Discord.WebSocket;
public class Program
{
private static DiscordSocketClient _client;
public static 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, Environment.GetEnvironmentVariable("DiscordToken"));
await _client.StartAsync();
_client.MessageUpdated += MessageUpdated;
_client.Ready += () =>
{
Console.WriteLine("Bot is connected!");
return Task.CompletedTask;
};
await Task.Delay(-1);
}
private static 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}");
}
}

View file

@ -0,0 +1,24 @@
using Discord;
using Discord.WebSocket;
public class LoggingService
{
public LoggingService(DiscordSocketClient client, CommandService command)
{
client.Log += LogAsync;
command.Log += LogAsync;
}
private Task LogAsync(LogMessage message)
{
if (message.Exception is CommandException cmdException)
{
Console.WriteLine($"[Command/{message.Severity}] {cmdException.Command.Aliases.First()}"
+ $" failed to execute in {cmdException.Context.Channel}.");
Console.WriteLine(cmdException);
}
else
Console.WriteLine($"[General/{message.Severity}] {message}");
return Task.CompletedTask;
}
}

View file

@ -0,0 +1,69 @@
---
uid: Guides.DI.Intro
title: Introduction
---
# Dependency Injection
Dependency injection is a feature not required in Discord.Net, but makes it a lot easier to use.
It can be combined with a large number of other libraries, and gives you better control over your application.
> Further into the documentation, Dependency Injection will be referred to as 'DI'.
## Installation
DI is not native to .NET. You need to install the extension packages to your project in order to use it:
- [Meta](https://www.nuget.org/packages/Microsoft.Extensions.DependencyInjection/).
- [Abstractions](https://www.nuget.org/packages/Microsoft.Extensions.DependencyInjection.Abstractions/).
> [!WARNING]
> Downloading the abstractions package alone will not give you access to required classes to use DI properly.
> Please install both packages, or choose to only install the meta package to implicitly install both.
### Visual Package Manager:
![Installing](images/manager.png)
### Command Line:
`PM> Install-Package Microsoft.Extensions.DependencyInjection`.
> [!TIP]
> ASP.NET already comes packed with all the necessary assemblies in its framework.
> You do not require to install any additional NuGet packages to make full use of all features of DI in ASP.NET projects.
## Getting started
First of all, you will need to create an application based around dependency injection,
which in order will be able to access and inject them across the project.
[!code-csharp[Building the Program](samples/program.cs)]
In order to freely pass around your dependencies in different classes,
you will need to register them to a new `ServiceCollection` and build them into an `IServiceProvider` as seen above.
The IServiceProvider then needs to be accessible by the startup file, so you can access your provider and manage them.
[!code-csharp[Building the Collection](samples/collection.cs)]
As shown above, an instance of `DiscordSocketConfig` is created, and added **before** the client itself is.
Because the collection will prefer to create the highest populated constructor available with the services already present,
it will prefer the constructor with the configuration, because you already added it.
## Using your dependencies
After building your provider in the Program class constructor, the provider is now available inside the instance you're actively using.
Through the provider, we can ask for the DiscordSocketClient we registered earlier.
[!code-csharp[Applying DI in RunAsync](samples/runasync.cs)]
> [!WARNING]
> Service constructors are not activated until the service is **first requested**.
> An 'endpoint' service will have to be requested from the provider before it is activated.
> If a service is requested with dependencies, its dependencies (if not already active) will be activated before the service itself is.
## Injecting dependencies
You can not only directly access the provider from a field or property, but you can also pass around instances to classes registered in the provider.
There are multiple ways to do this. Please refer to the
[Injection Documentation](xref:Guides.DI.Injection) for further information.

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -0,0 +1,44 @@
---
uid: Guides.DI.Injection
title: Injection
---
# Injecting instances within the provider
You can inject registered services into any class that is registered to the `IServiceProvider`.
This can be done through property or constructor.
> [!NOTE]
> As mentioned above, the dependency *and* the target class have to be registered in order for the serviceprovider to resolve it.
## Injecting through a constructor
Services can be injected from the constructor of the class.
This is the preferred approach, because it automatically locks the readonly field in place with the provided service and isn't accessible outside of the class.
[!code-csharp[Constructor Injection](samples/ctor-injecting.cs)]
## Injecting through properties
Injecting through properties is also allowed as follows.
[!code-csharp[Property Injection](samples/property-injecting.cs)]
> [!WARNING]
> Dependency Injection will not resolve missing services in property injection, and it will not pick a constructor instead.
> If a publicly accessible property is attempted to be injected and its service is missing, the application will throw an error.
## Using the provider itself
You can also access the provider reference itself from injecting it into a class. There are multiple use cases for this:
- Allowing libraries (Like Discord.Net) to access your provider internally.
- Injecting optional dependencies.
- Calling methods on the provider itself if necessary, this is often done for creating scopes.
[!code-csharp[Provider Injection](samples/provider.cs)]
> [!NOTE]
> It is important to keep in mind that the provider will pick the 'biggest' available constructor.
> If you choose to introduce multiple constructors,
> keep in mind that services missing from one constructor may have the provider pick another one that *is* available instead of throwing an exception.

View file

@ -0,0 +1,9 @@
async Task RunAsync()
{
//...
await _serviceProvider.GetRequiredService<ServiceActivator>()
.ActivateAsync();
//...
}

View file

@ -0,0 +1,13 @@
static IServiceProvider CreateServices()
{
var config = new DiscordSocketConfig()
{
//...
};
var collection = new ServiceCollection()
.AddSingleton(config)
.AddSingleton<DiscordSocketClient>();
return collection.BuildServiceProvider();
}

View file

@ -0,0 +1,14 @@
public class ClientHandler
{
private readonly DiscordSocketClient _client;
public ClientHandler(DiscordSocketClient client)
{
_client = client;
}
public async Task ConfigureAsync()
{
//...
}
}

View file

@ -0,0 +1,18 @@
public class ServiceActivator
{
// This contains *all* registered services of serviceType IService
private readonly IEnumerable<IService> _services;
public ServiceActivator(IEnumerable<IService> services)
{
_services = services;
}
public async Task ActivateAsync()
{
foreach(var service in _services)
{
await service.StartAsync();
}
}
}

View file

@ -0,0 +1,12 @@
public static ServiceCollection RegisterImplicitServices(this ServiceCollection collection, Type interfaceType, Type activatorType)
{
// Get all types in the executing assembly. There are many ways to do this, but this is fastest.
foreach (var type in typeof(Program).Assembly.GetTypes())
{
if (interfaceType.IsAssignableFrom(type) && !type.IsAbstract)
collection.AddSingleton(interfaceType, type);
}
// Register the activator so you can activate the instances.
collection.AddSingleton(activatorType);
}

View file

@ -0,0 +1,16 @@
public class MyModule : InteractionModuleBase
{
private readonly MyService _service;
public MyModule(MyService service)
{
_service = service;
}
[SlashCommand("things", "Shows things")]
public async Task ThingsAsync()
{
var str = string.Join("\n", _service.Things)
await RespondAsync(str);
}
}

View file

@ -0,0 +1,16 @@
public class Program
{
private static IServiceProvider _serviceProvider;
static IServiceProvider CreateProvider()
{
var collection = new ServiceCollection();
//...
return collection.BuildServiceProvider();
}
static async Task Main(string[] args)
{
_serviceProvider = CreateProvider();
}
}

View file

@ -0,0 +1,9 @@
public class ClientHandler
{
public DiscordSocketClient Client { get; set; }
public async Task ConfigureAsync()
{
//...
}
}

View file

@ -0,0 +1,26 @@
public class UtilizingProvider
{
private readonly IServiceProvider _provider;
private readonly AnyService _service;
// This service is allowed to be null because it is only populated if the service is actually available in the provider.
private readonly AnyOtherService? _otherService;
// This constructor injects only the service provider,
// and uses it to populate the other dependencies.
public UtilizingProvider(IServiceProvider provider)
{
_provider = provider;
_service = provider.GetRequiredService<AnyService>();
_otherService = provider.GetService<AnyOtherService>();
}
// This constructor injects the service provider, and AnyService,
// making sure that AnyService is not null without having to call GetRequiredService
public UtilizingProvider(IServiceProvider provider, AnyService service)
{
_provider = provider;
_service = service;
_otherService = provider.GetService<AnyOtherService>();
}
}

View file

@ -0,0 +1,17 @@
async Task RunAsync(string[] args)
{
// Request the instance from the client.
// Because we're requesting it here first, its targetted constructor will be called and we will receive an active instance.
var client = _services.GetRequiredService<DiscordSocketClient>();
client.Log += async (msg) =>
{
await Task.CompletedTask;
Console.WriteLine(msg);
}
await client.LoginAsync(TokenType.Bot, "");
await client.StartAsync();
await Task.Delay(Timeout.Infinite);
}

View file

@ -0,0 +1,6 @@
// With serviceType:
collection.AddScoped<IScopedService, ScopedService>();
// Without serviceType:
collection.AddScoped<ScopedService>();

View file

@ -0,0 +1,21 @@
static IServiceProvider CreateServices()
{
var config = new DiscordSocketConfig()
{
//...
};
// X represents either Interaction or Command, as it functions the exact same for both types.
var servConfig = new XServiceConfig()
{
//...
}
var collection = new ServiceCollection()
.AddSingleton(config)
.AddSingleton<DiscordSocketClient>()
.AddSingleton(servConfig)
.AddSingleton<XService>();
return collection.BuildServiceProvider();
}

View file

@ -0,0 +1,9 @@
public class MyService
{
public List<string> Things { get; }
public MyService()
{
Things = new();
}
}

View file

@ -0,0 +1,6 @@
// With serviceType:
collection.AddSingleton<ISingletonService, SingletonService>();
// Without serviceType:
collection.AddSingleton<SingletonService>();

View file

@ -0,0 +1,6 @@
// With serviceType:
collection.AddTransient<ITransientService, TransientService>();
// Without serviceType:
collection.AddTransient<TransientService>();

View file

@ -0,0 +1,39 @@
---
uid: Guides.DI.Scaling
title: Scaling your DI
---
# Scaling your DI
Dependency injection has a lot of use cases, and is very suitable for scaled applications.
There are a few ways to make registering & using services easier in large amounts.
## Using a range of services.
If you have a lot of services that all have the same use such as handling an event or serving a module,
you can register and inject them all at once by some requirements:
- All classes need to inherit a single interface or abstract type.
- While not required, it is preferred if the interface and types share a method to call on request.
- You need to register a class that all the types can be injected into.
### Registering implicitly
Registering all the types is done through getting all types in the assembly and checking if they inherit the target interface.
[!code-csharp[Registering](samples/implicit-registration.cs)]
> [!NOTE]
> As seen above, the interfaceType and activatorType are undefined. For our usecase below, these are `IService` and `ServiceActivator` in order.
### Using implicit dependencies
In order to use the implicit dependencies, you have to get access to the activator you registered earlier.
[!code-csharp[Accessing the activator](samples/access-activator.cs)]
When the activator is accessed and the `ActivateAsync()` method is called, the following code will be executed:
[!code-csharp[Executing the activator](samples/enumeration.cs)]
As a result of this, all the services that were registered with `IService` as its implementation type will execute their starting code, and start up.

View file

@ -0,0 +1,48 @@
---
uid: Guides.DI.Services
title: Using DI in Interaction & Command Frameworks
---
# DI in the Interaction- & Command Service
For both the Interaction- and Command Service modules, DI is quite straight-forward to use.
You can inject any service into modules without the modules having to be registered to the provider.
Discord.Net resolves your dependencies internally.
> [!WARNING]
> The way DI is used in the Interaction- & Command Service are nearly identical, except for one detail:
> [Resolving Module Dependencies](xref:Guides.IntFw.Intro#resolving-module-dependencies)
## Registering the Service
Thanks to earlier described behavior of allowing already registered members as parameters of the available ctors,
The socket client & configuration will automatically be acknowledged and the XService(client, config) overload will be used.
[!code-csharp[Service Registration](samples/service-registration.cs)]
## Usage in modules
In the constructor of your module, any parameters will be filled in by
the @System.IServiceProvider that you've passed.
Any publicly settable properties will also be filled in the same
manner.
[!code-csharp[Module Injection](samples/modules.cs)]
If you accept `Command/InteractionService` or `IServiceProvider` as a parameter in your constructor or as an injectable property,
these entries will be filled by the `Command/InteractionService` that the module is loaded from and the `IServiceProvider` that is passed into it respectively.
> [!NOTE]
> Annotating a property with a [DontInjectAttribute] attribute will
> prevent the property from being injected.
## Services
Because modules are transient of nature and will reinstantiate on every request,
it is suggested to create a singleton service behind it to hold values across multiple command executions.
[!code-csharp[Services](samples/services.cs)]

View file

@ -0,0 +1,52 @@
---
uid: Guides.DI.Dependencies
title: Types of Dependencies
---
# Dependency Types
There are 3 types of dependencies to learn to use. Several different usecases apply for each.
> [!WARNING]
> When registering types with a serviceType & implementationType,
> only the serviceType will be available for injection, and the implementationType will be used for the underlying instance.
## Singleton
A singleton service creates a single instance when first requested, and maintains that instance across the lifetime of the application.
Any values that are changed within a singleton will be changed across all instances that depend on it, as they all have the same reference to it.
### Registration:
[!code-csharp[Singleton Example](samples/singleton.cs)]
> [!NOTE]
> Types like the Discord client and Interaction/Command services are intended to be singleton,
> as they should last across the entire app and share their state with all references to the object.
## Scoped
A scoped service creates a new instance every time a new service is requested, but is kept across the 'scope'.
As long as the service is in view for the created scope, the same instance is used for all references to the type.
This means that you can reuse the same instance during execution, and keep the services' state for as long as the request is active.
### Registration:
[!code-csharp[Scoped Example](samples/scoped.cs)]
> [!NOTE]
> Without using HTTP or libraries like EFCORE, scopes are often unused in Discord bots.
> They are most commonly used for handling HTTP and database requests.
## Transient
A transient service is created every time it is requested, and does not share its state between references within the target service.
It is intended for lightweight types that require little state, to be disposed quickly after execution.
### Registration:
[!code-csharp[Transient Example](samples/transient.cs)]
> [!NOTE]
> Discord.Net modules behave exactly as transient types, and are intended to only last as long as the command execution takes.
> This is why it is suggested for apps to use singleton services to keep track of cross-execution data.

View file

@ -0,0 +1,109 @@
---
uid: Guides.Deployment
title: Deploying the Bot
---
# Deploying a Discord.Net Bot
After finishing your application, you may want to deploy your bot to a
remote location such as a Virtual Private Server (VPS) or another
computer so you can keep the bot up and running 24/7.
## Recommended VPS
For small-medium scaled bots, a cheap VPS (~$5) might be sufficient
enough. Here is a list of recommended VPS provider.
* [DigitalOcean](https://www.digitalocean.com/)
* Description: American cloud infrastructure provider headquartered
in New York City with data centers worldwide.
* Location(s):
* Asia: Singapore, India
* America: Canada, United States
* Europe: Netherlands, Germany, United Kingdom
* Based in: United States
* [Vultr](https://www.vultr.com/)
* Description: DigitalOcean-like
* Location(s):
* Asia: Japan, Australia, Singapore
* America: United States
* Europe: United Kingdom, France, Netherlands, Germany
* Based in: United States
* [OVH](https://www.ovh.com/)
* Description: French cloud computing company that offers VPS,
dedicated servers and other web services.
* Location(s):
* Asia: Australia, Singapore
* America: United States, Canada
* Europe: United Kingdom, Poland, Germany
* Based in: Europe
* [Scaleway](https://www.scaleway.com/)
* Description: Cheap but powerful VPS owned by [Online.net](https://online.net/).
* Location(s):
* Europe: France, Netherlands
* Based in: Europe
* [Time4VPS](https://www.time4vps.eu/)
* Description: Affordable and powerful VPS Hosting in Europe.
* Location(s):
* Europe: Lithuania
* Based in: Europe
* [ServerStarter.Host](https://serverstarter.host/clients/store/discord-bots)
* Description: Bot hosting with a panel for quick deployment and
no Linux knowledge required.
* Location(s):
* America: United States
* Based in: United States
## .NET Core Deployment
> [!NOTE]
> This section only covers the very basics of .NET Core deployment.
> To learn more about .NET Core deployment,
> visit [.NET Core application deployment] by Microsoft.
When redistributing the application - whether for deployment on a
remote machine or for sharing with another user - you may want to
publish the application; in other words, to create a
self-contained package without installing the dependencies
and the runtime on the target platform.
### Framework-dependent Deployment
To deploy a framework-dependent package (i.e. files to be used on a
remote machine with the `dotnet` command), simply publish
the package with:
* `dotnet publish -c Release`
This will create a package with the **least dependencies**
included with the application; however, the remote machine
must have `dotnet` runtime installed before the remote could run the
program.
> [!TIP]
> Do not know how to run a .NET Core application with
> the `dotnet` runtime? Navigate to the folder of the program
> (typically under `$projFolder/bin/Release`) and
> enter `dotnet program.dll` where `program.dll` is your compiled
> binaries.
### Self-contained Deployment
To deploy a self-contained package (i.e. files to be used on a remote
machine without the `dotnet` runtime), publish with a specific
[Runtime ID] with the `-r` switch.
This will create a package with dependencies compiled for the target
platform, meaning that all the required dependencies will be included
with the program. This will result in **larger package size**;
however, that means the copy of the runtime that can be run
natively on the target platform.
For example, the following command will create a Windows
executable (`.exe`) that is ready to be executed on any
Windows 10 x64 based machine:
* `dotnet publish -c Release -r win10-x64`
[.NET Core application deployment]: https://docs.microsoft.com/en-us/dotnet/core/deploying/
[Runtime ID]: https://docs.microsoft.com/en-us/dotnet/core/rid-catalog

102
docs/guides/emoji/emoji.md Normal file
View file

@ -0,0 +1,102 @@
---
uid: Guides.Emoji
title: Emoji
---
# Emoji in Discord.Net
Before we delve into the difference between an @Discord.Emoji and an
@Discord.Emote in Discord.Net, it is **crucial** to understand what
they both look like behind the scene. When the end-users are sending
or receiving an emoji or emote, they are typically in the form of
`:ok_hand:` or `:reeee:`; however, what goes under the hood is that,
depending on the type of emoji, they are sent in an entirely
different format.
What does this all mean? It means that you should know that by
reacting with a string like `“:ok_hand:”` will **NOT** automatically
translate to `👌`; rather, it will be treated as-is,
like `:ok_hand:`, thus the server will return a `400 Bad Request`.
## Emoji
An emoji is a standard emoji that can be found anywhere else outside
of Discord, which means strings like `👌`, `♥`, `👀` are all
considered an emoji in Discord. However, from the
introduction paragraph we have learned that we cannot
simply send `:ok_hand:` and have Discord take
care of it, but what do we need to send exactly?
To send an emoji correctly, one must send the emoji in its Unicode
form; this can be obtained in several different ways.
1. (Easiest) Escape the emoji by using the escape character, `\`, in
your Discord chat client; this will reveal the emojis pure Unicode
form, which will allow you to copy-paste into your code.
2. Look it up on Emojipedia, from which you can copy the emoji
easily into your code.
![Emojipedia](images/emojipedia.png)
3. (Recommended) Look it up in the Emoji list from [FileFormat.Info];
this will give you the .NET-compatible code that
represents the emoji.
* This is the most recommended method because some systems or
IDE sometimes do not render the Unicode emoji correctly.
![Fileformat Emoji Source Code](images/fileformat-emoji-src.png)
### Emoji Declaration
After obtaining the Unicode representation of the emoji, you may
create the @Discord.Emoji object by passing the string with unicode into its
constructor (e.g. `new Emoji("👌");` or `new Emoji("\uD83D\uDC4C");`).
Your method of declaring an @Discord.Emoji should look similar to
this:
[!code-csharp[Emoji Sample](samples/emoji-sample.cs)]
Also you can use `Emoji.Parse()` or `Emoji.TryParse()` methods
for parsing emojis from strings like `:heart:`, `<3` or `❤`.
[FileFormat.Info]: https://www.fileformat.info/info/emoji/list.htm
## Emote
The meat of the debate is here; what is an emote and how does it
differ from an emoji? An emote refers to a **custom emoji**
created on Discord.
The underlying structure of an emote also differs drastically; an
emote looks sort-of like a mention on Discord. It consists of two
main elements as illustrated below:
![Emote illustration](images/emote-format.png)
As you can see, emote uses a completely different format. To obtain
the raw string as shown above for your emote, you would need to
escape the emote using the escape character `\` in chat somewhere.
### Emote Declaration
After obtaining the raw emote string, you would need to use
@Discord.Emote.Parse* or @Discord.Emote.TryParse* to create a valid
emote object.
Your method of declaring an @Discord.Emote should look similar to
this:
[!code[Emote Sample](samples/emote-sample.cs)]
> [!TIP]
> For WebSocket users, you may also consider fetching the Emote
> via the @Discord.WebSocket.SocketGuild.Emotes collection.
> [!code-csharp[Socket emote sample](samples/socket-emote-sample.cs)]
> [!TIP]
> On Discord, any user with Discord Nitro subscription may use
> custom emotes from any guilds they are currently in. This is also
> true for _any_ standard bot accounts; this does not require
> the bot owner to have a Nitro subscription.
## Additional Information
To learn more about emote and emojis and how they could be used,
see the documentation of @Discord.IEmote.

Some files were not shown because too many files have changed in this diff Show more