Browse Source

Squash commits on branch docs/faq-n-patches

pull/988/head
Christopher F 7 years ago
parent
commit
0245812637
100 changed files with 3122 additions and 1071 deletions
  1. +3
    -0
      .gitignore
  2. +16
    -0
      docs/README.md
  3. +31
    -0
      docs/_overwrites/Commands/CommandException.Overwrite.md
  4. +22
    -0
      docs/_overwrites/Commands/DontAutoLoadAttribute.Overwrite.md
  5. +27
    -0
      docs/_overwrites/Commands/DontInjectAttribute.Overwrite.md
  6. +5
    -0
      docs/_overwrites/Commands/ICommandContext.Inclusion.md
  7. +27
    -0
      docs/_overwrites/Commands/ICommandContext.Overwrite.md
  8. +107
    -0
      docs/_overwrites/Commands/PreconditionAttribute.Overwrites.md
  9. +6
    -0
      docs/_overwrites/Commands/PreconditionAttribute.Remarks.Inclusion.md
  10. +48
    -0
      docs/_overwrites/Common/EmbedBuilder.Overwrites.md
  11. +25
    -0
      docs/_overwrites/Common/EmbedObjectBuilder.Inclusion.md
  12. +20
    -0
      docs/_overwrites/Common/EmbedObjectBuilder.Overwrites.md
  13. +26
    -0
      docs/_overwrites/Common/IEmote.Inclusion.md
  14. +81
    -0
      docs/_overwrites/Common/IEmote.Overwrites.md
  15. +24
    -0
      docs/_overwrites/Common/OverrideTypeReaderAttribute.Overwrites.md
  16. BIN
      docs/_overwrites/Common/images/embed-example.png
  17. BIN
      docs/_overwrites/Common/images/react-example.png
  18. +32
    -0
      docs/_template/light-dark-theme/partials/affix.tmpl.partial
  19. +25
    -0
      docs/_template/light-dark-theme/partials/head.tmpl.partial
  20. +14
    -0
      docs/_template/light-dark-theme/partials/scripts.tmpl.partial
  21. +3
    -0
      docs/_template/light-dark-theme/styles/cornerify.js
  22. +263
    -0
      docs/_template/light-dark-theme/styles/dark.css
  23. +43
    -0
      docs/_template/light-dark-theme/styles/light.css
  24. +65
    -0
      docs/_template/light-dark-theme/styles/master.css
  25. +15
    -0
      docs/_template/light-dark-theme/styles/plugin-featherlight.js
  26. +27
    -0
      docs/_template/light-dark-theme/styles/styleswitcher.js
  27. +9
    -0
      docs/_template/light-dark-theme/styles/theme-switcher.css
  28. +115
    -0
      docs/_template/light-dark-theme/styles/vs2015.css
  29. +11
    -8
      docs/api/index.md
  30. +36
    -59
      docs/docfx.json
  31. +111
    -0
      docs/faq/basics/basic-operations.md
  32. +58
    -0
      docs/faq/basics/client-basics.md
  33. +73
    -0
      docs/faq/basics/getting-started.md
  34. BIN
      docs/faq/basics/images/dev-mode.png
  35. BIN
      docs/faq/basics/images/mention-escape.png
  36. BIN
      docs/faq/basics/images/snowflake.png
  37. +15
    -0
      docs/faq/basics/samples/cast.cs
  38. +18
    -0
      docs/faq/basics/samples/emoji.cs
  39. +151
    -0
      docs/faq/commands/Commands.md
  40. +27
    -0
      docs/faq/commands/samples/DI.cs
  41. +20
    -0
      docs/faq/commands/samples/Remainder.cs
  42. +7
    -0
      docs/faq/commands/samples/runmode-cmdattrib.cs
  43. +9
    -0
      docs/faq/commands/samples/runmode-cmdconfig.cs
  44. +73
    -0
      docs/faq/misc/Glossary.md
  45. +26
    -0
      docs/faq/misc/Legacy.md
  46. +18
    -0
      docs/faq/toc.yml
  47. +0
    -343
      docs/guides/commands/commands.md
  48. +45
    -0
      docs/guides/commands/dependency-injection.md
  49. +221
    -0
      docs/guides/commands/intro.md
  50. +122
    -0
      docs/guides/commands/post-execution.md
  51. +58
    -0
      docs/guides/commands/preconditions.md
  52. +13
    -0
      docs/guides/commands/samples/command_exception_log.cs
  53. +13
    -0
      docs/guides/commands/samples/command_executed_adv_demo.cs
  54. +38
    -0
      docs/guides/commands/samples/command_executed_demo.cs
  55. +7
    -33
      docs/guides/commands/samples/command_handler.cs
  56. +6
    -0
      docs/guides/commands/samples/customresult_base.cs
  57. +10
    -0
      docs/guides/commands/samples/customresult_extended.cs
  58. +10
    -0
      docs/guides/commands/samples/customresult_usage.cs
  59. +1
    -1
      docs/guides/commands/samples/dependency_map_setup.cs
  60. +12
    -22
      docs/guides/commands/samples/dependency_module.cs
  61. +2
    -0
      docs/guides/commands/samples/empty-module.cs
  62. +15
    -11
      docs/guides/commands/samples/module.cs
  63. +11
    -0
      docs/guides/commands/samples/post-execution_basic.cs
  64. +1
    -1
      docs/guides/commands/samples/require_owner.cs
  65. +29
    -0
      docs/guides/commands/samples/typereader-register.cs
  66. +1
    -1
      docs/guides/commands/samples/typereader.cs
  67. +69
    -0
      docs/guides/commands/typereaders.md
  68. +14
    -17
      docs/guides/concepts/connections.md
  69. +86
    -0
      docs/guides/concepts/deployment.md
  70. +24
    -29
      docs/guides/concepts/entities.md
  71. +18
    -18
      docs/guides/concepts/events.md
  72. +20
    -19
      docs/guides/concepts/logging.md
  73. +3
    -5
      docs/guides/concepts/samples/entities.cs
  74. +248
    -0
      docs/guides/getting_started/first-bot.md
  75. +73
    -47
      docs/guides/getting_started/installing.md
  76. +0
    -237
      docs/guides/getting_started/intro.md
  77. +9
    -0
      docs/guides/getting_started/samples/first-bot/async-context.cs
  78. +5
    -5
      docs/guides/getting_started/samples/first-bot/client.cs
  79. +38
    -0
      docs/guides/getting_started/samples/first-bot/complete.cs
  80. +5
    -0
      docs/guides/getting_started/samples/first-bot/logging.cs
  81. +1
    -1
      docs/guides/getting_started/samples/first-bot/message.cs
  82. +0
    -0
      docs/guides/getting_started/samples/first-bot/structure.cs
  83. +0
    -15
      docs/guides/getting_started/samples/intro/async-context.cs
  84. +0
    -44
      docs/guides/getting_started/samples/intro/complete.cs
  85. +0
    -22
      docs/guides/getting_started/samples/intro/logging.cs
  86. +0
    -13
      docs/guides/getting_started/samples/project.csproj
  87. +16
    -0
      docs/guides/getting_started/samples/project.xml
  88. +17
    -17
      docs/guides/getting_started/terminology.md
  89. +51
    -0
      docs/guides/introduction/intro.md
  90. +0
    -61
      docs/guides/migrating/migrating.md
  91. +0
    -4
      docs/guides/migrating/samples/event.cs
  92. +0
    -5
      docs/guides/migrating/samples/sync_event.cs
  93. +22
    -13
      docs/guides/toc.yml
  94. +3
    -2
      docs/guides/voice/sending-voice.md
  95. +23
    -8
      docs/index.md
  96. +5
    -2
      docs/toc.yml
  97. +2
    -2
      src/Discord.Net.Commands/Attributes/AliasAttribute.cs
  98. +13
    -0
      src/Discord.Net.Commands/Attributes/CommandAttribute.cs
  99. +1
    -0
      src/Discord.Net.Commands/Attributes/DontAutoLoadAttribute.cs
  100. +9
    -6
      src/Discord.Net.Commands/Attributes/DontInjectAttribute.cs

+ 3
- 0
.gitignore View File

@@ -206,3 +206,6 @@ project.lock.json
docs/api/\.manifest

\.idea/

# Codealike UID
codealike.json

+ 16
- 0
docs/README.md View File

@@ -0,0 +1,16 @@
# Instructions for Building Documentation

The documentation for the Discord.Net library uses [DocFX][docfx-main]. [Instructions for installing this tool can be found here.][docfx-installing]

1. Navigate to the root of the repository.
2. (Optional) If you intend to target a specific version, ensure that you
have the correct version checked out.
3. Build the library. Run `dotnet build` in the root of this repository.
Ensure that the build passes without errors.
4. Build the docs using `docfx .\docs\docfx.json`. Add the `--serve` parameter
to preview the site locally. Some elements of the page may appear incorrect
when not hosted by a server.
- Remarks: According to the docfx website, this tool does work on Linux under mono.

[docfx-main]: https://dotnet.github.io/docfx/
[docfx-installing]: https://dotnet.github.io/docfx/tutorial/docfx_getting_started.html

+ 31
- 0
docs/_overwrites/Commands/CommandException.Overwrite.md 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;
}
```

+ 22
- 0
docs/_overwrites/Commands/DontAutoLoadAttribute.Overwrite.md 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>
{
// ...
}
```

+ 27
- 0
docs/_overwrites/Commands/DontInjectAttribute.Overwrite.md 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();
}
}
```

+ 5
- 0
docs/_overwrites/Commands/ICommandContext.Inclusion.md 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/commands/samples/empty-module.cs)]
[!code[Command handler](../../guides/commands/samples/command_handler.cs)]

+ 27
- 0
docs/_overwrites/Commands/ICommandContext.Overwrite.md 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)]

+ 107
- 0
docs/_overwrites/Commands/PreconditionAttribute.Overwrites.md View File

@@ -0,0 +1,107 @@
---
uid: Discord.Commands.PreconditionAttribute
seealso:
- linkId: Discord.Commands.ParameterPreconditionAttribute
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
seealso:
- linkId: Discord.Commands.PreconditionAttribute
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 RequireRoleAtribute : PreconditionAttribute
{
private readonly ulong _roleId;

public RequireRoleAtribute(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();
}
}
```

+ 6
- 0
docs/_overwrites/Commands/PreconditionAttribute.Remarks.Inclusion.md View File

@@ -0,0 +1,6 @@
A "precondidtion" in the command system is used to determine if a
condition is met before entering the command task. Using a
precondidtion 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.

+ 48
- 0
docs/_overwrites/Common/EmbedBuilder.Overwrites.md View File

@@ -0,0 +1,48 @@
---
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]
---

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!"
}
// Or with methods
.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()
.Build();
await ReplyAsync(embed: embed);
}
```

#### Result

![Embed Example](images/embed-example.png)

+ 25
- 0
docs/_overwrites/Common/EmbedObjectBuilder.Inclusion.md 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://discordapp.com/assets/e05ead6e6ebc08df9291738d0aa6986d.png");
var exampleFooter = new EmbedFooterBuilder()
.WithText("I am a nice footer")
.WithIconUrl("https://discordapp.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();
```

+ 20
- 0
docs/_overwrites/Common/EmbedObjectBuilder.Overwrites.md 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)]

+ 26
- 0
docs/_overwrites/Common/IEmote.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)

+ 81
- 0
docs/_overwrites/Common/IEmote.Overwrites.md 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)]

+ 24
- 0
docs/_overwrites/Common/OverrideTypeReaderAttribute.Overwrites.md 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)
{
//...
}
```

BIN
docs/_overwrites/Common/images/embed-example.png View File

Before After
Width: 334  |  Height: 165  |  Size: 15 KiB

BIN
docs/_overwrites/Common/images/react-example.png View File

Before After
Width: 307  |  Height: 95  |  Size: 8.8 KiB

+ 32
- 0
docs/_template/light-dark-theme/partials/affix.tmpl.partial View File

@@ -0,0 +1,32 @@
{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}

<div class="hidden-sm col-md-2" role="complementary">
<div class="sideaffix">
<div class="theme-switch-field">
<p>Theme</p>
<select id="theme-switcher">
<option value="dark">Dark</option>
<option value="light">Light</option>
</select>
</div>
{{^_disableContribution}}
<div class="contribution">
<ul class="nav">
{{#docurl}}
<li>
<a href="{{docurl}}" class="contribution-link">{{__global.improveThisDoc}}</a>
</li>
{{/docurl}}
{{#sourceurl}}
<li>
<a href="{{sourceurl}}" class="contribution-link">{{__global.viewSource}}</a>
</li>
{{/sourceurl}}
</ul>
</div>
{{/_disableContribution}}
<nav class="bs-docs-sidebar hidden-print hidden-xs hidden-sm affix" id="affix">
<!-- <p><a class="back-to-top" href="#top">Back to top</a><p> -->
</nav>
</div>
</div>

+ 25
- 0
docs/_template/light-dark-theme/partials/head.tmpl.partial View File

@@ -0,0 +1,25 @@
{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}

<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>{{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}}</title>
<meta name="viewport" content="width=device-width">
<meta name="title" content="{{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}}">
<meta name="generator" content="docfx {{_docfxVersion}}">
{{#_description}}<meta name="description" content="{{_description}}">{{/_description}}
<link rel="shortcut icon" href="{{_rel}}{{{_appFaviconPath}}}{{^_appFaviconPath}}favicon.ico{{/_appFaviconPath}}">
<link rel="stylesheet" href="{{_rel}}styles/docfx.vendor.css">
<link rel="stylesheet" href="{{_rel}}styles/docfx.css">
<link rel="stylesheet" href="{{_rel}}styles/master.css">
<link rel="stylesheet" href="{{_rel}}styles/main.css">
<link rel="stylesheet" href="{{_rel}}styles/theme-switcher.css">
<link href="//cdn.rawgit.com/noelboss/featherlight/1.7.6/release/featherlight.min.css" type="text/css" rel="stylesheet" />
<meta property="docfx:navrel" content="{{_navRel}}">
<meta property="docfx:tocrel" content="{{_tocRel}}">
<meta id="docfx-style:rel" content="{{_rel}}">
{{#_noindex}}<meta name="searchOption" content="noindex">{{/_noindex}}
{{#_enableSearch}}<meta property="docfx:rel" content="{{_rel}}">{{/_enableSearch}}
{{#_enableNewTab}}<meta property="docfx:newtab" content="true">{{/_enableNewTab}}
<script type="text/javascript" src="{{_rel}}styles/styleswitcher.js"></script>
</head>

+ 14
- 0
docs/_template/light-dark-theme/partials/scripts.tmpl.partial View File

@@ -0,0 +1,14 @@
{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}

<script type="text/javascript" src="{{_rel}}styles/docfx.vendor.js"></script>
<script type="text/javascript" src="{{_rel}}styles/docfx.js"></script>
<script type="text/javascript" src="{{_rel}}styles/main.js"></script>
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script src="//cdn.rawgit.com/noelboss/featherlight/1.7.6/release/featherlight.min.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" src="{{_rel}}styles/plugin-featherlight.js"></script>
<script type="text/javascript" src="{{_rel}}styles/styleswitcher.js"></script>
<script type="text/javascript" src="//malsup.github.io/jquery.corner.js"></script>
<script type="text/javascript" src="{{_rel}}styles/cornerify.js"></script>

+ 3
- 0
docs/_template/light-dark-theme/styles/cornerify.js View File

@@ -0,0 +1,3 @@
window.onload = function (e) {
$('img').corner();
}

+ 263
- 0
docs/_template/light-dark-theme/styles/dark.css View File

@@ -0,0 +1,263 @@
/* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. */

@import url('vs2015.css');
html,
body {
background: #212121;
color: #C0C0C0;
}

button,
a {
color: #64B5F6;
}

.sidenav{
background-color: rgb(30, 30, 30);
}

button:hover,
button:focus,
a:hover,
a:focus,
.btn:focus,
.btn:hover{
color: #2196F3;
}

a.disable,
a.disable:hover {
color: #EEEEEE;
}

.divider {
color: #37474F;
}

hr {
border-color: #37474F;
}

.subnav {
background: #383838
}

.inheritance h5,
.inheritedMembers h5 {
border-bottom: 1px solid #37474F;
}

article h4 {
border-bottom: 1px solid #37474F;
}

.docs-search {
background: #424242;
}

.search-results-group-heading {
color: #424242;
}

.search-close {
color: #424242;
}

.sidetoc {
background-color: #1b1b1b;
border-left: 0px solid #37474F;
border-right: 0px solid #37474F;
}

.sideaffix {
overflow: visible;
}

.sideaffix>div.contribution>ul>li>a.contribution-link:hover {
background-color: #333333;
}

.toc .nav>li>a {
color: rgb(218, 218, 218);
}

.toc .nav>li>a:hover,
.toc .nav>li>a:focus {
color: #E0E0E0;
}

.toc .nav>li.active>a {
color: #90CAF9;
}

.toc .nav>li.active>a:hover,
.toc .nav>li.active>a:focus {
background-color: #37474F;
color: #4FC3F7;
}

.sidefilter {
background-color: #1b1b1b;
border-left: 0px solid #37474F;
border-right: 0px solid #37474F;
}

.affix ul>li>a:hover {
background: none;
color: #EEEEEE;
}

.affix ul>li.active>a,
.affix ul>li.active>a:before {
color: #B3E5FC;
}

.affix ul>li>a {
color: #EEEEEE;
}

.affix>ul>li.active>a,
.affix>ul>li.active>a:before {
color: #B3E5FC;
}

.tryspan {
border-color: #37474F;
}

.footer {
border-top: 1px solid #5F5F5F;
background: #616161;
}

.alert-info {
color: #d9edf7;
background: #004458;
border-color: #005873
}

.alert-warning {
color: #fffaf2;
background: #80551a;
border-color: #99661f
}

.alert-danger {
color: #fff2f2;
background: #4d0000;
border-color: #660000
}

/* For tabbed content */

.tabGroup {
margin-top: 1rem;
}

.tabGroup ul[role="tablist"] {
margin: 0;
padding: 0;
list-style: none;
}

.tabGroup ul[role="tablist"]>li {
list-style: none;
display: inline-block;
}

.tabGroup a[role="tab"] {
color: white;
box-sizing: border-box;
display: inline-block;
padding: 5px 7.5px;
text-decoration: none;
border-bottom: 2px solid #fff;
}

.tabGroup a[role="tab"]:hover,
.tabGroup a[role="tab"]:focus,
.tabGroup a[role="tab"][aria-selected="true"] {
border-bottom: 2px solid #607D8B;
}

.tabGroup a[role="tab"][aria-selected="true"] {
color: #81D4FA;
}

.tabGroup a[role="tab"]:hover,
.tabGroup a[role="tab"]:focus {
color: #29B6F6;
}

.tabGroup a[role="tab"]:focus {
outline: 1px solid #607D8B;
outline-offset: -1px;
}

@media (min-width: 768px) {
.tabGroup a[role="tab"] {
padding: 5px 15px;
}
}

.tabGroup section[role="tabpanel"] {
border: 1px solid #607D8B;
padding: 15px;
margin: 0;
overflow: hidden;
}

.tabGroup section[role="tabpanel"]>.codeHeader,
.tabGroup section[role="tabpanel"]>pre {
margin-left: -16px;
margin-right: -16px;
}

.tabGroup section[role="tabpanel"]> :first-child {
margin-top: 0;
}

.tabGroup section[role="tabpanel"]>pre:last-child {
display: block;
margin-bottom: -16px;
}

.mainContainer[dir='rtl'] main ul[role="tablist"] {
margin: 0;
}

/* code */

code {
color: white;
background-color: #4a4c52;
border-radius: 4px;
padding: 3px 7px;
}

pre {
background-color: #282a36;
}

/* table */

.table-striped>tbody>tr:nth-of-type(odd) {
background-color: #333333;
color: #d3d3d3
}

tbody>tr {
background-color: #424242;
color: #c0c0c0
}

.table>tbody+tbody {
border-top: 2px solid rgb(173, 173, 173)
}

/* select */

select {
background-color: #3b3b3b;
border-color: #2e2e2e;
}

+ 43
- 0
docs/_template/light-dark-theme/styles/light.css View File

@@ -0,0 +1,43 @@
/* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. */

html,
body {
background: #fff;
color: #000;
}

.sideaffix {
overflow: visible;
}

/* code */

code {
color: #222f3d;
background-color: #f9f9f9;
border-radius: 4px;
padding: 3px 7px;
}

/* table */

.table-striped>tbody>tr:nth-of-type(odd) {
color: #333333;
background-color: #d3d3d3
}

tbody>tr {
color: #424242;
background-color: #c0c0c0
}

.table>tbody+tbody {
border-top: 2px solid rgb(173, 173, 173)
}

/* select */

select {
background-color: #fcfcfc;
border-color: #aeb1b5;
}

+ 65
- 0
docs/_template/light-dark-theme/styles/master.css View File

@@ -0,0 +1,65 @@
@import url('https://fonts.googleapis.com/css?family=Titillium+Web');
html,
body {
font-family: 'Titillium Web', 'Segoe UI', Tahoma, Helvetica, sans-serif;
height: 100%;
font-size: 15px;
}

p,
li,
.toc {
line-height: 160%;
}

img {
box-shadow: 0px 0px 3px 0px rgb(66, 66, 66);
max-width: 95% !important;
margin-top: 15px;
margin-bottom: 15px;
}

h1,
h2,
h3,
h4,
h5,
h6 {
line-height: 130%;
}

.sideaffix {
line-height: 140%;
}

header .navbar {
border-width: 0 0 0px;
border-radius: 0;
}

body .toc {
background-color: inherit;
overflow: visible;
}

select {
display: inline-block;
overflow: auto;
-webkit-box-sizing: border-box;
box-sizing: border-box;
margin: 0;
padding: 0 30px 0 6px;
vertical-align: middle;
height: 28px;
border: 1px solid #e3e3e3;
line-height: 16px;
outline: 0;
text-overflow: ellipsis;
-webkit-appearance: none;
-moz-appearance: none;
cursor: pointer;
background-image: linear-gradient(45deg, transparent 50%, #707070 0), linear-gradient(135deg, #707070 50%, transparent 0);
background-position: calc(100% - 13px) 11px, calc(100% - 8px) 11px;
background-size: 5px 5px, 5px 6px;
background-repeat: no-repeat;
}

+ 15
- 0
docs/_template/light-dark-theme/styles/plugin-featherlight.js View File

@@ -0,0 +1,15 @@
$(document).ready(function() {
//find all images, but not the logo, and add the lightbox
$('img').not('#logo').each(function(){
var $img = $(this);
var filename = $img.attr('src')
//add cursor
$img.css('cursor','zoom-in');
$img.css('cursor','-moz-zoom-in');
$img.css('cursor','-webkit-zoom-in');

//add featherlight
$img.attr('alt', filename);
$img.featherlight(filename);
});
});

+ 27
- 0
docs/_template/light-dark-theme/styles/styleswitcher.js View File

@@ -0,0 +1,27 @@
const baseUrl = document.getElementById("docfx-style:rel").content;
var themeElement;

function onThemeSelect(event) {
const theme = event.target.value;
window.localStorage.setItem("theme", theme);
window.themeElement.href = getUrl(theme);
}

function getUrl(slug) {
return baseUrl + "styles/" + slug + ".css";
}

const themeElement = document.createElement("link");
themeElement.rel = "stylesheet";

const theme = window.localStorage.getItem("theme") || "light";
themeElement.href = getUrl(theme);

document.head.appendChild(themeElement);
window.themeElement = themeElement;

document.addEventListener("DOMContentLoaded", function() {
const themeSwitcher = document.getElementById("theme-switcher");
themeSwitcher.onchange = onThemeSelect;
themeSwitcher.value = theme;
}, false);

+ 9
- 0
docs/_template/light-dark-theme/styles/theme-switcher.css View File

@@ -0,0 +1,9 @@
div.theme-switch-field {
padding-left: 10px;
padding-bottom: 15px
}

div.theme-switch-field > p{
font-weight: bold;
font-size: 1.2em;
}

+ 115
- 0
docs/_template/light-dark-theme/styles/vs2015.css View File

@@ -0,0 +1,115 @@
/*
* Visual Studio 2015 dark style
* Author: Nicolas LLOBERA <nllobera@gmail.com>
*/

.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
background: #282a36;
color: #DCDCDC;
}

.hljs-keyword,
.hljs-literal,
.hljs-symbol,
.hljs-name {
color: #569CD6;
}
.hljs-link {
color: #569CD6;
text-decoration: underline;
}

.hljs-built_in,
.hljs-type {
color: #4EC9B0;
}

.hljs-number,
.hljs-class {
color: #B8D7A3;
}

.hljs-string,
.hljs-meta-string {
color: #D69D85;
}

.hljs-regexp,
.hljs-template-tag {
color: #9A5334;
}

.hljs-subst,
.hljs-function,
.hljs-title,
.hljs-params,
.hljs-formula {
color: #DCDCDC;
}

.hljs-comment,
.hljs-quote {
color: #57A64A;
font-style: italic;
}

.hljs-doctag {
color: #608B4E;
}

.hljs-meta,
.hljs-meta-keyword,
.hljs-tag {
color: #9B9B9B;
}

.hljs-variable,
.hljs-template-variable {
color: #BD63C5;
}

.hljs-attr,
.hljs-attribute,
.hljs-builtin-name {
color: #9CDCFE;
}

.hljs-section {
color: gold;
}

.hljs-emphasis {
font-style: italic;
}

.hljs-strong {
font-weight: bold;
}

/*.hljs-code {
font-family:'Monospace';
}*/

.hljs-bullet,
.hljs-selector-tag,
.hljs-selector-id,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #D7BA7D;
}

.hljs-addition {
background-color: #144212;
display: inline-block;
width: 100%;
}

.hljs-deletion {
background-color: #600;
display: inline-block;
width: 100%;
}

+ 11
- 8
docs/api/index.md View File

@@ -1,13 +1,16 @@
---
uid: API.Docs
---

# API Documentation

This is where you will find documentation for all members and objects in Discord.Net
This is where you will find documentation for all members and objects in Discord.Net.

__Commonly Used Entities__
# Commonly Used Entities

* @Discord.WebSocket
* @Discord.WebSocket.DiscordSocketClient
* @Discord.WebSocket.SocketGuildChannel
* @Discord.WebSocket.SocketGuildUser
* @Discord.WebSocket.SocketMessage
* @Discord.WebSocket.SocketRole
* @Discord.WebSocket
* @Discord.WebSocket.DiscordSocketClient
* @Discord.WebSocket.SocketGuildChannel
* @Discord.WebSocket.SocketGuildUser
* @Discord.WebSocket.SocketMessage
* @Discord.WebSocket.SocketRole

+ 36
- 59
docs/docfx.json View File

@@ -1,74 +1,51 @@
{
"metadata": [
{
"src": [
{
"src": "..",
"files": [
"src/**/*.cs"
],
"exclude": [
"**/obj/**",
"**/bin/**",
"_site/**"
]
}
],
"dest": "api",
"filter": "filterConfig.yml"
"metadata": [{
"src": [{
"src": "../src",
"files": [
"**.csproj"
]
}],
"dest": "api",
"filter": "filterConfig.yml",
"properties": {
"TargetFramework": "netstandard1.3"
}
],
}],
"build": {
"content": [
{
"files": [
"api/**.yml",
"api/index.md"
]
"content": [{
"files": ["api/**.yml", "api/index.md"]
},
{
"files": [
"guides/**.md",
"guides/**/toc.yml",
"toc.yml",
"*.md"
],
"exclude": [
"obj/**",
"_site/**"
]
}
],
"resource": [
"files": ["toc.yml", "index.md"]
},
{
"files": [
"**/images/**",
"**/samples/**"
],
"exclude": [
"obj/**",
"_site/**"
]
}
],
"overwrite": [
"files": ["faq/**.md", "faq/**/toc.yml"]
},
{
"files": [
"apidoc/**.md"
],
"exclude": [
"obj/**",
"_site/**"
]
"files": ["guides/**.md", "guides/**/toc.yml"]
}
],
"resource": [{
"files": [
"**/images/**",
"**/samples/**"
]
}],
"dest": "_site",
"template": [
"default"
"default",
"_template/light-dark-theme"
],
"overwrite": "_overwrites/**/**.md",
"globalMetadata": {
"_appFooter": "Discord.Net (c) 2015-2017"
"_appTitle": "Discord.Net Documentation",
"_appFooter": "Discord.Net (c) 2015-2018 2.0.0-beta",
"_enableSearch": true,
},
"noLangKeyword": false
"noLangKeyword": false,
"xrefService": [
"https://xref.docs.microsoft.com/query?uid={uid}"
]
}
}
}

+ 111
- 0
docs/faq/basics/basic-operations.md View File

@@ -0,0 +1,111 @@
---
uid: FAQ.Basics.BasicOp
title: Questions about Basic Operations
---

# Basic Operations Questions

## 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/programming-guide/types/how-to-safely-cast-by-using-as-and-is-operators

## How do I send a message?

> [!TIP]
> The [GetChannel] method by default returns an [IChannel].
> This means channels such as [IVoiceChannel], [ICategoryChannel]
> can be returned. This is why that you cannot send 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:FAQ.Misc.Glossary#message-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.

[!code-csharp[Emoji](samples/emoji.cs)]

[AddReactionAsync]: xref:Discord.IUserMessage.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/RogueException/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

+ 58
- 0
docs/faq/basics/client-basics.md View File

@@ -0,0 +1,58 @@
---
uid: FAQ.Basics.ClientBasics
title: Basic Questions about Client
---

# Client Basics Questions

## My client keeps returning 401 upon logging in!

> [!WARNING]
> Userbot/selfbot (logging in with a user token) is not
> officially supported with this library.
>
> Logging in under a user account may result in account
> termination!

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 tokens is different from a *client secret*.

[TokenType]: xref:Discord.TokenType

## 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 in order 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's 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

+ 73
- 0
docs/faq/basics/getting-started.md View File

@@ -0,0 +1,73 @@
---
uid: FAQ.Basics.GetStarted
title: Beginner Questions / How to Get Started
---

# Basic Concepts / Getting Started

## How do I add my bot to my server/guild?

You can do so by using the [permission calculator] provided
by FiniteReality.
This tool allows you to set the permissions that the bot will be
added with, and invite the bot into your guild. With this method,
bots will also be assigned their own special roles that normal users
cannot use; this is what we call a `Managed` role, and this is a much
safer method of permission management than to create a role that any
users can be assigned to.

[permission calculator]: https://finitereality.github.io/permissions-calculator

## 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 applies to both user and bot
accounts. That also means that you should never ever 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)

The ID can be seen by anyone; 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.

Several common ways to do this:

1. Make the role mentionable and mention the role, and escape it
using the `\` character in front.
2. Inspect the roles collection within the guild via your debugger.

BIN
docs/faq/basics/images/dev-mode.png View File

Before After
Width: 1366  |  Height: 727  |  Size: 79 KiB

BIN
docs/faq/basics/images/mention-escape.png View File

Before After
Width: 1603  |  Height: 400  |  Size: 19 KiB

BIN
docs/faq/basics/images/snowflake.png View File

Before After
Width: 700  |  Height: 365  |  Size: 71 KiB

+ 15
- 0
docs/faq/basics/samples/cast.cs 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
}
}

+ 18
- 0
docs/faq/basics/samples/emoji.cs 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);

+ 151
- 0
docs/faq/commands/Commands.md View File

@@ -0,0 +1,151 @@
---
uid: FAQ.Commands
title: Questions about Commands
---

# Command-related Questions

## How can I restrict some of my commands so only certain 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` etc.*).

If, however, you wish to restrict the commands based on the user's
role, you can either create your own custom precondition or use
Joe4evr's [Preconditions Addons] that provides a few custom
preconditions that aren't provided in the stock library.
Its source can also be used as an example for creating your own
custom preconditions.

[RequireUserPermission]: xref:Discord.Commands.RequireUserPermissionAttribute
[Preconditions Addons]: https://github.com/Joe4evr/Discord.Addons/tree/master/src/Discord.Addons.Preconditions

## I'm getting an error about `Assembly#GetEntryAssembly`.

You may be confusing [CommandService#AddModulesAsync] with
[CommandService#AddModuleAsync]. The former is used to add modules
via the assembly, while the latter is used to add a single module.

[CommandService#AddModulesAsync]: xref:Discord.Commands.CommandService.AddModulesAsync*
[CommandService#AddModuleAsync]: xref:Discord.Commands.CommandService.AddModuleAsync*

## What does [Remainder] do in the command signature?

The [RemainderAttribute] leaves the string unparsed, meaning you
don't 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

## 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. This means that they are spawned
every time when a request is received, and are killed from memory
when the execution finishes. This is why you cannot store persistent
data inside a module. To workaround this, consider using a service.

Service is often used to hold data externally, so that they will
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,
as well as how it works in [Discord.Net](xref:Guides.Commands.DI#usage-in-modules).

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

## I have a long-running Task in my command, and Discord.Net keeps saying that a `MessageReceived` handler is blocking the gateway. What gives?

By default, all commands are executed on the same thread as the
gateway task, which is responsible for keeping the connection from
your client to Discord alive. When you execute a command,
this blocks the gateway from communicating for as long as the command
task is being executed. The library will warn you about any long
running event handler (in this case, the command handler) that
persists for **more than 3 seconds**.

To resolve this, the library has designed a flag called [RunMode].

There are 2 main `RunMode`s.

1. `RunMode.Sync` (default)
2. `RunMode.Async`

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)]

***

***

> [!IMPORTANT]
> While specifying `RunMode.Async` allows the command to be spun off
> to a different thread instead of the gateway thread,
> keep in mind that there will be **potential consequences**
> by doing so. Before applying this flag, please
> consider whether it is necessary to do so.
>
> Further details regarding `RunMode.Async` can be found below.

[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 `ExecuteAsyncInternalAsync`, the task
that is used to invoke the command task, to be finished on a
different thread. This 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 race condition.
2. Unnecessary overhead caused by [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.

However, there are ways to remedy some of these.

For #3, in Discord.Net 2.0, the library introduces a new event called
[CommandExecuted], which is raised whenever the command is
**successfully executed**. This event will be raised regardless of
the `RunMode` type and will return the appropriate execution result.

For #4, exceptions are caught in [CommandService#Log] event under
[LogMessage.Exception] as [CommandException].

[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
[CommandExecuted]: xref:Discord.Commands.CommandService.CommandExecuted
[CommandService#Log]: xref:Discord.Commands.CommandService.Log
[LogMessage.Exception]: xref:Discord.LogMessage.Exception*
[CommandException]: xref:Discord.Commands.CommandException

+ 27
- 0
docs/faq/commands/samples/DI.cs View File

@@ -0,0 +1,27 @@
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 ctor
private readonly MyService _myService;
public MyModule (MyService myService) => _myService = myService;
[Command("string")]
public Task GetOrSetStringAsync(string input)
{
if (_myService.MyCoolString == null) _myService.MyCoolString = input;
return ReplyAsync(_myService.MyCoolString);
}
}

+ 20
- 0
docs/faq/commands/samples/Remainder.cs 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"

+ 7
- 0
docs/faq/commands/samples/runmode-cmdattrib.cs 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);
}

+ 9
- 0
docs/faq/commands/samples/runmode-cmdconfig.cs View File

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

+ 73
- 0
docs/faq/misc/Glossary.md View File

@@ -0,0 +1,73 @@
---
uid: FAQ.Misc.Glossary
title: Common Terminologies / Glossary
---

# Glossary

## Common Types

* A **Guild** ([IGuild]) is an isolated collection of users and
channels, and are often referred to as "servers".
- Example: [Discord API](https://discord.gg/jkrBmQR)
* A **Channel** ([IChannel]) represents a generic channel.
- Example: #dotnet_discord-net
- See [Channel Types](#channel-types)
[IGuild]: xref:Discord.IGuild
[IChannel]: xref:Discord.IChannel

## Channel Types

### Message Channels
* A **Text channel** ([ITextChannel]) is a message channel from a
Guild.
* A **DM channel** ([IDMChannel]) is a message channel from a DM.
* A **Group channel** ([IGroupChannel]) is a message channel from a
Group.
- This is rarely used due to the bot's inability to join groups.
* A **Private channel** ([IPrivateChannel]) is a DM or a Group.
* A **Message channel** ([IMessageChannel]) can be any of the above.

### Misc Channels
* A **Guild channel** ([IGuildChannel]) is a guild channel in a guild.
- This can be any channels that may exist in a guild.
* A **Voice channel** ([IVoiceChannel]) is a voice channel in a guild.
* A **Category channel** ([ICategoryChannel]) (2.0+) is a category that
holds one or more sub-channels.

[IGuildChannel]: xref:Discord.IGuildChannel
[IMessageChannel]: xref:Discord.IMessageChannel
[ITextChannel]: xref:Discord.ITextChannel
[IGroupChannel]: xref:Discord.IGroupChannel
[IDMChannel]: xref:Discord.IDMChannel
[IPrivateChannel]: xref:Discord.IPrivateChannel
[IVoiceChannel]: xref:Discord.IVoiceChannel
[ICategoryChannel]: xref:Discord.ICategoryChannel

## Emoji Types

* An **Emote** ([Emote]) is a custom emote from a guild.
- Example: `<:dotnet:232902710280716288>`
* An **Emoji** ([Emoji]) is a Unicode emoji.
- Example: `👍`

[Emote]: xref:Discord.Emote
[Emoji]: xref:Discord.Emoji

## Activity Types

* A **Game** ([Game]) refers to a user's game activity.
* A **Rich Presence** ([RichGame]) refers to a user's detailed
gameplay status.
- Visit [Rich Presence Intro] on Discord docs for more info.
* A **Streaming Status** ([StreamingGame]) refers to user's activity
for streaming on services such as Twitch.
* A **Spotify Status** ([SpotifyGame]) (2.0+) refers to a user's
activity for listening to a song on Spotify.

[Game]: xref:Discord.Game
[RichGame]: xref:Discord.RichGame
[StreamingGame]: xref:Discord.StreamingGame
[SpotifyGame]: xref:Discord.SpotifyGame
[Rich Presence Intro]: https://discordapp.com/developers/docs/rich-presence/best-practices

+ 26
- 0
docs/faq/misc/Legacy.md View File

@@ -0,0 +1,26 @@
---
uid: FAQ.Misc.Legacy
title: Questions about Legacy Versions
---

# Legacy Questions

## X, Y, Z does not work! It doesn't return a valid value anymore

If you're currently using an older version in stable branch, please
upgrade to the latest pre-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 pre-release.

[release tag]: https://github.com/RogueException/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/foxbot/DiscordBotBase/blob/csharp/src/DiscordBot/Program.cs#L36).

[DependencyInjection]: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection

+ 18
- 0
docs/faq/toc.yml View File

@@ -0,0 +1,18 @@
- 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: Command
items:
- name: Commands
topicUid: FAQ.Commands
- name: Misc
items:
- name: Glossary
topicUid: FAQ.Misc.Glossary
- name: Legacy or Upgrade
topicUid: FAQ.Misc.Legacy

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

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

[Discord.Commands](xref:Discord.Commands) provides an Attribute-based
command parser.

## Setup

To use Commands, you must create a [Command Service] and a Command
Handler.

Included below is a very barebone Command Handler. You can extend your
Command Handler as much as you like; however, the below is the bare
minimum.

The `CommandService` will optionally accept a [CommandServiceConfig],
which _does_ set a few default values for you. It is recommended to
look over the properties in [CommandServiceConfig] and their default
values.

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

[Command Service]: xref:Discord.Commands.CommandService
[CommandServiceConfig]: xref:Discord.Commands.CommandServiceConfig

## With Attributes

In 1.0, Commands can be defined ahead of time with attributes, or at
runtime with builders.

For most bots, ahead-of-time Commands should be all you need, and this
is the recommended method of defining Commands.

### Modules

The first step to creating Commands is to create a _module_.

A Module is an organizational pattern that allows you to write your
Commands in different classes and have them automatically loaded.

Discord.Net's implementation of Modules is influenced heavily from
ASP.NET Core's Controller pattern. This means that the lifetime of a
module instance is only as long as the Command is being invoked.

**Avoid using long-running code** in your modules wherever possible.
You should **not** be implementing very much logic into your modules,
instead, outsource to a service for that.

If you are unfamiliar with Inversion of Control, it is recommended to
read the MSDN article on [IoC] and [Dependency Injection].

To begin, create a new class somewhere in your project and inherit the
class from [ModuleBase]. This class **must** be `public`.

>[!NOTE]
>[ModuleBase] is an _abstract_ class, meaning that you may extend it
>or override it as you see fit. Your module may inherit from any
>extension of ModuleBase.

By now, your module should look like this:

[!code-csharp[Empty Module](samples/empty-module.cs)]

[IoC]: https://msdn.microsoft.com/en-us/library/ff921087.aspx
[Dependency Injection]: https://msdn.microsoft.com/en-us/library/ff921152.aspx
[ModuleBase]: xref:Discord.Commands.ModuleBase`1

### Adding Commands

The next step to creating Commands is actually creating the Commands.

To create a Command, add a method to your module of type `Task`.
Typically, you will want to mark this method as `async`, although it
is not required.

Adding parameters to a Command is done by adding parameters to the
parent Task.

For example, to take an integer as an argument from the user, add `int
arg`; to take a user as an argument from the user, add `IUser user`.
In 1.0, a Command can accept nearly any type of argument; a full list
of types that are parsed by default can be found in the below section
on _Type Readers_.

Parameters, by default, are always required. To make a parameter
optional, give it a default value. To accept a comma-separated list,
set the parameter to `params Type[]`.

Should a parameter include spaces, it **must** be wrapped in quotes.
For example, for a Command with a parameter `string food`, you would
execute it with `!favoritefood "Key Lime Pie"`.

If you would like a parameter to parse until the end of a Command,
flag the parameter with the [RemainderAttribute]. This will allow a
user to invoke a Command without wrapping a parameter in quotes.

Finally, flag your Command with the [CommandAttribute]. (you must
specify a name for this Command, except for when it is part of a
Module Group - see below)

[RemainderAttribute]: xref:Discord.Commands.RemainderAttribute
[CommandAttribute]: xref:Discord.Commands.CommandAttribute

### Command Overloads

You may add overloads to your Commands, and the Command parser will
automatically pick up on it.

If for whatever reason, you have two Commands which are ambiguous to
each other, you may use the @Discord.Commands.PriorityAttribute to
specify which should be tested before the other.

The `Priority` attributes are sorted in ascending order; the higher
priority will be called first.

### Command Context

Every Command can access the execution context through the [Context]
property on [ModuleBase]. `ICommandContext` allows you to access the
message, channel, guild, and user that the Command was invoked from,
as well as the underlying Discord client that the Command was invoked
from.

Different types of Contexts may be specified using the generic variant
of [ModuleBase]. When using a [SocketCommandContext], for example, the
properties on this context will already be Socket entities, so you
will not need to cast them.

To reply to messages, you may also invoke [ReplyAsync], instead of
accessing the channel through the [Context] and sending a message.

> [!WARNING]
>Contexts should **NOT** be mixed! You cannot have one module that
>uses `CommandContext` and another that uses `SocketCommandContext`.

[Context]: xref:Discord.Commands.ModuleBase`1#Discord_Commands_ModuleBase_1_Context
[SocketCommandContext]: xref:Discord.Commands.SocketCommandContext
[ReplyAsync]: xref:Discord.Commands.ModuleBase`1#Discord_Commands_ModuleBase_1_ReplyAsync_System_String_System_Boolean_Discord_Embed_Discord_RequestOptions_

### Example Module

At this point, your module should look comparable to this example:
[!code-csharp[Example Module](samples/module.cs)]

#### Loading Modules Automatically

The Command Service can automatically discover all classes in an
Assembly that inherit [ModuleBase] and load them.

To opt a module out of auto-loading, flag it with
[DontAutoLoadAttribute].

Invoke [CommandService.AddModulesAsync] to discover modules and
install them.

[DontAutoLoadAttribute]: xref:Discord.Commands.DontAutoLoadAttribute
[CommandService.AddModulesAsync]: xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddModulesAsync_Assembly_

#### Loading Modules Manually

To manually load a module, invoke [CommandService.AddModuleAsync] by
passing in the generic type of your module and optionally, a
dependency map.

[CommandService.AddModuleAsync]: xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddModuleAsync__1

### Module Constructors

Modules are constructed using Dependency Injection. Any parameters
that are placed in the Module's constructor must be injected into an
@System.IServiceProvider first. Alternatively, you may accept an
`IServiceProvider` as an argument and extract services yourself.

### Module Properties

Modules with `public` settable properties will have the dependencies
injected after the construction of the Module.

### Module Groups

Module Groups allow you to create a module where Commands are
prefixed. To create a group, flag a module with the
@Discord.Commands.GroupAttribute.

Module groups also allow you to create **nameless Commands**, where
the [CommandAttribute] is configured with no name. In this case, the
Command will inherit the name of the group it belongs to.

### Submodules

Submodules are Modules that reside within another one. Typically,
submodules are used to create nested groups (although not required to
create nested groups).

[!code-csharp[Groups and Submodules](samples/groups.cs)]

## With Builders

**TODO**

## Dependency Injection

The Command Service is bundled with a very barebone Dependency
Injection service for your convenience. It is recommended that you use
DI when writing your modules.

### Setup

First, you need to create an @System.IServiceProvider; you may create
your own one if you wish.

Next, add the dependencies that your modules will use to the map.

Finally, pass the map into the `LoadAssembly` method. Your modules
will be automatically loaded with this dependency map.

[!code-csharp[IServiceProvider Setup](samples/dependency_map_setup.cs)]

### Usage in Modules

In the constructor of your Module, any parameters will be filled in by
the @System.IServiceProvider that you've passed into `LoadAssembly`.

Any publicly settable properties will also be filled in the same
manner.

>[!NOTE]
> Annotating a property with a [DontInjectAttribute] attribute will prevent the
property from being injected.

>[!NOTE]
>If you accept `CommandService` or `IServiceProvider` as a parameter
in your constructor or as an injectable property, these entries will
be filled by the `CommandService` that the Module is loaded from and
the `ServiceProvider` that is passed into it respectively.

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

[DontInjectAttribute]: xref:Discord.Commands.DontInjectAttribute

# Preconditions

Precondition serve as a permissions system for your Commands. Keep in
mind, however, that they are not limited to _just_ permissions and can
be as complex as you want them to be.

>[!NOTE]
>There are two types of Preconditions.
[PreconditionAttribute] can be applied to Modules, Groups, or Commands;
[ParameterPreconditionAttribute] can be applied to Parameters.

[PreconditionAttribute]: xref:Discord.Commands.PreconditionAttribute
[ParameterPreconditionAttribute]: xref:Discord.Commands.ParameterPreconditionAttribute

## Bundled Preconditions

Commands ship with four bundled Preconditions; you may view their
usages on their respective API pages.

- @Discord.Commands.RequireContextAttribute
- @Discord.Commands.RequireOwnerAttribute
- @Discord.Commands.RequireBotPermissionAttribute
- @Discord.Commands.RequireUserPermissionAttribute

## Custom Preconditions

To write your own Precondition, create a new class that inherits from
either [PreconditionAttribute] or [ParameterPreconditionAttribute]
depending on your use.

In order for your Precondition to function, you will need to override
the [CheckPermissions] method.

Your IDE should provide an option to fill this in for you.

If the context meets the required parameters, return
[PreconditionResult.FromSuccess], otherwise return
[PreconditionResult.FromError] and include an error message if
necessary.

[!code-csharp[Custom Precondition](samples/require_owner.cs)]

[CheckPermissions]: xref:Discord.Commands.PreconditionAttribute#Discord_Commands_PreconditionAttribute_CheckPermissions_Discord_Commands_ICommandContext_Discord_Commands_CommandInfo_IServiceProvider_
[PreconditionResult.FromSuccess]: xref:Discord.Commands.PreconditionResult#Discord_Commands_PreconditionResult_FromSuccess
[PreconditionResult.FromError]: xref:Discord.Commands.PreconditionResult#Discord_Commands_PreconditionResult_FromError_System_String_

# Type Readers

Type Readers allow you to parse different types of arguments in
your commands.

By default, the following Types are supported arguments:

- bool
- char
- sbyte/byte
- ushort/short
- uint/int
- ulong/long
- float, double, decimal
- string
- DateTime/DateTimeOffset/TimeSpan
- IMessage/IUserMessage
- IChannel/IGuildChannel/ITextChannel/IVoiceChannel/IGroupChannel
- IUser/IGuildUser/IGroupUser
- IRole

### Creating a Type Readers

To create a `TypeReader`, create a new class that imports @Discord and
@Discord.Commands and ensure the class inherits from
@Discord.Commands.TypeReader.

Next, satisfy the `TypeReader` class by overriding the [Read] method.

>[!NOTE]
>In many cases, Visual Studio can fill this in for you, using the
>"Implement Abstract Class" IntelliSense hint.

Inside this task, add whatever logic you need to parse the input
string.

If you are able to successfully parse the input, return
[TypeReaderResult.FromSuccess] with the parsed input, otherwise return
[TypeReaderResult.FromError] and include an error message if
necessary.

[TypeReaderResult]: xref:Discord.Commands.TypeReaderResult
[TypeReaderResult.FromSuccess]: xref:Discord.Commands.TypeReaderResult#Discord_Commands_TypeReaderResult_FromSuccess_Discord_Commands_TypeReaderValue_
[TypeReaderResult.FromError]: xref:Discord.Commands.TypeReaderResult#Discord_Commands_TypeReaderResult_FromError_Discord_Commands_CommandError_System_String_
[Read]: xref:Discord.Commands.TypeReader#Discord_Commands_TypeReader_Read_Discord_Commands_ICommandContext_System_String_IServiceProvider_

#### Sample

[!code-csharp[TypeReaders](samples/typereader.cs)]

### Installing TypeReaders

TypeReaders are not automatically discovered by the Command Service
and must be explicitly added.

To install a TypeReader, invoke [CommandService.AddTypeReader].

[CommandService.AddTypeReader]: xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddTypeReader__1_Discord_Commands_TypeReader_

+ 45
- 0
docs/guides/commands/dependency-injection.md View File

@@ -0,0 +1,45 @@
---
uid: Guides.Commands.DI
title: Dependency Injection
---

# Dependency Injection

The Command Service is bundled with a very barebone Dependency
Injection service for your convenience. It is recommended that you use
DI when writing your modules.

## Setup

1. Create an @System.IServiceProvider.
2. Add the dependencies to the service collection that you wish
to use in the modules.
3. Pass the service collection into `AddModulesAsync`.

### Example - Setting up Injection

[!code-csharp[IServiceProvider Setup](samples/dependency_map_setup.cs)]

## Usage in Modules

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.

> [!NOTE]
> Annotating a property with a [DontInjectAttribute] attribute will
> prevent the property from being injected.

> [!NOTE]
> If you accept `CommandService` or `IServiceProvider` as a parameter
> in your constructor or as an injectable property, these entries will
> be filled by the `CommandService` that the module is loaded from and
> the `IServiceProvider` that is passed into it respectively.

### Example - Injection in Modules

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

[DontInjectAttribute]: xref:Discord.Commands.DontInjectAttribute

+ 221
- 0
docs/guides/commands/intro.md View File

@@ -0,0 +1,221 @@
---
uid: Guides.Commands.Intro
title: Introduction to Command Service
---

# The Command Service

[Discord.Commands](xref:Discord.Commands) provides an attribute-based
command parser.

## Get Started

To use commands, you must create a [Command Service] and a command
handler.

Included below is a barebone command handler. You can extend your
command handler as much as you like; however, the below is the bare
minimum.

> [!NOTE]
> The `CommandService` will optionally accept a [CommandServiceConfig],
> which *does* set a few default values for you. It is recommended to
> look over the properties in [CommandServiceConfig] and their default
> values.

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

[Command Service]: xref:Discord.Commands.CommandService
[CommandServiceConfig]: xref:Discord.Commands.CommandServiceConfig

## With Attributes

Starting from 1.0, commands can be defined ahead of time with
attributes, or at runtime with builders.

For most bots, ahead-of-time commands should be all you need, and this
is the recommended method of defining commands.

### Modules

The first step to creating commands is to create a _module_.

A module is an organizational pattern that allows you to write your
commands in different classes and have them automatically loaded.

Discord.Net's implementation of "modules" is influenced heavily from
ASP.NET Core's Controller pattern. This means that the lifetime of a
module instance is only as long as the command is being invoked.

Before we create a module, it is **crucial** for you to remember that
in order to create a module and have it automatically discovered,
your module must:

* Be public
* Inherit [ModuleBase]

By now, your module should look like this:

[!code-csharp[Empty Module](samples/empty-module.cs)]

> [!NOTE]
> [ModuleBase] is an `abstract` class, meaning that you may extend it
> or override it as you see fit. Your module may inherit from any
> extension of ModuleBase.

[IoC]: https://msdn.microsoft.com/en-us/library/ff921087.aspx
[Dependency Injection]: https://msdn.microsoft.com/en-us/library/ff921152.aspx
[ModuleBase]: xref:Discord.Commands.ModuleBase`1

### Adding/Creating Commands

> [!WARNING]
> **Avoid using long-running code** in your modules wherever possible.
> You should **not** be implementing very much logic into your
> modules, instead, outsource to a service for that.
>
> If you are unfamiliar with Inversion of Control, it is recommended
> to read the MSDN article on [IoC] and [Dependency Injection].

The next step to creating commands is actually creating the commands.

For a command to be valid, it **must** have a return type of `Task`
or `Task<RuntimeResult>`. Typically, you might want to mark this
method as `async`, although it is not required.

Then, flag your command with the [CommandAttribute]. Note that you must
specify a name for this command, except for when it is part of a
[Module Group](#module-groups).

### Command Parameters

Adding parameters to a command is done by adding parameters to the
parent `Task`.

For example:

* To take an integer as an argument from the user, add `int num`.
* To take a user as an argument from the user, add `IUser user`.
* ...etc.

Starting from 1.0, a command can accept nearly any type of argument;
a full list of types that are parsed by default can
be found in @Guides.Commands.TypeReaders.

[CommandAttribute]: xref:Discord.Commands.CommandAttribute

#### Optional Parameters

Parameters, by default, are always required. To make a parameter
optional, give it a default value (i.e. `int num = 0`).

#### Parameters with Spaces

To accept a comma-separated list, set the parameter to `params Type[]`.

Should a parameter include spaces, the parameter **must** be
wrapped in quotes. For example, for a command with a parameter
`string food`, you would execute it with
`!favoritefood "Key Lime Pie"`.

If you would like a parameter to parse until the end of a command,
flag the parameter with the [RemainderAttribute]. This will
allow a user to invoke a command without wrapping a
parameter in quotes.

[RemainderAttribute]: xref:Discord.Commands.RemainderAttribute

### Command Overloads

You may add overloads to your commands, and the command parser will
automatically pick up on it.

If, for whatever reason, you have two commands which are ambiguous to
each other, you may use the @Discord.Commands.PriorityAttribute to
specify which should be tested before the other.

The `Priority` attributes are sorted in ascending order; the higher
priority will be called first.

### Command Context

Every command can access the execution context through the [Context]
property on [ModuleBase]. `ICommandContext` allows you to access the
message, channel, guild, user, and the underlying Discord client
that the command was invoked from.

Different types of `Context` may be specified using the generic variant
of [ModuleBase]. When using a [SocketCommandContext], for example, the
properties on this context will already be Socket entities, so you
will not need to cast them.

To reply to messages, you may also invoke [ReplyAsync], instead of
accessing the channel through the [Context] and sending a message.

> [!WARNING]
> Contexts should **NOT** be mixed! You cannot have one module that
> uses `CommandContext` and another that uses `SocketCommandContext`.

[Context]: xref:Discord.Commands.ModuleBase`1.Context
[SocketCommandContext]: xref:Discord.Commands.SocketCommandContext
[ReplyAsync]: xref:Discord.Commands.ModuleBase`1.ReplyAsync*

> [!TIP]
> At this point, your module should look comparable to this example:
> [!code-csharp[Example Module](samples/module.cs)]

#### Loading Modules Automatically

The Command Service can automatically discover all classes in an
`Assembly` that inherit [ModuleBase] and load them. Invoke
[CommandService.AddModulesAsync] to discover modules and
install them.

To opt a module out of auto-loading, flag it with
[DontAutoLoadAttribute].

[DontAutoLoadAttribute]: xref:Discord.Commands.DontAutoLoadAttribute
[CommandService.AddModulesAsync]: xref:Discord.Commands.CommandService.AddModulesAsync*

#### Loading Modules Manually

To manually load a module, invoke [CommandService.AddModuleAsync] by
passing in the generic type of your module and optionally, a
service provider.

[CommandService.AddModuleAsync]: xref:Discord.Commands.CommandService.AddModuleAsync*

### Module Constructors

Modules are constructed using @Guides.Commands.DI. Any parameters
that are placed in the Module's constructor must be injected into an
@System.IServiceProvider first.

> [!TIP]
> Alternatively, you may accept an
> `IServiceProvider` as an argument and extract services yourself,
> although this is discouraged.

### Module Properties

Modules with `public` settable properties will have the dependencies
injected after the construction of the module. See @Guides.Commands.DI
to learn more.

### Module Groups

Module Groups allow you to create a module where commands are
prefixed. To create a group, flag a module with the
@Discord.Commands.GroupAttribute.

Module Groups also allow you to create **nameless Commands**, where
the [CommandAttribute] is configured with no name. In this case, the
command will inherit the name of the group it belongs to.

### Submodules

Submodules are "modules" that reside within another one. Typically,
submodules are used to create nested groups (although not required to
create nested groups).

[!code-csharp[Groups and Submodules](samples/groups.cs)]

+ 122
- 0
docs/guides/commands/post-execution.md View File

@@ -0,0 +1,122 @@
---
uid: Guides.Commands.PostExecution
title: Post-command Execution Handling
---

# Preface

When developing commands, you may want to consider building a
post-execution handling system so you can have a finer control
over commands. Discord.Net offers several post-execution workflows
for you to work with.

If you recall, in the [Command Guide], we've shown the following
example for executing and handling commands,

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

You may notice that after we perform [ExecuteAsync], we store the
result and print it to the chat. This is essentially the most
basic post-execution handling.

With this in mind, we could start doing things like the following,

[!code[Basic Command Handler](samples/post-execution_basic.cs)]

However, this may not always be preferred, because you are
creating your post-execution logic *with* the essential command
handler. This could lead to messy code and could potentially be a
violation of the SRP (Single Responsibility Principle).

Another major issue is if your command is marked with
`RunMode.Async`, [ExecuteAsync] will **always** return a successful
[ExecuteResult] instead of the actual result. You can learn more
about the impact in the [FAQ](xref:FAQ.Commands).

## CommandExecuted Event

Enter [CommandExecuted], an event that was introduced in
Discord.Net 2.0. This event is raised whenever a command is
successfully executed **without any run-time exceptions** or **without
any parsing or precondition failure**. This means this event can be
used to streamline your post-execution design, and the best thing
about this event is that it is not prone to `RunMode.Async`'s
[ExecuteAsync] drawbacks.

Thus, we can begin working on code such as:

[!code[CommandExecuted demo](samples/command_executed_demo.cs)]

So now we have a streamlined post-execution pipeline, great! What's
next? We can take this further by using [RuntimeResult].

### RuntimeResult

`RuntimeResult` was originally introduced in 1.0 to allow
developers to centralize their command result logic.
In other words, it is a result type that is designed to be
returned when the command has finished its execution.

However, it wasn't widely adopted due to the aforementioned
[ExecuteAsync] drawback. Since we now have access to a proper
result-handler via the [CommandExecuted] event, we can start
making use of this class.

The best way to make use of it is to create your own version of
`RuntimeResult`. You can achieve this by inheriting the `RuntimeResult`
class.

The following creates a bare-minimum required for a sub-class
of `RuntimeResult`,

[!code[Base Use](samples/customresult_base.cs)]

The sky's the limit from here. You can add any additional information
you'd like regarding the execution result.

For example, you may want to add your own result type or other
helpful information regarding the execution, or something
simple like static methods to help you create return types easily.

[!code[Extended Use](samples/customresult_extended.cs)]

After you're done creating your own [RuntimeResult], you can
implement it in your command by marking the command return type to
`Task<RuntimeResult>`.

> [!NOTE]
> You must mark the return type as `Task<RuntimeResult>` instead of
> `Task<MyCustomResult>`. Only the former will be picked up when
> building the module.

Here's an example of a command that utilizes such logic:

[!code[Usage](samples/customresult_usage.cs)]

And now we can check for it in our [CommandExecuted] handler:

[!code[Usage](samples/command_executed_adv_demo.cs)]

## CommandService.Log Event

We have so far covered the handling of various result types, but we
haven't talked about what to do if the command enters a catastrophic
failure (i.e. exceptions). To resolve this, we can make use of the
[CommandService.Log] event.

All exceptions thrown during a command execution will be caught and
be sent to the Log event under the [LogMessage.Exception] property
as a [CommandException] type. The [CommandException] class allows
us to access the exception thrown, as well as the context
of the command.

[!code[Logger Sample](samples/command_exception_log.cs)]

[CommandException]: xref:Discord.Commands.CommandException
[LogMessage.Exception]: xref:Discord.LogMessage.Exception
[CommandService.Log]: xref:Discord.Commands.CommandService.Log
[RuntimeResult]: xref:Discord.Commands.RuntimeResult
[CommandExecuted]: xref:Discord.Commands.CommandService.CommandExecuted
[ExecuteAsync]: xref:Discord.Commands.CommandService.ExecuteAsync*
[ExecuteResult]: xref:Discord.Commands.ExecuteResult
[Command Guide]: xref:Guides.Commands.Intro

+ 58
- 0
docs/guides/commands/preconditions.md View File

@@ -0,0 +1,58 @@
---
uid: Guides.Commands.Preconditions
title: Preconditions
---

# Preconditions

Preconditions serve as a permissions system for your Commands. Keep in
mind, however, that they are not limited to _just_ permissions and can
be as complex as you want them to be.

There are two types of Preconditions you can use:

* [PreconditionAttribute] can be applied to Modules, Groups, or Commands.
* [ParameterPreconditionAttribute] can be applied to Parameters.

You may visit their respective API documentation to find out more.

[PreconditionAttribute]: xref:Discord.Commands.PreconditionAttribute
[ParameterPreconditionAttribute]: xref:Discord.Commands.ParameterPreconditionAttribute

## Bundled Preconditions

@Discord.Commands ships with several bundled Preconditions for you
to use.

* @Discord.Commands.RequireContextAttribute
* @Discord.Commands.RequireOwnerAttribute
* @Discord.Commands.RequireBotPermissionAttribute
* @Discord.Commands.RequireUserPermissionAttribute
* @Discord.Commands.RequireNsfwAttribute

## Custom Preconditions

To write your own Precondition, create a new class that inherits from
either [PreconditionAttribute] or [ParameterPreconditionAttribute]
depending on your use.

In order for your Precondition to function, you will need to override
the [CheckPermissionsAsync] method.

If the context meets the required parameters, return
[PreconditionResult.FromSuccess], otherwise return
[PreconditionResult.FromError] and include an error message if
necessary.

> [!NOTE]
> Visual Studio can help you implement missing members
> from the abstract class by using the "Implement Abstract Class"
> IntelliSense hint.

### Example - Creating a Custom Precondition

[!code-csharp[Custom Precondition](samples/require_owner.cs)]

[CheckPermissionsAsync]: xref:Discord.Commands.PreconditionAttribute.CheckPermissionsAsync*
[PreconditionResult.FromSuccess]: xref:Discord.Commands.PreconditionResult.FromSuccess*
[PreconditionResult.FromError]: xref:Discord.Commands.PreconditionResult.FromError*

+ 13
- 0
docs/guides/commands/samples/command_exception_log.cs View File

@@ -0,0 +1,13 @@
public async Task LogAsync(LogMessage logMessage)
{
// This casting type requries C#7
if (logMessage.Exception is CommandException cmdException)
{
// We can tell the user that something unexpected has happened
await cmdException.Context.Channel.SendMessageAsync("Something went catastrophically wrong!");

// We can also log this incident
Console.WriteLine($"{cmdException.Context.User} failed to execute '{cmdException.Command.Name}' in {cmdException.Context.Channel}.");
Console.WriteLine(cmdException.ToString());
}
}

+ 13
- 0
docs/guides/commands/samples/command_executed_adv_demo.cs View File

@@ -0,0 +1,13 @@
public async Task OnCommandExecutedAsync(CommandInfo command, ICommandContext context, IResult result)
{
switch(result)
{
case MyCustomResult customResult:
// do something extra with it
break;
default:
if (!string.IsNullOrEmpty(result.ErrorReason))
await context.Channel.SendMessageAsync(result.ErrorReason);
break;
}
}

+ 38
- 0
docs/guides/commands/samples/command_executed_demo.cs View File

@@ -0,0 +1,38 @@
public async Task SetupAsync()
{
await _command.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
// Hook the execution event
_command.CommandExecuted += OnCommandExecutedAsync;
// Hook the command handler
_client.MessageReceived += HandleCommandAsync;
}
public async Task OnCommandExecutedAsync(CommandInfo command, ICommandContext context, IResult result)
{
// We have access to the information of the command executed,
// the context of the command, and the result returned from the
// execution in this event.

// We can tell the user what went wrong
if (!string.IsNullOrEmpty(result?.ErrorReason))
{
await context.Channel.SendMessageAsync(result.ErrorReason);
}

// ...or even log the result (the method used should fit into
// your existing log handler)
await _log.LogAsync(new LogMessage(LogSeverity.Info, "CommandExecution", $"{command?.Name} was executed at {DateTime.UtcNow}."));
}
public async Task HandleCommandAsync(SocketMessage msg)
{
var message = messageParam as SocketUserMessage;
if (message == null) return;
int argPos = 0;
if (!(message.HasCharPrefix('!', ref argPos) || message.HasMentionPrefix(_client.CurrentUser, ref argPos))) return;
var context = new SocketCommandContext(_client, message);
var result = await _commands.ExecuteAsync(context, argPos, _services);
// Optionally, you may pass the result manually into your
// CommandExecuted event handler if you wish to handle parsing or
// precondition failures in the same method.

// await OnCommandExecutedAsync(null, context, result);
}

+ 7
- 33
docs/guides/commands/samples/command_handler.cs View File

@@ -1,46 +1,20 @@
using System;
using System.Threading.Tasks;
using System.Reflection;
using Discord;
using Discord.WebSocket;
using Discord.Commands;
using Microsoft.Extensions.DependencyInjection;

public class Program
public class CommandHandle
{
private CommandService _commands;
private DiscordSocketClient _client;
private IServiceProvider _services;

private static void Main(string[] args) => new Program().StartAsync().GetAwaiter().GetResult();
private readonly DiscordSocketClient _client;
private readonly CommandService _commands;

public async Task StartAsync()
public CommandHandle(DiscordSocketClient client)
{
_client = new DiscordSocketClient();
_client = client;
_commands = new CommandService();

// Avoid hard coding your token. Use an external source instead in your code.
string token = "bot token here";

_services = new ServiceCollection()
.AddSingleton(_client)
.AddSingleton(_commands)
.BuildServiceProvider();

await InstallCommandsAsync();

await _client.LoginAsync(TokenType.Bot, token);
await _client.StartAsync();

await Task.Delay(-1);
}
public async Task InstallCommandsAsync()
{
// Hook the MessageReceived Event into our Command Handler
_client.MessageReceived += HandleCommandAsync;
// Discover all of the commands in this assembly and load them.
await _commands.AddModulesAsync(Assembly.GetEntryAssembly());
await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
}

private async Task HandleCommandAsync(SocketMessage messageParam)


+ 6
- 0
docs/guides/commands/samples/customresult_base.cs View File

@@ -0,0 +1,6 @@
public class MyCustomResult : RuntimeResult
{
public MyCustomResult(CommandError? error, string reason) : base(error, reason)
{
}
}

+ 10
- 0
docs/guides/commands/samples/customresult_extended.cs View File

@@ -0,0 +1,10 @@
public class MyCustomResult : RuntimeResult
{
public MyCustomResult(CommandError? error, string reason) : base(error, reason)
{
}
public static MyCustomResult FromError(string reason) =>
new MyCustomResult(CommandError.Unsuccessful, reason);
public static MyCustomResult FromSuccess(string reason = null) =>
new MyCustomResult(null, reason);
}

+ 10
- 0
docs/guides/commands/samples/customresult_usage.cs View File

@@ -0,0 +1,10 @@
public class MyModule : ModuleBase<SocketCommandContext>
{
[Command("eat")]
public async Task<RuntimeResult> ChooseAsync(string food)
{
if (food == "salad")
return MyCustomResult.FromError("No salad allowed!");
return MyCustomResult.FromSuccess($"I'll take a {food}!").
}
}

+ 1
- 1
docs/guides/commands/samples/dependency_map_setup.cs View File

@@ -14,5 +14,5 @@ public async Task InstallAsync(DiscordSocketClient client)
.AddSingleton<DatabaseService>()
.BuildServiceProvider();
// ...
await _commands.AddModulesAsync(Assembly.GetEntryAssembly());
await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
}

+ 12
- 22
docs/guides/commands/samples/dependency_module.cs View File

@@ -1,40 +1,30 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;

public class ModuleA : ModuleBase
public class DatabaseModule : ModuleBase<SocketCommandContext>
{
private readonly DatabaseService _database;

// Dependencies can be injected via the constructor
public ModuleA(DatabaseService database)
public DatabaseModule(DatabaseService database)
{
_database = database;
}

public async Task ReadFromDb()
[Command("read")]
public async Task ReadFromDbAsync()
{
var x = _database.getX();
await ReplyAsync(x);
await ReplyAsync(_database.GetData());
}
}

public class ModuleB
public class MixModule : ModuleBase<SocketCommandContext>
{

// Public settable properties will be injected
public AnnounceService { get; set; }
public AnnounceService AnnounceService { get; set; }

// Public properties without setters will not
public CommandService Commands { get; }
// Public properties without setters will not be injected
public ImageService ImageService { get; }

// Public properties annotated with [DontInject] will not
// be injected
[DontInject]
public NotificationService { get; set; }

public ModuleB(CommandService commands)
{
Commands = commands;
}

}
public NotificationService NotificationService { get; set; }
}

+ 2
- 0
docs/guides/commands/samples/empty-module.cs View File

@@ -1,5 +1,7 @@
using Discord.Commands;

// Keep in mind your module **must** be public and inherit ModuleBase.
// If it isn't, it will not be discovered by AddModulesAsync!
public class InfoModule : ModuleBase<SocketCommandContext>
{

+ 15
- 11
docs/guides/commands/samples/module.cs View File

@@ -1,24 +1,25 @@
// Create a module with no prefix
public class Info : ModuleBase<SocketCommandContext>
public class InfoModule : ModuleBase<SocketCommandContext>
{
// ~say hello -> hello
// ~say hello world -> hello world
[Command("say")]
[Summary("Echoes a message.")]
public async Task SayAsync([Remainder] [Summary("The text to echo")] string echo)
{
// ReplyAsync is a method on ModuleBase
await ReplyAsync(echo);
}
public Task SayAsync([Remainder] [Summary("The text to echo")] string echo)
=> ReplyAsync(echo);
// ReplyAsync is a method on ModuleBase
}

// Create a module with the 'sample' prefix
[Group("sample")]
public class Sample : ModuleBase<SocketCommandContext>
public class SampleModule : ModuleBase<SocketCommandContext>
{
// ~sample square 20 -> 400
[Command("square")]
[Summary("Squares a number.")]
public async Task SquareAsync([Summary("The number to square.")] int num)
public async Task SquareAsync(
[Summary("The number to square.")]
int num)
{
// We can also access the channel from the Command Context.
await Context.Channel.SendMessageAsync($"{num}^2 = {Math.Pow(num, 2)}");
@@ -31,9 +32,12 @@ public class Sample : ModuleBase<SocketCommandContext>
// ~sample userinfo 96642168176807936 --> Khionu#8708
// ~sample whois 96642168176807936 --> Khionu#8708
[Command("userinfo")]
[Summary("Returns info about the current user, or the user parameter, if one passed.")]
[Summary
("Returns info about the current user, or the user parameter, if one passed.")]
[Alias("user", "whois")]
public async Task UserInfoAsync([Summary("The (optional) user to get info for")] SocketUser user = null)
public async Task UserInfoAsync(
[Summary("The (optional) user to get info from")]
SocketUser user = null)
{
var userInfo = user ?? Context.Client.CurrentUser;
await ReplyAsync($"{userInfo.Username}#{userInfo.Discriminator}");


+ 11
- 0
docs/guides/commands/samples/post-execution_basic.cs View File

@@ -0,0 +1,11 @@
var result = await _commands.ExecuteAsync(context, argPos, _services);
if (result.CommandError != null)
switch(result.CommandError)
{
case CommandError.BadArgCount:
await context.Channel.SendMessageAsync("Parameter count does not match any command's.");
break;
default:
await context.Channel.SendMessageAsync($"An error has occurred {result.ErrorReason}");
break;
}

+ 1
- 1
docs/guides/commands/samples/require_owner.cs View File

@@ -10,7 +10,7 @@ using System.Threading.Tasks;
public class RequireOwnerAttribute : PreconditionAttribute
{
// Override the CheckPermissions method
public async override Task<PreconditionResult> CheckPermissions(ICommandContext context, CommandInfo command, IServiceProvider services)
public async override Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
{
// Get the ID of the bot's owner
var ownerId = (await services.GetService<DiscordSocketClient>().GetApplicationInfoAsync()).Owner.Id;


+ 29
- 0
docs/guides/commands/samples/typereader-register.cs View File

@@ -0,0 +1,29 @@
public class CommandHandler
{
private readonly CommandService _commands;
private readonly DiscordSocketClient _client;
private readonly IServiceProvider _services;

public CommandHandler(CommandService commands, DiscordSocketClient client, IServiceProvider services)
{
_commands = commands;
_client = client;
_services = services;
}

public async Task SetupAsync()
{
_client.MessageReceived += CommandHandleAsync;

// Add BooleanTypeReader to type read for the type "bool"
_commands.AddTypeReader(typeof(bool), new BooleanTypeReader());

// Then register the modules
await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
}

public async Task CommandHandleAsync(SocketMessage msg)
{
// ...
}
}

+ 1
- 1
docs/guides/commands/samples/typereader.cs View File

@@ -4,7 +4,7 @@ using Discord.Commands;

public class BooleanTypeReader : TypeReader
{
public override Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider services)
public override Task<TypeReaderResult> ReadAsync(ICommandContext context, string input, IServiceProvider services)
{
bool result;
if (bool.TryParse(input, out result))


+ 69
- 0
docs/guides/commands/typereaders.md View File

@@ -0,0 +1,69 @@
---
uid: Guides.Commands.TypeReaders
title: Type Readers
---

# Type Readers

Type Readers allow you to parse different types of arguments in
your commands.

By default, the following Types are supported arguments:

* `bool`
* `char`
* `sbyte`/`byte`
* `ushort`/`short`
* `uint`/`int`
* `ulong`/`long`
* `float`, `double`, `decimal`
* `string`
* `DateTime`/`DateTimeOffset`/`TimeSpan`
* `Nullable<T>` where applicible
* Any implementation of `IChannel`/`IMessage`/`IUser`/`IRole`

## Creating a Type Reader

To create a `TypeReader`, create a new class that imports @Discord and
@Discord.Commands and ensure the class inherits from
@Discord.Commands.TypeReader. Next, satisfy the `TypeReader` class by
overriding the [ReadAsync] method.

Inside this Task, add whatever logic you need to parse the input
string.

If you are able to successfully parse the input, return
[TypeReaderResult.FromSuccess] with the parsed input, otherwise return
[TypeReaderResult.FromError] and include an error message if
necessary.

> [!NOTE]
> Visual Studio can help you implement missing members
> from the abstract class by using the "Implement Abstract Class"
> IntelliSense hint.

[TypeReaderResult]: xref:Discord.Commands.TypeReaderResult
[TypeReaderResult.FromSuccess]: xref:Discord.Commands.TypeReaderResult.FromSuccess*
[TypeReaderResult.FromError]: xref:Discord.Commands.TypeReaderResult.FromError*
[ReadAsync]: xref:Discord.Commands.TypeReader.ReadAsync*

### Example - Creating a Type Reader

[!code-csharp[TypeReaders](samples/typereader.cs)]

## Registering a Type Reader

TypeReaders are not automatically discovered by the Command Service
and must be explicitly added.

To register a TypeReader, invoke [CommandService.AddTypeReader].

> [!IMPORTANT]
> TypeReaders must be added prior to module discovery, otherwise your
> TypeReaders may not work!

[CommandService.AddTypeReader]: xref:Discord.Commands.CommandService.AddTypeReader*

### Example - Adding a Type Reader

[!code-csharp[Adding TypeReaders](samples/typereader-register.cs)]

+ 14
- 17
docs/guides/concepts/connections.md View File

@@ -1,24 +1,21 @@
---
uid: Guides.Concepts.ManageConnections
title: Managing Connections
---

In Discord.Net, once a client has been started, it will automatically
maintain a connection to Discord's gateway, until it is manually
maintain a connection to Discord's gateway until it is manually
stopped.

### Usage

To start a connection, invoke the `StartAsync` method on a client that
supports a WebSocket connection.

These clients include the [DiscordSocketClient] and
[DiscordRpcClient], as well as Audio clients.

To end a connection, invoke the `StopAsync` method. This will
gracefully close any open WebSocket or UdpSocket connections.
supports a WebSocket connection; to end a connection, invoke the
`StopAsync` method. This will gracefully close any open WebSocket or
UdpSocket connections.

Since the Start/Stop methods only signal to an underlying connection
manager that a connection needs to be started, **they return before a
manager that a connection needs to be started, **they return before a
connection is actually made.**

As a result, you will need to hook into one of the connection-state
@@ -27,25 +24,25 @@ 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
the [DiscordSocketClient], this does **not** mean that the client is
ready to be used.

A separate event, `Ready`, is provided on DiscordSocketClient, which
A separate event, `Ready`, is provided on [DiscordSocketClient], which
is raised only when the client has finished guild stream or guild
sync, and has a complete guild cache.

[DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient
[DiscordRpcClient]: xref:Discord.Rpc.DiscordRpcClient

### Samples

[!code-csharp[Connection Sample](samples/events.cs)]

### Tips
### Reconnection

Avoid running long-running code on the gateway! If you deadlock the
gateway (as explained in [events]), the connection manager will be
unable to recover and reconnect.
> [!TIP]
> Avoid running long-running code on the gateway! If you deadlock the
> gateway (as explained in [events]), the connection manager will be
> unable to recover and reconnect.

Assuming the client disconnected because of a fault on Discord's end,
and not a deadlock on your end, we will always attempt to reconnect
@@ -55,4 +52,4 @@ Don't worry about trying to maintain your own connections, the
connection manager is designed to be bulletproof and never fail - if
your client doesn't manage to reconnect, you've found a bug!

[events]: events.md
[events]: xref:Guides.Concepts.Events

+ 86
- 0
docs/guides/concepts/deployment.md View File

@@ -0,0 +1,86 @@
---
uid: Guides.Concepts.Deployment
title: Deploying the Bot
---

# Deploying the 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

## .NET Core Deployment

> [!NOTE]
> This section only covers the very basics of .NET Core deployment.
> To learn more about deployment, visit [.NET Core application deployment]
> by Microsoft.

By default, .NET Core compiles all projects as a DLL file, so that any
.NET Core runtime can execute the application.

You may execute the application via `dotnet myprogram.dll` assuming you
have the dotnet CLI installed.

When redistributing the application, you may want to publish the
application, or in other words, create a self-contained package
for use on another machine without installing the dependencies first.

This can be achieved by using the dotnet CLI too on the development
machine:

`dotnet publish -c Release`

Additionally, you may want to target a specific platform when
publishing the application so you may use the application without
having to install the Core runtime on the target machine. To do this,
you may specify an [Runtime ID] upon build/publish with the `-r`
option.

For example, when targeting a Windows 10 machine, you may want to use
the following to create the application in Windows executable
format (.exe):

`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

+ 24
- 29
docs/guides/concepts/entities.md View File

@@ -1,23 +1,26 @@
---
uid: Guides.Concepts.Entities
title: Entities
---

>[!NOTE]
This article is written with the Socket variants of entities in mind,
not the general interfaces or Rest/Rpc entities.
# Entities in Discord.Net

> [!NOTE]
> This article is written with the Socket variants of entities in mind,
> not the general interfaces or Rest/Rpc entities.

Discord.Net provides a versatile entity system for navigating the
Discord API.

### Inheritance
## Inheritance

Due to the nature of the Discord API, some entities are designed with
multiple variants; for example, `SocketUser` and `SocketGuildUser`.

All models will contain the most detailed version of an entity
possible, even if the type is less detailed.
possible, even if the type is less detailed.

For example, in the case of the `MessageReceived` event, a
For example, in the case of the `MessageReceived` event, a
`SocketMessage` is passed in with a channel property of type
`SocketMessageChannel`. All messages come from channels capable of
messaging, so this is the only variant of a channel that can cover
@@ -28,44 +31,36 @@ But that doesn't mean a message _can't_ come from a
retrieve information about a guild from a message entity, you will
need to cast its channel object to a `SocketTextChannel`.

### Navigation
You can find out various types of entities in the @FAQ.Misc.Glossary
page.

## Navigation

All socket entities have navigation properties on them, which allow
you to easily navigate to an entity's parent or children. As explained
above, you will sometimes need to cast to a more detailed version of
an entity to navigate to its parent.

### Accessing Entities
## Accessing Entities

The most basic forms of entities, `SocketGuild`, `SocketUser`, and
`SocketChannel` can be pulled from the DiscordSocketClient's global
cache, and can be retrieved using the respective `GetXXX` method on
DiscordSocketClient.

>[!TIP]
It is **vital** that you use the proper IDs for an entity when using
a GetXXX method. It is recommended that you enable Discord's
_developer mode_ to allow easy access to entity IDs, found in
Settings > Appearance > Advanced
> [!TIP]
> It is **vital** that you use the proper IDs for an entity when using
> a `GetXXX` method. It is recommended that you enable Discord's
> _developer mode_ to allow easy access to entity IDs, found in
> Settings > Appearance > Advanced. Read more about it in the
> [FAQ](xref:FAQ.Basics.GetStarted) page.

More detailed versions of entities can be pulled from the basic
entities, e.g. `SocketGuild.GetUser`, which returns a
`SocketGuildUser`, or `SocketGuild.GetChannel`, which returns a
entities, e.g. `SocketGuild.GetUser`, which returns a
`SocketGuildUser`, or `SocketGuild.GetChannel`, which returns a
`SocketGuildChannel`. Again, you may need to cast these objects to get
a variant of the type that you need.

### Samples

[!code-csharp[Entity Sample](samples/entities.cs)]

### Tips

Avoid using boxing-casts to coerce entities into a variant, use the
[`as`] keyword, and a null-conditional operator instead.

This allows you to write safer code and avoid [InvalidCastExceptions].

For example, `(message.Author as SocketGuildUser)?.Nickname`.
## Sample

[`as`]: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/as
[InvalidCastExceptions]: https://msdn.microsoft.com/en-us/library/system.invalidcastexception(v=vs.110).aspx
[!code-csharp[Entity Sample](samples/entities.cs)]

+ 18
- 18
docs/guides/concepts/events.md View File

@@ -1,16 +1,19 @@
---
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 `EventArgs`, the
event's parameters are passed directly into the handler.
@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
## Usage

To receive data from an event, hook into it using C#'s delegate
event pattern.
@@ -18,7 +21,7 @@ event pattern.
You may either opt to hook an event to an anonymous function (lambda)
or a named function.

### Safety
## Safety

All events are designed to be thread-safe; events are executed
synchronously off the gateway task in the same context as the gateway
@@ -39,7 +42,7 @@ 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
## 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,
@@ -49,7 +52,7 @@ 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
### 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
@@ -58,10 +61,10 @@ model of the entity _after_ the change was made.

This pattern is typically only found on `EntityUpdated` events.

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

@@ -70,15 +73,12 @@ object.

[Cacheable]: xref:Discord.Cacheable`2

### Samples

[!code-csharp[Event Sample](samples/events.cs)]
> [!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.

### Tips
## Sample

Many events relating to a Message entity (i.e. `MessageUpdated` and
`ReactionAdded`) rely on the client's message cache, which is
**not** enabled by default. Set the `MessageCacheSize` flag in
[DiscordSocketConfig] to enable it.

[DiscordSocketConfig]: xref:Discord.WebSocket.DiscordSocketConfig
[!code-csharp[Event Sample](samples/events.cs)]

+ 20
- 19
docs/guides/concepts/logging.md View File

@@ -1,19 +1,28 @@
---
title: Logging
uid: Guides.Concepts.Logging
title: Logging Events/Data
---

Discord.Net's clients provide a [Log] event that all messages will be
disbatched over.
# 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.

[Log]: xref:Discord.Rest.BaseDiscordClient#Discord_Rest_BaseDiscordClient_Log
[Events]: events.md
[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
## Usage in Client(s)

To receive log events, simply hook the discord client's log method
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
@@ -22,10 +31,10 @@ to a logging function to write their own messages.

[LogMessage]: xref:Discord.LogMessage

### Usage in Commands
## Usage in Commands

Discord.Net's [CommandService] also provides a log event, identical
in signature to other log events.
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
@@ -34,14 +43,6 @@ and error can be found and handled.
[CommandService]: xref:Discord.Commands.CommandService
[CommandException]: xref:Discord.Commands.CommandException

#### Samples
## Sample

[!code-csharp[Logging Sample](samples/logging.cs)]

#### Tips

Due to the nature of Discord.Net's event system, all log event
handlers will be executed synchronously on the gateway thread. If your
log output will be dumped to a Web API (e.g. Sentry), you are advised
to wrap your output in a `Task.Run` so the gateway thread does not
become blocked while waiting for logging data to be written.

+ 3
- 5
docs/guides/concepts/samples/entities.cs View File

@@ -1,13 +1,11 @@
public string GetChannelTopic(ulong id)
{
var channel = client.GetChannel(81384956881809408) as SocketTextChannel;
if (channel == null) return "";
return channel.Topic;
return channel?.Topic;
}

public string GuildOwner(SocketChannel channel)
public SocketGuildUser GetGuildOwner(SocketChannel channel)
{
var guild = (channel as SocketGuildChannel)?.Guild;
if (guild == null) return "";
return Context.Guild.Owner.Username;
return guild?.Owner;
}

+ 248
- 0
docs/guides/getting_started/first-bot.md View File

@@ -0,0 +1,248 @@
---
uid: Guides.GettingStarted.FirstBot
title: Start making a bot
---

# Making a Ping-Pong bot

One of ways to get started with the Discord API is to write a basic
ping-pong bot. This bot will respond to a simple command "ping."
We will expand on this to create more diverse commands later, but for
now, it is a good starting point.

## Creating a Discord Bot

Before writing your bot, it is necessary to create a bot account via the
Discord Applications Portal first.

1. Visit the [Discord Applications Portal].
2. Create a New Application.
3. Give the application a name (this will be the bot's initial username).
4. Create the Application.

![Step 4](images/intro-create-app.png)

5. In the application review page, click **Create a Bot User**.

![Step 5](images/intro-create-bot.png)

6. Confirm the popup.
7. If this bot will be public, check "Public Bot." **Do not tick any
other options!**

[Discord Applications Portal]: https://discordapp.com/developers/applications/me

## Adding your bot to a server

Bots **cannot** use invite links; they must be explicitly invited
through the OAuth2 flow.

1. Open your bot's application on the [Discord Applications Portal].
2. Retrieve the application's **Client ID**.

![Step 2](images/intro-client-id.png)

3. Create an OAuth2 authorization URL

- `https://discordapp.com/oauth2/authorize?client_id=<CLIENT ID>&scope=bot`

4. Open the authorization URL in your browser.
5. Select a server.
6. Click on authorize.

> [!NOTE]
> Only servers where you have the `MANAGE_SERVER` permission will be
> present in this list.

![Step 6](images/intro-add-bot.png)

## Connecting to Discord

If you have not already created a project and installed Discord.Net,
do that now.

For more information, see @Guides.GettingStarted.Installation.

### Async

Discord.Net uses .NET's [Task-based Asynchronous Pattern (TAP)]
extensively - nearly every operation is asynchronous. It is highly
recommended that these operations are awaited in a
properly established async context whenever possible.

To establish an async context, we will be creating an async main method
in your console application, and rewriting the static main method to
invoke the new async main.

[!code-csharp[Async Context](samples/first-bot/async-context.cs)]

As a result of this, your program will now start and immediately
jump into an async context. This will allow us to create a connection
to Discord later on without having to worry about setting up the
correct async implementation.

> [!WARNING]
> If your application throws any exceptions within an async context,
> they will be thrown all the way back up to the first non-async method;
> since our first non-async method is the program's `Main` method, this
> means that **all** unhandled exceptions will be thrown up there, which
> will crash your application.
>
> Discord.Net will prevent exceptions in event handlers from crashing
> your program, but any exceptions in your async main **will** cause
> the application to crash.

[Task-based Asynchronous Pattern (TAP)]: https://docs.microsoft.com/en-us/dotnet/articles/csharp/async

### Creating a logging method

Before we create and configure a Discord client, we will add a method
to handle Discord.Net's log events.

To allow agnostic support of as many log providers as possible, we
log information through a `Log` event with a proprietary `LogMessage`
parameter. See the [API Documentation] for this event.

If you are using your own logging framework, this is where you would
invoke it. For the sake of simplicity, we will only be logging to
the console.

You may learn more about this concept in @Guides.Concepts.Logging.

[!code-csharp[Async Context](samples/first-bot/logging.cs)]

[API Documentation]: xref:Discord.Rest.BaseDiscordClient.Log

### Creating a Discord Client

Finally, we can create a new connection to Discord.

Since we are writing a bot, we will be using a [DiscordSocketClient]
along with socket entities. See @Guides.GettingStarted.Terminology
if you are unsure of the differences.

To establish a new connection, we will create an instance of
[DiscordSocketClient] in the new async main. You may pass in an
optional @Discord.WebSocket.DiscordSocketConfig if necessary. For most
users, the default will work fine.

Before connecting, we should hook the client's `Log` event to the
log handler that we had just created. Events in Discord.Net work
similarly to any other events in C#.

Next, you will need to "login to Discord" with the [LoginAsync]
method with the application's "token."

> [!NOTE]
> Pay attention to what you are copying from the developer portal!
> A token is not the same as the application's "client secret."

![Token](images/intro-token.png)

> [!IMPORTANT]
> Your bot's token can be used to gain total access to your bot, so
> **do __NOT__ share this token with anyone else!** It may behoove you
> to store this token in an external source if you plan on distributing
> the source code for your bot.

We may now invoke the client's [StartAsync] method, which will
start connection/reconnection logic. It is important to note that
**this method will return as soon as connection logic has been started!**

Any methods that rely on the client's state should go in an event
handler. This means that you should **not** directly be interacting with
the client before it is fully ready.

Finally, we will want to block the async main method from returning
when running the application. To do this, we can await an infinite delay
or any other blocking method, such as reading from the console.

The following lines can now be added:

[!code-csharp[Create client](samples/first-bot/client.cs)]

At this point, feel free to start your program and see your bot come
online in Discord.

> [!TIP]
> Encountering a `PlatformNotSupportedException` when starting your bot?
> This means that you are targeting a platform where .NET's default
> WebSocket client is not supported. Refer to the [installation guide]
> for how to fix this.

[DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient
[LoginAsync]: xref:Discord.Rest.BaseDiscordClient.LoginAsync*
[StartAsync]: xref:Discord.WebSocket.DiscordSocketClient.StartAsync*
[installation guide]: xref:Guides.GettingStarted.Installation#installing-on-net-standard-11

### Handling a 'ping'

> [!WARNING]
> Please note that this is *not* a proper way to create a command.
> Use the `CommandService` provided by the library instead, as explained
> in the [Command Guide](xref:Guides.Commands.Intro) section.

Now that we have learned to open a connection to Discord, we can
begin handling messages that the users are sending. To start out, our
bot will listen for any message whose content is equal to `!ping` and
will respond back with "Pong!".

Since we want to listen for new messages, the event to hook into
is [MessageReceived].

In your program, add a method that matches the signature of the
`MessageReceived` event - it must be a method (`Func`) that returns
the type `Task` and takes a single parameter, a [SocketMessage]. Also,
since we will be sending data to Discord in this method, we will flag
it as `async`.

In this method, we will add an `if` block to determine if the message
content fits the rules of our scenario - recall that it must be equal
to `!ping`.

Inside the branch of this condition, we will want to send a message,
`Pong!`, back to the channel from which the message comes from. To
find the channel, look for the `Channel` property on the message
parameter.

Next, we will want to send a message to this channel. Since the
channel object is of type [ISocketMessageChannel], we can invoke the
[SendMessageAsync] instance method. For the message content, send back
a string, "Pong!".

You should have now added the following lines,

[!code-csharp[Message](samples/first-bot/message.cs)]

Now that your first bot is complete. You may continue to add on to this
if you desire, but for any bots that will be carrying out multiple
commands, it is strongly recommended to use the command framework as
shown below.

> [!NOTE]
> For your reference, you may view the [completed program].

[MessageReceived]: xref:Discord.WebSocket.BaseSocketClient.MessageReceived
[SocketMessage]: xref:Discord.WebSocket.SocketMessage
[ISocketMessageChannel]: xref:Discord.WebSocket.ISocketMessageChannel
[SendMessageAsync]: xref:Discord.WebSocket.ISocketMessageChannel.SendMessageAsync*
[completed program]: samples/first-bot/complete.cs

# Building a bot with commands

@Guides.Commands.Intro will guide you through how to setup a program
that is ready for [CommandService], a service that is ready for
advanced command usage.

For reference, view an [annotated example] of this structure.

[annotated example]: samples/first-bot/structure.cs

It is important to know that the recommended design pattern of bots
should be to separate...

1. the program (initialization and command handler)
2. the modules (handle commands)
3. the services (persistent storage, pure functions, data manipulation)

[CommandService]: xref:Discord.Commands.CommandService

+ 73
- 47
docs/guides/getting_started/installing.md View File

@@ -1,38 +1,48 @@
---
uid: Guides.GettingStarted.Installation
title: Installing Discord.Net
---

Discord.Net is distributed through the NuGet package manager, and it
is recommended to use NuGet to get started.
# Discord.Net Installation

Optionally, you may compile from source and install yourself.
Discord.Net is distributed through the NuGet package manager, so it is
recommended for you to install the library that way.

# Supported Platforms
Alternatively, you may compile from the source and install the library
yourself.

## Supported Platforms

Currently, Discord.Net targets [.NET Standard] 1.3 and offers support
for .NET Standard 1.1. If your application will be targeting .NET
Standard 1.1, please see the [additional steps].

Since Discord.Net is built on the .NET Standard, it is also
recommended to create applications using [.NET Core], though not
Since Discord.Net is built on top of .NET Standard, it is also
recommended to create applications using [.NET Core], although it is not
required. When using .NET Framework, it is suggested to target
`.NET Framework 4.6.1` or higher.

> [!WARNING]
> Using this library with [Mono] is not recommended until further
> notice. It is known to have issues with the library's WebSockets
> implementation and may crash the application upon startup.

[Mono]: https://www.mono-project.com/
[.NET Standard]: https://docs.microsoft.com/en-us/dotnet/articles/standard/library
[.NET Core]: https://docs.microsoft.com/en-us/dotnet/articles/core/
[additional steps]: #installing-on-net-standard-11

# Installing with NuGet
## Installing with NuGet

Release builds of Discord.Net 1.0 will be published to the
Release builds of Discord.Net will be published to the
[official NuGet feed].

Development builds of Discord.Net 1.0, as well as addons *(TODO)* are
published to our development [MyGet feed].
Development builds of Discord.Net, as well as add-ons, will be
published to our [MyGet feed].

Direct feed link: `https://www.myget.org/F/discord-net/api/v3/index.json`

Not sure how to add a direct feed? See how [with Visual Studio] or
Not sure how to add a direct feed? See how [with Visual Studio] or
[without Visual Studio].

[official NuGet feed]: https://nuget.org
@@ -40,85 +50,101 @@ Not sure how to add a direct feed? See how [with Visual Studio] or
[with Visual Studio]: https://docs.microsoft.com/en-us/nuget/tools/package-manager-ui#package-sources
[without Visual Studio]: #configuring-nuget-without-visual-studio

## Using Visual Studio
### [Using Visual Studio](#tab/vs-install)

> [!TIP]
>Don't forget to change your package source if you're installing from
the developer feed.
>Also make sure to check "Enable Prereleases" if installing a dev
build!
1. Create a solution for your bot.
2. In Solution Explorer, find the "Dependencies" element under your
bot's project.
> Don't forget to change your package source if you're installing from
> the developer feed.
> Also make sure to check "Enable Prereleases" if installing a dev
> build!
1. Create a new solution for your bot.
2. In the Solution Explorer, find the "Dependencies" element under your
bot's project.
3. Right click on "Dependencies", and select "Manage NuGet packages."
![Step 3](images/install-vs-deps.png)
![Step 3](images/install-vs-deps.png)
4. In the "Browse" tab, search for `Discord.Net`.
5. Install the `Discord.Net` package.
![Step 5](images/install-vs-nuget.png)
![Step 5](images/install-vs-nuget.png)

## Using JetBrains Rider
### [Using JetBrains Rider](#tab/rider-install)

> [!TIP]
Make sure to check the "Prerelease" box if installing a dev build!
> Make sure to check the "Prerelease" box if installing a dev build!

1. Create a new solution for your bot.
2. Open the NuGet window (Tools > NuGet > Manage NuGet packages for
Solution).
Solution).
![Step 2](images/install-rider-nuget-manager.png)
3. In the "Packages" tab, search for `Discord.Net`.
![Step 3](images/install-rider-search.png)
4. Install by adding the package to your project.
![Step 4](images/install-rider-add.png)

## Using Visual Studio Code
### [Using Visual Studio Code](#tab/vs-code)

> [!TIP]
Don't forget to add the package source to a [NuGet.Config file] if
you're installing from the developer feed.
> Don't forget to add the package source to a [NuGet.Config file] if
> you're installing from the developer feed.

1. Create a new project for your bot.
2. Add `Discord.Net` to your .csproj.

[!code-xml[Sample .csproj](samples/project.csproj)]
[!code[Sample .csproj](samples/project.xml)]

[NuGet.Config file]: #configuring-nuget-without-visual-studio

### [Using dotnet CLI](#tab/dotnet-cli)

> [!TIP]
> Don't forget to add the package source to a [NuGet.Config file] if
> you're installing from the developer feed.

1. Open command-line and navigate to where your .csproj is located.
2. Enter `dotnet add package Discord.Net`.

[NuGet.Config file]: #configuring-nuget-without-visual-studio

# Compiling from Source
***

## Compiling from Source

In order to compile Discord.Net, you require the following:
In order to compile Discord.Net, you will need the following:

### Using Visual Studio

- [Visual Studio 2017](https://www.visualstudio.com/)
- [.NET Core SDK 1.0](https://www.microsoft.com/net/download/core#/sdk)
- [.NET Core SDK]

The .NET Core and Docker (Preview) workload is required during Visual
Studio installation.

### Using Command Line

- [.NET Core SDK 1.0](https://www.microsoft.com/net/download/core#/sdk)
- [.NET Core SDK]

# Additional Information
[.NET Core SDK]: https://www.microsoft.com/net/download/

## Installing on .NET Standard 1.1
## Additional Information

### Installing on .NET Standard 1.1

For applications targeting a runtime corresponding with .NET Standard
1.1 or 1.2, the builtin WebSocket and UDP provider will not work. For
applications which utilize a WebSocket connection to Discord
(WebSocket or RPC), third-party provider packages will need to be
1.1 or 1.2, the built-in WebSocket and UDP provider will not work. For
applications which utilize a WebSocket connection to Discord, such as
WebSocket or RPC, third-party provider packages will need to be
installed and configured.

First, install the following packages through NuGet, or compile
> [!NOTE]
> `Discord.Net.Providers.UDPClient` is _only_ required if your
> bot will be utilizing voice chat.

First, install the following packages through NuGet, or compile them
yourself, if you prefer:

- Discord.Net.Providers.WS4Net
- Discord.Net.Providers.UDPClient

Note that `Discord.Net.Providers.UDPClient` is _only_ required if your
bot will be utilizing voice chat.

Next, you will need to configure your [DiscordSocketClient] to use
these custom providers over the default ones.

@@ -131,16 +157,16 @@ are passing into your client.
[DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient
[DiscordSocketConfig]: xref:Discord.WebSocket.DiscordSocketConfig

## Configuring NuGet without Visual Studio
### Configuring NuGet without Visual Studio

If you plan on deploying your bot or developing outside of Visual
Studio, you will need to create a local NuGet configuration file for
your project.

To do this, create a file named `nuget.config` alongside the root of
your application, where the project solution is located.
To do this, create a file named `NuGet.Config` alongside the root of
your application, where the project is located.

Paste the following snippets into this configuration file, adding any
additional feeds as necessary.
additional feeds if necessary.

[!code-xml[NuGet Configuration](samples/nuget.config)]
[!code[NuGet Configuration](samples/nuget.config)]

+ 0
- 237
docs/guides/getting_started/intro.md View File

@@ -1,237 +0,0 @@
---
title: Getting Started
---

# Making a Ping-Pong bot

One of the first steps to getting started with the Discord API is to
write a basic ping-pong bot. We will expand on this to create more
diverse commands later, but for now, it is a good starting point.

## Creating a Discord Bot

Before you can begin writing your bot, it is necessary to create a bot
account on Discord.

1. Visit the [Discord Applications Portal].
2. Create a New Application.
3. Give the application a name (this will be the bot's initial
username).
4. Create the Application.
![Step 4](images/intro-create-app.png)
5. In the application review page, click **Create a Bot User**.
![Step 5](images/intro-create-bot.png)
6. Confirm the popup.
7. If this bot will be public, check "Public Bot." **Do not tick any
other options!**

[Discord Applications Portal]: https://discordapp.com/developers/applications/me

## Adding your bot to a server

Bots **cannot** use invite links, they must be explicitly invited
through the OAuth2 flow.

1. Open your bot's application on the [Discord Applications Portal].
2. Retrieve the app's **Client ID**.
![Step 2](images/intro-client-id.png)
3. Create an OAuth2 authorization URL
`https://discordapp.com/oauth2/authorize?client_id=<CLIENT ID>&scope=bot`
4. Open the authorization URL in your browser.
5. Select a server.
6. Click on authorize.
>[!NOTE]
Only servers where you have the `MANAGE_SERVER` permission will be
present in this list.
![Step 6](images/intro-add-bot.png)


## Connecting to Discord

If you have not already created a project and installed Discord.Net,
do that now. (see the [Installing](installing.md) section)

### Async

Discord.Net uses .NET's [Task-based Asynchronous Pattern (TAP)]
extensively - nearly every operation is asynchronous.

It is highly recommended that these operations are awaited in a
properly established async context whenever possible. Establishing an
async context can be problematic, but not hard.

To do so, we will be creating an async main in your console
application, and rewriting the static main method to invoke the new
async main.

[!code-csharp[Async Context](samples/intro/async-context.cs)]

As a result of this, your program will now start and immediately
jump into an async context. This will allow us to create a connection
to Discord later on without needing to worry about setting up the
correct async implementation.

>[!TIP]
If your application throws any exceptions within an async context,
they will be thrown all the way back up to the first non-async method;
since our first non-async method is the program's `Main` method, this
means that **all** unhandled exceptions will be thrown up there, which
will crash your application. Discord.Net will prevent exceptions in
event handlers from crashing your program, but any exceptions in your
async main **will** cause the application to crash.

[Task-based Asynchronous Pattern (TAP)]: https://docs.microsoft.com/en-us/dotnet/articles/csharp/async

### Creating a logging method

Before we create and configure a Discord client, we will add a method
to handle Discord.Net's log events.

To allow agnostic support of as many log providers as possible, we
log information through a `Log` event with a proprietary `LogMessage`
parameter. See the [API Documentation] for this event.

If you are using your own logging framework, this is where you would
invoke it. For the sake of simplicity, we will only be logging to
the Console.

[!code-csharp[Async Context](samples/intro/logging.cs)]

[API Documentation]: xref:Discord.Rest.BaseDiscordClient#Discord_Rest_BaseDiscordClient_Log

### Creating a Discord Client

Finally, we can create a connection to Discord. Since we are writing
a bot, we will be using a [DiscordSocketClient] along with socket
entities. See the [terminology](terminology.md) if you're unsure of
the differences.

To do so, create an instance of [DiscordSocketClient] in your async
main, passing in a configuration object only if necessary. For most
users, the default will work fine.

Before connecting, we should hook the client's `Log` event to the
log handler that was just created. Events in Discord.Net work
similarly to other events in C#, so hook this event the way that
you typically would.

Next, you will need to "login to Discord" with the `LoginAsync`
method.

You may create a variable to hold your bot's token (this can be found
on your bot's application page on the [Discord Applications Portal]).

![Token](images/intro-token.png)

>[!IMPORTANT]
Your bot's token can be used to gain total access to your bot, so
**do __NOT__ share this token with anyone else!** It may behoove you
to store this token in an external file if you plan on distributing
the source code for your bot.

We may now invoke the client's `StartAsync` method, which will
start connection/reconnection logic. It is important to note that
**this method returns as soon as connection logic has been started!**

Any methods that rely on the client's state should go in an event
handler.

Finally, we will want to block the async main method from returning
until after the application is exited. To do this, we can await an
infinite delay or any other blocking method, such as reading from
the console.

The following lines can now be added:

[!code-csharp[Create client](samples/intro/client.cs)]

At this point, feel free to start your program and see your bot come
online in Discord.

>[!TIP]
Encountering a `PlatformNotSupportedException` when starting your bot?
This means that you are targeting a platform where .NET's default
WebSocket client is not supported. Refer to the [installation guide]
for how to fix this.

[DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient
[installation guide]: installing.md#installing-on-net-standard-11

### Handling a 'ping'

>[!WARNING]
Please note that this is *not* a proper way to create a command.
Use the `CommandService` provided by the library instead, as explained
in the [Command Guide] section.

Now that we have learned how to open a connection to Discord, we can
begin handling messages that users are sending.

To start out, our bot will listen for any message where the content
is equal to `!ping` and respond back with "Pong!".

Since we want to listen for new messages, the event to hook into
is [MessageReceived].

In your program, add a method that matches the signature of the
`MessageReceived` event - it must be a method (`Func`) that returns
the type `Task` and takes a single parameter, a [SocketMessage]. Also,
since we will be sending data to Discord in this method, we will flag
it as `async`.

In this method, we will add an `if` block to determine if the message
content fits the rules of our scenario - recall that it must be equal
to `!ping`.

Inside the branch of this condition, we will want to send a message
back to the channel from which the message comes from - "Pong!". To
find the channel, look for the `Channel` property on the message
parameter.

Next, we will want to send a message to this channel. Since the
channel object is of type [SocketMessageChannel], we can invoke the
`SendMessageAsync` instance method. For the message content, send back
a string containing "Pong!".

You should have now added the following lines:

[!code-csharp[Message](samples/intro/message.cs)]

Now your first bot is complete. You may continue to add on to this
if you desire, but for any bots that will be carrying out multiple
commands, it is strongly recommended to use the command framework as
shown below.

For your reference, you may view the [completed program].

[MessageReceived]: xref:Discord.WebSocket.DiscordSocketClient#Discord_WebSocket_DiscordSocketClient_MessageReceived
[SocketMessage]: xref:Discord.WebSocket.SocketMessage
[SocketMessageChannel]: xref:Discord.WebSocket.ISocketMessageChannel
[completed program]: samples/intro/complete.cs
[Command Guide]: ../commands/commands.md

# Building a bot with commands

This section will show you how to write a program that is ready for
[Commands](../commands/commands.md). Note that we will not be
explaining _how_ to write Commands or Services, it will only be
covering the general structure.

For reference, view an [annotated example] of this structure.

[annotated example]: samples/intro/structure.cs

It is important to know that the recommended design pattern of bots
should be to separate the program (initialization and command handler),
the modules (handle commands), and the services (persistent storage,
pure functions, data manipulation).

**todo:** diagram of bot structure

+ 9
- 0
docs/guides/getting_started/samples/first-bot/async-context.cs View File

@@ -0,0 +1,9 @@
public class Program
{
public static void Main(string[] args)
=> new Program().MainAsync().GetAwaiter().GetResult();

public async Task MainAsync()
{
}
}

docs/guides/getting_started/samples/intro/client.cs → docs/guides/getting_started/samples/first-bot/client.cs View File

@@ -1,17 +1,17 @@
// Program.cs
using Discord.WebSocket;
// ...
private DiscordSocketClient _client;

public async Task MainAsync()
{
_client = new DiscordSocketClient();

_client.Log += Log;

string token = "abcdefg..."; // Remember to keep this private!
// Remember to keep this private or to read this
// from an external source!
string token = "abcdefg...";
await _client.LoginAsync(TokenType.Bot, token);
await _client.StartAsync();

// Block this task until the program is closed.
await Task.Delay(-1);
}
}

+ 38
- 0
docs/guides/getting_started/samples/first-bot/complete.cs View File

@@ -0,0 +1,38 @@
public class Program
{
private DiscordSocketClient _client;
public static void Main(string[] args)
=> new Program().MainAsync().GetAwaiter().GetResult();

public async Task MainAsync()
{
_client = new DiscordSocketClient();

_client.Log += Log;
_client.MessageReceived += MessageReceivedAsync;

// Remember to keep this private or to read this
// from an external source!
string token = "abcdefg...";
await _client.LoginAsync(TokenType.Bot, token);
await _client.StartAsync();

// Block this task until the program is closed.
await Task.Delay(-1);
}

private async Task MessageReceivedAsync(SocketMessage message)
{
if (message.Content == "!ping")
{
await message.Channel.SendMessageAsync("Pong!");
}
}

private Task Log(LogMessage msg)
{
Console.WriteLine(msg.ToString());
return Task.CompletedTask;
}
}

+ 5
- 0
docs/guides/getting_started/samples/first-bot/logging.cs View File

@@ -0,0 +1,5 @@
private Task Log(LogMessage msg)
{
Console.WriteLine(msg.ToString());
return Task.CompletedTask;
}

docs/guides/getting_started/samples/intro/message.cs → docs/guides/getting_started/samples/first-bot/message.cs View File

@@ -1,6 +1,6 @@
public async Task MainAsync()
{
// client.Log ...
// ...
_client.MessageReceived += MessageReceived;
// ...
}

docs/guides/getting_started/samples/intro/structure.cs → docs/guides/getting_started/samples/first-bot/structure.cs View File


+ 0
- 15
docs/guides/getting_started/samples/intro/async-context.cs View File

@@ -1,15 +0,0 @@
using System;
using System.Threading.Tasks;

namespace MyBot
{
public class Program
{
public static void Main(string[] args)
=> new Program().MainAsync().GetAwaiter().GetResult();

public async Task MainAsync()
{
}
}
}

+ 0
- 44
docs/guides/getting_started/samples/intro/complete.cs View File

@@ -1,44 +0,0 @@
using Discord;
using Discord.WebSocket;
using System;
using System.Threading.Tasks;

namespace MyBot
{
public class Program
{
private DiscordSocketClient _client;
public static void Main(string[] args)
=> new Program().MainAsync().GetAwaiter().GetResult();

public async Task MainAsync()
{
_client = new DiscordSocketClient();

_client.Log += Log;
_client.MessageReceived += MessageReceived;

string token = "abcdefg..."; // Remember to keep this private!
await _client.LoginAsync(TokenType.Bot, token);
await _client.StartAsync();

// Block this task until the program is closed.
await Task.Delay(-1);
}

private async Task MessageReceived(SocketMessage message)
{
if (message.Content == "!ping")
{
await message.Channel.SendMessageAsync("Pong!");
}
}

private Task Log(LogMessage msg)
{
Console.WriteLine(msg.ToString());
return Task.CompletedTask;
}
}
}

+ 0
- 22
docs/guides/getting_started/samples/intro/logging.cs View File

@@ -1,22 +0,0 @@
using Discord;
using System;
using System.Threading.Tasks;

namespace MyBot
{
public class Program
{
public static void Main(string[] args)
=> new Program().MainAsync().GetAwaiter().GetResult();

public async Task MainAsync()
{
}

private Task Log(LogMessage msg)
{
Console.WriteLine(msg.ToString());
return Task.CompletedTask;
}
}
}

+ 0
- 13
docs/guides/getting_started/samples/project.csproj View File

@@ -1,13 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
<NoWin32Manifest>true</NoWin32Manifest>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Discord.Net" Version="1.*" />
</ItemGroup>

</Project>

+ 16
- 0
docs/guides/getting_started/samples/project.xml View File

@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<!--
The following may differ depending on the latest version of
.NET Core Framework or Discord.Net.
-->
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Discord.Net" Version="2.*" />
</ItemGroup>

</Project>

+ 17
- 17
docs/guides/getting_started/terminology.md View File

@@ -1,5 +1,5 @@
---
uid: Terminology
uid: Guides.GettingStarted.Terminology
title: Terminology
---

@@ -7,34 +7,34 @@ title: Terminology

## Preface

Most terms for objects remain the same between 0.9 and 1.0. The major
difference is that the ``Server`` is now called ``Guild`` to stay in
line with Discord internally.
Most terms for objects remain the same between 0.9 and 1.0 and above.
The major difference is that the ``Server`` is now called ``Guild``
to stay in line with Discord internally.

## Implementation Specific Entities

Discord.Net 1.0 is split into a core library and three different
implementations - `Discord.Net.Core`, `Discord.Net.Rest`,
Discord.Net is split into a core library and three different
implementations - `Discord.Net.Core`, `Discord.Net.Rest`,
`Discord.Net.Rpc`, and `Discord.Net.WebSockets`.

As a bot developer, you will only need to use `Discord.Net.WebSockets`,
As a bot developer, you will only need to use `Discord.Net.WebSockets`,
but you should be aware of the differences between them.

`Discord.Net.Core` provides a set of interfaces that models Discord's
API. These interfaces are consistent throughout all implementations of
Discord.Net, and if you are writing an implementation-agnostic library
or addon, you can rely on the core interfaces to ensure that your
`Discord.Net.Core` provides a set of interfaces that models Discord's
API. These interfaces are consistent throughout all implementations of
Discord.Net, and if you are writing an implementation-agnostic library
or addon, you can rely on the core interfaces to ensure that your
addon will run on all platforms.

`Discord.Net.Rest` provides a set of concrete classes to be used
**strictly** with the REST portion of Discord's API. Entities in this
`Discord.Net.Rest` provides a set of concrete classes to be used
**strictly** with the REST portion of Discord's API. Entities in this
implementation are prefixed with `Rest` (e.g. `RestChannel`).

`Discord.Net.Rpc` provides a set of concrete classes that are used
with Discord's RPC API. Entities in this implementation are prefixed
`Discord.Net.Rpc` provides a set of concrete classes that are used
with Discord's RPC API. Entities in this implementation are prefixed
with `Rpc` (e.g. `RpcChannel`).

`Discord.Net.WebSocket` provides a set of concrete classes that are
`Discord.Net.WebSocket` provides a set of concrete classes that are
used primarily with Discord's WebSocket API or entities that are kept
in cache. When developing bots, you will be using this implementation.
in cache. When developing bots, you will be using this implementation.
All entities are prefixed with `Socket` (e.g. `SocketChannel`).

+ 51
- 0
docs/guides/introduction/intro.md View File

@@ -0,0 +1,51 @@
---
uid: Guides.Introduction
title: Introduction to Discord.Net
---

# Introduction

## Looking to get started?

First of all, welcome! You may visit us on our Discord should you
have any questions. Before you delve into using the library,
however, you should have some decent understanding of the language
you are about to use. This library touches on
[Task-based Asynchronous Pattern] \(TAP), [polymorphism], [interface]
and many more advanced topics extensively. Please make sure that you
understand these topics to some extent before proceeding.

Here are some examples:

1. [Official quick start guide]
2. [Official template]

> [!NOTE]
> Please note that you should *not* try to blindly copy paste
> the code. The examples are meant to be a template or a guide.
> It is not meant to be something that will work out of the box.

[Official template]: https://github.com/foxbot/DiscordBotBase/tree/csharp/src/DiscordBot
[Official quick start guide]: https://github.com/RogueException/Discord.Net/blob/dev/docs/guides/getting_started/samples/first-bot/structure.cs
[Task-based Asynchronous Pattern]: https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap
[polymorphism]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/polymorphism
[interface]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/interfaces/

## New to .NET/C#?

If you are new to the language, using this lib may prove to be
difficult, but don't worry! There are many resources online that can
help you get started in the wonderful world of .NET. Here are some
resources to get you started.

- [C# Programming Guide (MSDN/Microsoft, Free)](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/)
- [C# Fundamentals For Absolute Beginners (Channel9/Microsoft, Free)](https://channel9.msdn.com/Series/C-Fundamentals-for-Absolute-Beginners)
- [C# Path (Pluralsight, Paid)](https://www.pluralsight.com/paths/csharp)

## Still have questions?

Please visit us at `#dotnet_discord-net` on the [Discord API] server.
Describe the problem in details to us, what you've done, and,
if any, the problematic code uploaded onto [Hastebin](https://hastebin.com).

[Discord API]: https://discord.gg/jkrBmQR

+ 0
- 61
docs/guides/migrating/migrating.md View File

@@ -1,61 +0,0 @@
# Migrating from 0.9

**1.0.0 is the biggest breaking change the library has gone through, due to massive
changes in the design of the library.**

>A medium to advanced understanding is recommended when working with this library.

It is recommended to familiarize yourself with the entities in 1.0 before continuing.
Feel free to look through the library's source directly, look through IntelliSense, or
look through our hosted [API Documentation](xref:Discord).

## Entities

Most API models function _similarly_ to 0.9, however their names have been changed.
You should also keep in mind that we now separate different types of Channels and Users.

Before proceeding, please read over @Terminology to understand the naming behind some objects.

Below is a table that compares most common 0.9 entities to their 1.0 counterparts.

>This should be used mostly for migration purposes. Please take some time to consider whether
>or not you are using the right "tool for the job" when working with 1.0

| 0.9 | 1.0 | Notice |
| --- | --- | ------ |
| Server | @Discord.WebSocket.SocketGuild |
| Channel | @Discord.WebSocket.SocketGuildChannel | Applies only to channels that are members of a Guild |
| Channel.IsPrivate | @Discord.WebSocket.SocketDMChannel
| ChannelType.Text | @Discord.WebSocket.SocketTextChannel | This applies only to Text Channels in Guilds
| ChannelType.Voice | @Discord.WebSocket.SocketVoiceChannel | This applies only to Voice Channels in Guilds
| User | @Discord.WebSocket.SocketGuildUser | This applies only to users belonging to a Guild*
| Profile | @Discord.WebSocket.SocketGuildUser
| Message | @Discord.WebSocket.SocketUserMessage

\* To retrieve an @Discord.WebSocket.SocketGuildUser, you must retrieve the user from an @Discord.WebSocket.SocketGuild.

## Event Registration

Prior to 1.0, events were registered using the standard c# `Handler(EventArgs)` pattern. In 1.0,
events are delegates, but are still registered the same.

For example, let's look at [DiscordSocketClient.MessageReceived](xref:Discord.WebSocket.DiscordSocketClient#Discord_WebSocket_DiscordSocketClient_MessageReceived)

To hook an event into MessageReceived, we now use the following code:
[!code-csharp[Event Registration](samples/event.cs)]

> **All Event Handlers in 1.0 MUST return Task!**

If your event handler is marked as `async`, it will automatically return `Task`. However,
if you do not need to execute asynchronus code, do _not_ mark your handler as `async`, and instead,
stick a `return Task.CompletedTask` at the bottom.

[!code-csharp[Sync Event Registration](samples/sync_event.cs)]

**Event handlers no longer require a sender.** The only arguments your event handler needs to accept
are the parameters used by the event. It is recommended to look at the event in IntelliSense or on the
API docs before implementing it.

## Async

Nearly everything in 1.0 is an async Task. You should always await any tasks you invoke.

+ 0
- 4
docs/guides/migrating/samples/event.cs View File

@@ -1,4 +0,0 @@
_client.MessageReceived += async (msg) =>
{
await msg.Channel.SendMessageAsync(msg.Content);
}

+ 0
- 5
docs/guides/migrating/samples/sync_event.cs View File

@@ -1,5 +0,0 @@
_client.Log += (msg) =>
{
Console.WriteLine(msg.ToString());
return Task.CompletedTask;
}

+ 22
- 13
docs/guides/toc.yml View File

@@ -1,27 +1,36 @@
- name: Introduction
topicUid: Guides.Introduction
- name: Getting Started
items:
- name: Installation
href: getting_started/installing.md
topicUid: Guides.GettingStarted.Installation
- name: Your First Bot
href: getting_started/intro.md
topicUid: Guides.GettingStarted.FirstBot
- name: Terminology
href: getting_started/terminology.md
topicUid: Guides.GettingStarted.Terminology
- name: Basic Concepts
items:
- name: Logging Data
href: concepts/logging.md
topicUid: Guides.Concepts.Logging
- name: Working with Events
href: concepts/events.md
topicUid: Guides.Concepts.Events
- name: Managing Connections
href: concepts/connections.md
topicUid: Guides.Concepts.ManageConnections
- name: Entities
href: concepts/entities.md
topicUid: Guides.Concepts.Entities
- name: Deployment
topicUid: Guides.Concepts.Deployment
- name: The Command Service
items:
- name: Command Guide
href: commands/commands.md
- name: Introduction
topicUid: Guides.Commands.Intro
- name: TypeReaders
topicUid: Guides.Commands.TypeReaders
- name: Preconditions
topicUid: Guides.Commands.Preconditions
- name: Dependency Injection
topicUid: Guides.Commands.DI
- name: Post-execution Handling
topicUid: Guides.Commands.PostExecution
- name: Voice
items:
- name: Voice Guide
href: voice/sending-voice.md
- name: Migrating from 0.9
topicUid: Guides.Voice.SendingVoice

+ 3
- 2
docs/guides/voice/sending-voice.md View File

@@ -1,4 +1,5 @@
---
uid: Guides.Voice.SendingVoice
title: Sending Voice
---

@@ -44,7 +45,7 @@ guild. To switch channels within a guild, invoke [ConnectAsync] on
another voice channel in the guild.

[IAudioClient]: xref:Discord.Audio.IAudioClient
[ConnectAsync]: xref:Discord.IAudioChannel#Discord_IAudioChannel_ConnectAsync_Action_IAudioClient__
[ConnectAsync]: xref:Discord.IAudioChannel.ConnectAsync*

## Transmitting Audio

@@ -98,4 +99,4 @@ you will want to wait for audio to stop playing before continuing on
to the next song. You can await `AudioOutStream.FlushAsync` to wait for
the audio client's internal buffer to clear out.

[!code-csharp[Sending Audio](samples/audio_ffmpeg.cs)]
[!code-csharp[Sending Audio](samples/audio_ffmpeg.cs)]

+ 23
- 8
docs/index.md View File

@@ -1,13 +1,28 @@
---
uid: Root.Landing
title: Home
---

# Discord.Net Documentation

Discord.Net is an asynchronous, multiplatform .NET Library used to interface with the [Discord API](https://discordapp.com/).
## What is Discord.Net?

If this is your first time using Discord.Net, you should refer to the [Intro](guides/getting_started/intro.md) for tutorials.
More experienced users might refer to the [API Documentation](api/index.md) for a breakdown of the individuals objects in the library.
Discord.Net is an asynchronous, multi-platform .NET Library used to
interface with the [Discord API](https://discordapp.com/).

For additional resources:
- [Discord API Guild](https://discord.gg/discord-api) - Look for `#dotnet_discord-net`
- [GitHub](https://github.com/RogueException/Discord.Net/tree/dev)
- [NuGet](https://www.nuget.org/packages/Discord.Net/)
- [MyGet Feed](https://www.myget.org/feed/Packages/discord-net) - Addons and nightly builds
## Where to begin?

If this is your first time using Discord.Net, you should refer to the
[Intro](xref:Guides.Introduction) for tutorials.

More experienced users might want to refer to the
[API Documentation](xref:API.Docs) for a breakdown of the individual
objects in the library.

## Additional Resources

- [Discord API Guild](https://discord.gg/discord-api) - Look for `#dotnet_discord-net`
- [GitHub](https://github.com/RogueException/Discord.Net/)
- [NuGet](https://www.nuget.org/packages/Discord.Net/)
- [MyGet Feed](https://www.myget.org/feed/Packages/discord-net) - Add-ons and nightly builds
- [AppVeyor CI](https://ci.appveyor.com/project/RogueException/discord-net) - Nightly builds via Continuous Integration

+ 5
- 2
docs/toc.yml View File

@@ -1,6 +1,9 @@

- name: Guides
href: guides/
topicUid: Guides.Introduction
- name: FAQ
href: faq/
topicUid: FAQ.Basics.GetStarted
- name: API Documentation
href: api/
homepage: api/index.md
topicUid: API.Docs

+ 2
- 2
src/Discord.Net.Commands/Attributes/AliasAttribute.cs View File

@@ -2,11 +2,11 @@ using System;

namespace Discord.Commands
{
/// <summary> Provides aliases for a command. </summary>
/// <summary> Marks the aliases for a command. </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class AliasAttribute : Attribute
{
/// <summary> The aliases which have been defined for the command. </summary>
/// <summary> Gets the aliases which have been defined for the command. </summary>
public string[] Aliases { get; }

/// <summary> Creates a new <see cref="AliasAttribute"/> with the given aliases. </summary>


+ 13
- 0
src/Discord.Net.Commands/Attributes/CommandAttribute.cs View File

@@ -2,16 +2,29 @@ using System;

namespace Discord.Commands
{
/// <summary> Marks the execution information for a command. </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class CommandAttribute : Attribute
{
/// <summary>
/// Gets the text that has been set to be recognized as a command.
/// </summary>
public string Text { get; }
/// <summary>
/// Specifies the <see cref="RunMode" /> of the command. This affects how the command is executed.
/// </summary>
public RunMode RunMode { get; set; } = RunMode.Default;

/// <inheritdoc />
public CommandAttribute()
{
Text = null;
}

/// <summary>
/// Initializes a new <see cref="CommandAttribute" /> attribute with the specified name.
/// </summary>
/// <param name="text">The name of the command.</param>
public CommandAttribute(string text)
{
Text = text;


+ 1
- 0
src/Discord.Net.Commands/Attributes/DontAutoLoadAttribute.cs View File

@@ -2,6 +2,7 @@ using System;

namespace Discord.Commands
{
/// <summary> Prevents the marked module from being loaded automatically. </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class DontAutoLoadAttribute : Attribute
{


+ 9
- 6
src/Discord.Net.Commands/Attributes/DontInjectAttribute.cs View File

@@ -1,9 +1,12 @@
using System;

namespace Discord.Commands {

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class DontInjectAttribute : Attribute {
}

namespace Discord.Commands
{
/// <summary>
/// Prevents the marked property from being injected into a module.
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class DontInjectAttribute : Attribute
{
}
}

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

Loading…
Cancel
Save