$(VersionSuffix)-dev
diff --git a/LICENSE b/LICENSE
index 3765bf39c..fb9480169 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2015-2019 Discord.Net Contributors
+Copyright (c) 2015-2021 Discord.Net Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index f4bc5811e..cf0293359 100644
--- a/README.md
+++ b/README.md
@@ -1,42 +1,71 @@
# Discord.Net
-[](https://www.nuget.org/packages/Discord.Net)
-[](https://www.myget.org/feed/Packages/discord-net)
-[](https://dev.azure.com/discord-net/Discord.Net/_build/latest?definitionId=1&branchName=dev)
-[](https://discord.gg/jkrBmQR)
-
-An unofficial .NET API Wrapper for the Discord client (http://discordapp.com).
-
-Check out the [documentation](https://discord.foxbot.me/) or join the [Discord API Chat](https://discord.gg/jkrBmQR).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Discord NET is an unofficial .NET API Wrapper for the Discord client (https://discord.com).
+
+## Documentation
+
+- [Nightly](https://discordnet.dev)
## Installation
+
### Stable (NuGet)
+
Our stable builds available from NuGet through the Discord.Net metapackage:
+
- [Discord.Net](https://www.nuget.org/packages/Discord.Net/)
The individual components may also be installed from NuGet:
+
- [Discord.Net.Commands](https://www.nuget.org/packages/Discord.Net.Commands/)
- [Discord.Net.Rest](https://www.nuget.org/packages/Discord.Net.Rest/)
- [Discord.Net.WebSocket](https://www.nuget.org/packages/Discord.Net.WebSocket/)
- [Discord.Net.Webhook](https://www.nuget.org/packages/Discord.Net.Webhook/)
### Unstable (MyGet)
+
Nightly builds are available through our MyGet feed (`https://www.myget.org/F/discord-net/api/v3/index.json`).
+### Unstable (Labs)
+
+Labs builds are available on nuget (`https://www.nuget.org/packages/Discord.Net.Labs/`) and myget (`https://www.myget.org/F/discord-net-labs/api/v3/index.json`).
+
## Compiling
+
In order to compile Discord.Net, you require the following:
### Using Visual Studio
+
- [Visual Studio 2017](https://www.microsoft.com/net/core#windowsvs2017)
- [.NET Core SDK](https://www.microsoft.com/net/download/core)
The .NET Core workload must be selected during Visual Studio installation.
### Using Command Line
+
- [.NET Core SDK](https://www.microsoft.com/net/download/core)
## Known Issues
### WebSockets (Win7 and earlier)
+
.NET Core 1.1 does not support WebSockets on Win7 and earlier. This issue has been fixed since the release of .NET Core 2.1. It is recommended to target .NET Core 2.1 or above for your project if you wish to run your bot on legacy platforms; alternatively, you may choose to install the [Discord.Net.Providers.WS4Net](https://www.nuget.org/packages/Discord.Net.Providers.WS4Net/) package.
## Versioning Guarantees
diff --git a/StyleAnalyzer.targets b/StyleAnalyzer.targets
new file mode 100644
index 000000000..0794cfacb
--- /dev/null
+++ b/StyleAnalyzer.targets
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 5a1d48082..9aa0e5788 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -14,25 +14,20 @@ trigger:
jobs:
- job: Linux
pool:
- vmImage: 'ubuntu-16.04'
+ vmImage: 'ubuntu-latest'
steps:
- - task: UseDotNet@2
- displayName: 'Use .NET Core sdk'
- inputs:
- packageType: 'sdk'
- version: '3.x'
- template: azure/build.yml
- job: Windows_build
pool:
- vmImage: 'windows-2019'
+ vmImage: 'windows-latest'
condition: ne(variables['Build.SourceBranch'], 'refs/heads/dev')
steps:
- template: azure/build.yml
- job: Windows_deploy
pool:
- vmImage: 'windows-2019'
+ vmImage: 'windows-latest'
condition: |
and (
succeeded(),
@@ -44,4 +39,3 @@ jobs:
steps:
- template: azure/build.yml
- template: azure/deploy.yml
- - template: azure/docs.yml
diff --git a/azure/build.yml b/azure/build.yml
index 412e4a823..a4646ad73 100644
--- a/azure/build.yml
+++ b/azure/build.yml
@@ -1,5 +1,17 @@
steps:
-- script: dotnet restore -v minimal Discord.Net.sln
+- task: UseDotNet@2
+ displayName: 'Use .NET Core sdk'
+ inputs:
+ packageType: 'sdk'
+ version: '6.0.x'
+ includePreviewVersions: true
+
+- task: DotNetCoreCLI@2
+ inputs:
+ command: 'restore'
+ projects: 'Discord.Net.sln'
+ feedsToUse: 'select'
+ verbosityRestore: 'Minimal'
displayName: Restore packages
- script: dotnet build "Discord.Net.sln" --no-restore -v minimal -c $(buildConfiguration) /p:BuildNumber=$(buildNumber) /p:IsTagBuild=$(buildTag)
diff --git a/azure/deploy.yml b/azure/deploy.yml
index 61994299e..4742da3c8 100644
--- a/azure/deploy.yml
+++ b/azure/deploy.yml
@@ -7,6 +7,7 @@ steps:
dotnet pack "src\Discord.Net.Webhook\Discord.Net.Webhook.csproj" --no-restore --no-build -v minimal -c $(buildConfiguration) -o "$(Build.ArtifactStagingDirectory)" /p:BuildNumber=$(buildNumber) /p:IsTagBuild=$(buildTag)
dotnet pack "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" --no-restore --no-build -v minimal -c $(buildConfiguration) -o "$(Build.ArtifactStagingDirectory)" /p:BuildNumber=$(buildNumber) /p:IsTagBuild=$(buildTag)
dotnet pack "src\Discord.Net.Analyzers\Discord.Net.Analyzers.csproj" --no-restore --no-build -v minimal -c $(buildConfiguration) -o "$(Build.ArtifactStagingDirectory)" /p:BuildNumber=$(buildNumber) /p:IsTagBuild=$(buildTag)
+ dotnet pack "src\Discord.Net.Interactions\Discord.Net.Interactions.csproj" --no-restore --no-build -v minimal -c $(buildConfiguration) -o "$(Build.ArtifactStagingDirectory)" /p:BuildNumber=$(buildNumber) /p:IsTagBuild=$(buildTag)
displayName: Pack projects
- task: NuGetCommand@2
diff --git a/docs/_overwrites/Commands/ICommandContext.Inclusion.md b/docs/_overwrites/Commands/ICommandContext.Inclusion.md
index 4c1257b23..a5eaeeab6 100644
--- a/docs/_overwrites/Commands/ICommandContext.Inclusion.md
+++ b/docs/_overwrites/Commands/ICommandContext.Inclusion.md
@@ -1,5 +1,5 @@
An example of how this class is used the command system can be seen
below:
-[!code[Sample module](../../guides/commands/samples/intro/empty-module.cs)]
-[!code[Command handler](../../guides/commands/samples/intro/command_handler.cs)]
\ No newline at end of file
+[!code[Sample module](../../guides/text_commands/samples/intro/empty-module.cs)]
+[!code[Command handler](../../guides/text_commands/samples/intro/command_handler.cs)]
diff --git a/docs/_overwrites/Common/EmbedBuilder.Overwrites.md b/docs/_overwrites/Common/EmbedBuilder.Overwrites.md
index 409a78e94..85c292dd2 100644
--- a/docs/_overwrites/Common/EmbedBuilder.Overwrites.md
+++ b/docs/_overwrites/Common/EmbedBuilder.Overwrites.md
@@ -28,7 +28,7 @@ public async Task SendRichEmbedAsync()
var embed = new EmbedBuilder
{
// Embed property can be set within object initializer
- Title = "Hello world!"
+ Title = "Hello world!",
Description = "I am a description set by initializer."
};
// Or with methods
diff --git a/docs/_overwrites/Common/EmbedObjectBuilder.Inclusion.md b/docs/_overwrites/Common/EmbedObjectBuilder.Inclusion.md
index eac0d9ca5..a9d3539ed 100644
--- a/docs/_overwrites/Common/EmbedObjectBuilder.Inclusion.md
+++ b/docs/_overwrites/Common/EmbedObjectBuilder.Inclusion.md
@@ -4,10 +4,10 @@ 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");
+ .WithIconUrl("https://discord.com/assets/e05ead6e6ebc08df9291738d0aa6986d.png");
var exampleFooter = new EmbedFooterBuilder()
.WithText("I am a nice footer")
- .WithIconUrl("https://discordapp.com/assets/28174a34e77bb5e5310ced9f95cb480b.png");
+ .WithIconUrl("https://discord.com/assets/28174a34e77bb5e5310ced9f95cb480b.png");
var exampleField = new EmbedFieldBuilder()
.WithName("Title of Another Field")
.WithValue("I am an [example](https://example.com).")
@@ -22,4 +22,4 @@ var embed = new EmbedBuilder()
.WithAuthor(exampleAuthor)
.WithFooter(exampleFooter)
.Build();
-```
\ No newline at end of file
+```
diff --git a/docs/_template/description-generator/plugins/DocFX.Plugin.DescriptionGenerator.dll b/docs/_template/description-generator/plugins/DocFX.Plugin.DescriptionGenerator.dll
index 095ea46ef..6e8839796 100644
Binary files a/docs/_template/description-generator/plugins/DocFX.Plugin.DescriptionGenerator.dll and b/docs/_template/description-generator/plugins/DocFX.Plugin.DescriptionGenerator.dll differ
diff --git a/docs/_template/description-generator/plugins/LICENSE b/docs/_template/description-generator/plugins/LICENSE
index eb92c0a03..d74703f3d 100644
--- a/docs/_template/description-generator/plugins/LICENSE
+++ b/docs/_template/description-generator/plugins/LICENSE
@@ -19,3 +19,11 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+
+==============================================================================
+
+Humanizer (https://github.com/Humanizr/Humanizer)
+The MIT License (MIT)
+Copyright (c) .NET Foundation and Contributors
+
+==============================================================================
\ No newline at end of file
diff --git a/docs/_template/last-modified/plugins/LastModifiedPostProcessor.dll b/docs/_template/last-modified/plugins/LastModifiedPostProcessor.dll
index dd790e55e..be36969c9 100644
Binary files a/docs/_template/last-modified/plugins/LastModifiedPostProcessor.dll and b/docs/_template/last-modified/plugins/LastModifiedPostProcessor.dll differ
diff --git a/docs/_template/last-modified/plugins/LibGit2Sharp.dll b/docs/_template/last-modified/plugins/LibGit2Sharp.dll
index 82f214846..5b3568103 100644
Binary files a/docs/_template/last-modified/plugins/LibGit2Sharp.dll and b/docs/_template/last-modified/plugins/LibGit2Sharp.dll differ
diff --git a/docs/_template/last-modified/plugins/LibGit2Sharp.dll.config b/docs/_template/last-modified/plugins/LibGit2Sharp.dll.config
index ddc821b29..c93e4cc22 100644
--- a/docs/_template/last-modified/plugins/LibGit2Sharp.dll.config
+++ b/docs/_template/last-modified/plugins/LibGit2Sharp.dll.config
@@ -1,4 +1,4 @@
-
-
-
+
+
+
diff --git a/docs/_template/last-modified/plugins/lib/alpine-x64/libgit2-ef5a385.so b/docs/_template/last-modified/plugins/lib/alpine-x64/libgit2-ef5a385.so
new file mode 100644
index 000000000..793f4483a
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/alpine-x64/libgit2-ef5a385.so differ
diff --git a/docs/_template/last-modified/plugins/lib/alpine.3.9-x64/libgit2-ef5a385.so b/docs/_template/last-modified/plugins/lib/alpine.3.9-x64/libgit2-ef5a385.so
new file mode 100644
index 000000000..49186df25
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/alpine.3.9-x64/libgit2-ef5a385.so differ
diff --git a/docs/_template/last-modified/plugins/lib/debian-arm64/libgit2-ef5a385.so b/docs/_template/last-modified/plugins/lib/debian-arm64/libgit2-ef5a385.so
new file mode 100644
index 000000000..11ef799ab
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/debian-arm64/libgit2-ef5a385.so differ
diff --git a/docs/_template/last-modified/plugins/lib/debian.9-x64/libgit2-ef5a385.so b/docs/_template/last-modified/plugins/lib/debian.9-x64/libgit2-ef5a385.so
new file mode 100644
index 000000000..5cd5e46b0
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/debian.9-x64/libgit2-ef5a385.so differ
diff --git a/docs/_template/last-modified/plugins/lib/fedora-x64/libgit2-ef5a385.so b/docs/_template/last-modified/plugins/lib/fedora-x64/libgit2-ef5a385.so
new file mode 100644
index 000000000..be1be9322
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/fedora-x64/libgit2-ef5a385.so differ
diff --git a/docs/_template/last-modified/plugins/lib/linux-arm/libgit2-6777db8.so b/docs/_template/last-modified/plugins/lib/linux-arm/libgit2-6777db8.so
new file mode 100644
index 000000000..4324c2ad9
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/linux-arm/libgit2-6777db8.so differ
diff --git a/docs/_template/last-modified/plugins/lib/linux-arm64/libgit2-6777db8.so b/docs/_template/last-modified/plugins/lib/linux-arm64/libgit2-6777db8.so
new file mode 100644
index 000000000..a72d7419d
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/linux-arm64/libgit2-6777db8.so differ
diff --git a/docs/_template/last-modified/plugins/lib/linux-musl-x64/libgit2-6777db8.so b/docs/_template/last-modified/plugins/lib/linux-musl-x64/libgit2-6777db8.so
new file mode 100644
index 000000000..a1f59dfb3
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/linux-musl-x64/libgit2-6777db8.so differ
diff --git a/docs/_template/last-modified/plugins/lib/linux-x64/libgit2-6777db8.so b/docs/_template/last-modified/plugins/lib/linux-x64/libgit2-6777db8.so
new file mode 100644
index 000000000..b47f83e21
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/linux-x64/libgit2-6777db8.so differ
diff --git a/docs/_template/last-modified/plugins/lib/linux-x64/libgit2-ef5a385.so b/docs/_template/last-modified/plugins/lib/linux-x64/libgit2-ef5a385.so
new file mode 100644
index 000000000..1ec4b01f5
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/linux-x64/libgit2-ef5a385.so differ
diff --git a/docs/_template/last-modified/plugins/lib/osx/libgit2-6777db8.dylib b/docs/_template/last-modified/plugins/lib/osx/libgit2-6777db8.dylib
new file mode 100644
index 000000000..cb1e7eb8c
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/osx/libgit2-6777db8.dylib differ
diff --git a/docs/_template/last-modified/plugins/lib/osx/libgit2-ef5a385.dylib b/docs/_template/last-modified/plugins/lib/osx/libgit2-ef5a385.dylib
new file mode 100644
index 000000000..81f71d6ea
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/osx/libgit2-ef5a385.dylib differ
diff --git a/docs/_template/last-modified/plugins/lib/rhel-x64/libgit2-ef5a385.so b/docs/_template/last-modified/plugins/lib/rhel-x64/libgit2-ef5a385.so
new file mode 100644
index 000000000..3d194a97f
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/rhel-x64/libgit2-ef5a385.so differ
diff --git a/docs/_template/last-modified/plugins/lib/ubuntu.16.04-arm64/libgit2-ef5a385.so b/docs/_template/last-modified/plugins/lib/ubuntu.16.04-arm64/libgit2-ef5a385.so
new file mode 100644
index 000000000..a3282b0d2
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/ubuntu.16.04-arm64/libgit2-ef5a385.so differ
diff --git a/docs/_template/last-modified/plugins/lib/ubuntu.18.04-x64/libgit2-ef5a385.so b/docs/_template/last-modified/plugins/lib/ubuntu.18.04-x64/libgit2-ef5a385.so
new file mode 100644
index 000000000..0360ce3e0
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/ubuntu.18.04-x64/libgit2-ef5a385.so differ
diff --git a/docs/_template/last-modified/plugins/lib/win32/x64/git2-6777db8.dll b/docs/_template/last-modified/plugins/lib/win32/x64/git2-6777db8.dll
new file mode 100644
index 000000000..af7d32a6d
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/win32/x64/git2-6777db8.dll differ
diff --git a/docs/_template/last-modified/plugins/lib/win32/x64/git2-ef5a385.dll b/docs/_template/last-modified/plugins/lib/win32/x64/git2-ef5a385.dll
new file mode 100644
index 000000000..7ffcdf97f
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/win32/x64/git2-ef5a385.dll differ
diff --git a/docs/_template/last-modified/plugins/lib/win32/x86/git2-6777db8.dll b/docs/_template/last-modified/plugins/lib/win32/x86/git2-6777db8.dll
new file mode 100644
index 000000000..c680911e2
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/win32/x86/git2-6777db8.dll differ
diff --git a/docs/_template/last-modified/plugins/lib/win32/x86/git2-ef5a385.dll b/docs/_template/last-modified/plugins/lib/win32/x86/git2-ef5a385.dll
new file mode 100644
index 000000000..9fe2e3e01
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/win32/x86/git2-ef5a385.dll differ
diff --git a/docs/_template/light-dark-theme/partials/affix.tmpl.partial b/docs/_template/light-dark-theme/partials/affix.tmpl.partial
index aafcdf05b..b3ce60b5c 100644
--- a/docs/_template/light-dark-theme/partials/affix.tmpl.partial
+++ b/docs/_template/light-dark-theme/partials/affix.tmpl.partial
@@ -27,7 +27,8 @@
{{/_disableContribution}}
diff --git a/docs/_template/light-dark-theme/partials/head.tmpl.partial b/docs/_template/light-dark-theme/partials/head.tmpl.partial
index c214e7548..8a01b48cf 100644
--- a/docs/_template/light-dark-theme/partials/head.tmpl.partial
+++ b/docs/_template/light-dark-theme/partials/head.tmpl.partial
@@ -10,7 +10,7 @@
-
+
diff --git a/docs/docfx.json b/docs/docfx.json
index 759dc174f..3fac4ff0b 100644
--- a/docs/docfx.json
+++ b/docs/docfx.json
@@ -1,19 +1,22 @@
{
- "metadata": [{
- "src": [{
- "src": "../src",
- "files": [
- "**.csproj"
- ]
- }],
- "dest": "api",
- "filter": "filterConfig.yml",
- "properties": {
- "TargetFramework": "netstandard2.0"
+ "metadata": [
+ {
+ "src": [
+ {
+ "src": "../src",
+ "files": ["**.csproj"]
+ }
+ ],
+ "dest": "api",
+ "filter": "filterConfig.yml",
+ "properties": {
+ "TargetFramework": "net5.0"
+ }
}
- }],
+ ],
"build": {
- "content": [{
+ "content": [
+ {
"files": ["api/**.yml", "api/index.md"]
},
{
@@ -27,19 +30,21 @@
},
{
"src": "../",
- "files": [ "CHANGELOG.md" ]
+ "files": ["CHANGELOG.md"]
+ }
+ ],
+ "resource": [
+ {
+ "files": [
+ "**/images/**",
+ "**/samples/**",
+ "langwordMapping.yml",
+ "marketing/logo/**.svg",
+ "marketing/logo/**.png",
+ "favicon.ico"
+ ]
}
],
- "resource": [{
- "files": [
- "**/images/**",
- "**/samples/**",
- "langwordMapping.yml",
- "marketing/logo/**.svg",
- "marketing/logo/**.png",
- "favicon.ico"
- ]
- }],
"dest": "_site",
"template": [
"default",
@@ -47,17 +52,19 @@
"_template/last-modified",
"_template/description-generator"
],
- "postProcessors": ["ExtractSearchIndex", "LastModifiedPostProcessor", "DescriptionPostProcessor"],
+ "postProcessors": [
+ "ExtractSearchIndex",
+ "LastModifiedPostProcessor",
+ "DescriptionPostProcessor"
+ ],
"overwrite": "_overwrites/**/**.md",
"globalMetadata": {
"_appTitle": "Discord.Net Documentation",
- "_appFooter": "Discord.Net (c) 2015-2020 2.2.0",
+ "_appFooter": "Discord.Net (c) 2015-2021 3.1.0",
"_enableSearch": true,
"_appLogoPath": "marketing/logo/SVG/Logomark Purple.svg",
- "_appFaviconPath": "favicon.ico"
+ "_appFaviconPath": "favicon.ico"
},
- "xrefService": [
- "https://xref.docs.microsoft.com/query?uid={uid}"
- ]
+ "xrefService": ["https://xref.docs.microsoft.com/query?uid={uid}"]
}
}
diff --git a/docs/faq/basics/basic-operations.md b/docs/faq/basics/basic-operations.md
index 35c71709f..d6121dbb0 100644
--- a/docs/faq/basics/basic-operations.md
+++ b/docs/faq/basics/basic-operations.md
@@ -13,7 +13,7 @@ language-specific tips when using this library.
> [!WARNING]
> Direct casting (e.g., `(Type)type`) is **the least recommended**
-> way of casting, as it *can* throw an [InvalidCastException]
+> 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.
@@ -26,7 +26,7 @@ A good and safe casting example:
[!code-csharp[Casting](samples/cast.cs)]
-[InvalidCastException]: https://docs.microsoft.com/en-us/dotnet/api/system.invalidcastexception
+[invalidcastexception]: https://docs.microsoft.com/en-us/dotnet/api/system.invalidcastexception
[this post]: https://docs.microsoft.com/en-us/dotnet/csharp/how-to/safely-cast-using-pattern-matching-is-and-as-operators
## How do I send a message?
@@ -45,15 +45,15 @@ 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*
+[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.Glossary#message-channels
+[glossary]: xref:FAQ.Glossary#message-channels
## How can I get the guild from a message?
@@ -86,9 +86,9 @@ implement [IEmote] and are valid options.
[!code-csharp[Emoji](samples/emoji-self.cs)]
-***
+---
-[AddReactionAsync]: xref:Discord.IMessage.AddReactionAsync*
+[addreactionasync]: xref:Discord.IMessage.AddReactionAsync*
## What is a "preemptive rate limit?"
@@ -107,7 +107,7 @@ reactions.
## Can I opt-out of preemptive rate limits?
-Unfortunately, not at the moment. See [#401](https://github.com/RogueException/Discord.Net/issues/401).
+Unfortunately, not at the moment. See [#401](https://github.com/discord-net/Discord.Net/issues/401).
[IChannel]: xref:Discord.IChannel
[ICategoryChannel]: xref:Discord.ICategoryChannel
@@ -120,4 +120,4 @@ Unfortunately, not at the moment. See [#401](https://github.com/RogueException/D
[IUserMessage]: xref:Discord.IUserMessage
[IEmote]: xref:Discord.IEmote
[Emote]: xref:Discord.Emote
-[Emoji]: xref:Discord.Emoji
\ No newline at end of file
+[Emoji]: xref:Discord.Emoji
diff --git a/docs/faq/basics/client-basics.md b/docs/faq/basics/client-basics.md
index 9377ac2e9..ade33c35d 100644
--- a/docs/faq/basics/client-basics.md
+++ b/docs/faq/basics/client-basics.md
@@ -9,6 +9,58 @@ In the following section, you will find commonly asked questions and
answers about common issues that you may face when utilizing the
various clients offered by the library.
+## I keep having trouble with intents!
+
+As Discord.NET has upgraded from Discord API v6 to API v9,
+`GatewayIntents` must now be specified in the socket config, as well as on the [developer portal].
+
+```cs
+
+// Where ever you declared your websocket client.
+DiscordSocketClient _client;
+
+...
+
+var config = new DiscordSocketConfig()
+{
+ .. // Other config options can be presented here.
+ GatewayIntents = GatewayIntents.All
+}
+
+_client = new DiscordSocketClient(config);
+
+```
+### Common intents:
+
+- AllUnprivileged: This is a group of most common intents, that do NOT require any [developer portal] intents to be enabled.
+This includes intents that receive messages such as: `GatewayIntents.GuildMessages, GatewayIntents.DirectMessages`
+- GuildMembers: An intent disabled by default, as you need to enable it in the [developer portal].
+- GuildPresences: Also disabled by default, this intent together with `GuildMembers` are the only intents not included in `AllUnprivileged`.
+- All: All intents, it is ill adviced to use this without care, as it *can* cause a memory leak from presence.
+The library will give responsive warnings if you specify unnecessary intents.
+
+
+> [!NOTE]
+> All gateway intents, their Discord API counterpart and their enum value are listed
+> [HERE](xref:Discord.GatewayIntents)
+
+### Stacking intents:
+
+It is common that you require several intents together.
+The example below shows how this can be done.
+
+```cs
+
+GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.GuildMembers | ..
+
+```
+
+> [!NOTE]
+> Further documentation on the ` | ` operator can be found
+> [HERE](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/bitwise-and-shift-operators)
+
+[developer portal]: https://discord.com/developers/
+
## My client keeps returning 401 upon logging in!
> [!WARNING]
@@ -28,9 +80,9 @@ There are few possible reasons why this may occur.
mind that a token is **different** from a *client secret*.
[TokenType]: xref:Discord.TokenType
-[827]: https://github.com/RogueException/Discord.Net/issues/827
-[958]: https://github.com/RogueException/Discord.Net/issues/958
-[Discord API Terms of Service]: https://discordapp.com/developers/docs/legal
+[827]: https://github.com/discord-net/Discord.Net/issues/827
+[958]: https://github.com/discord-net/Discord.Net/issues/958
+[Discord API Terms of Service]: https://discord.com/developers/docs/legal
## How do I do X, Y, Z when my bot connects/logs on? Why do I get a `NullReferenceException` upon calling any client methods after connect?
@@ -73,7 +125,9 @@ instances, with each one serving a different amount of guilds.
There are very few differences from the [DiscordSocketClient] class, and it is very straightforward
to modify your existing code to use a [DiscordShardedClient] when necessary.
-1. You need to specify the total amount of shards, or shard ids, via [DiscordShardedClient]'s constructors.
+1. You can specify the total amount of shards, or shard ids, via [DiscordShardedClient]'s constructors.
+If the total shards are not specified then the library will get the recommended shard count via the
+[Get Gateway Bot](https://discord.com/developers/docs/topics/gateway#get-gateway-bot) route.
2. The [Connected], [Disconnected], [Ready], and [LatencyUpdated] events
are replaced with [ShardConnected], [ShardDisconnected], [ShardReady], and [ShardLatencyUpdated].
3. Every event handler you apply/remove to the [DiscordShardedClient] is applied/removed to each shard.
diff --git a/docs/faq/basics/getting-started.md b/docs/faq/basics/getting-started.md
index f38d2cea7..dc4b11548 100644
--- a/docs/faq/basics/getting-started.md
+++ b/docs/faq/basics/getting-started.md
@@ -74,6 +74,9 @@ it will return the user ID of the aforementioned user.
Several common ways to do this:
-1. Make the role mentionable and mention the role, and escape it
+1. (Easiest) Right click on the role either in the Server Settings
+ or in the user's role list.
+ 
+2. 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.
\ No newline at end of file
+3. Inspect the roles collection within the guild via your debugger.
diff --git a/docs/faq/basics/images/role-copy.png b/docs/faq/basics/images/role-copy.png
new file mode 100644
index 000000000..1dbc2982f
Binary files /dev/null and b/docs/faq/basics/images/role-copy.png differ
diff --git a/docs/faq/basics/images/scope.png b/docs/faq/basics/images/scope.png
new file mode 100644
index 000000000..04d60bce2
Binary files /dev/null and b/docs/faq/basics/images/scope.png differ
diff --git a/docs/faq/basics/interactions.md b/docs/faq/basics/interactions.md
new file mode 100644
index 000000000..33b89ac2d
--- /dev/null
+++ b/docs/faq/basics/interactions.md
@@ -0,0 +1,88 @@
+---
+uid: FAQ.Basics.InteractionBasics
+title: Basics of interactions, common practice
+---
+
+# Interactions basics, where to get started
+
+This section answers basic questions and common mistakes in handling application commands, and responding to them.
+
+## What's the difference between RespondAsync, DeferAsync and FollowupAsync?
+
+The difference between these 3 functions is in how you handle the command response.
+[RespondAsync] and
+[DeferAsync] let the API know you have succesfully received the command. This is also called 'acknowledging' a command.
+DeferAsync will not send out a response, RespondAsync will.
+[FollowupAsync] follows up on succesful acknowledgement.
+
+> [!WARNING]
+> If you have not acknowledged the command FollowupAsync will not work! the interaction has not been resonded to, so you cannot follow it up!
+
+[RespondAsync]: xref:Discord.IDiscordInteraction
+[DeferAsync]: xref:Discord.IDiscordInteraction
+[FollowUpAsync]: xref:Discord.IDiscordInteraction
+
+## Im getting System.TimeoutException: 'Cannot respond to an interaction after 3 seconds!'
+
+This happens because your computers clock is out of sync or your trying to respond after 3 seconds. If your clock is out of sync and you cant fix it, you can set the `UseInteractionSnowflakeDate` to false in the config.
+
+## Bad form Exception when I try to create my commands, why do I get this?
+
+Bad form exceptions are thrown if the slash, user or message command builder has invalid values.
+The following options could resolve your error.
+
+#### Is your command name lowercase?
+
+If your command name is not lowercase, it is not seen as a valid command entry.
+`Avatar` is invalid; `avatar` is valid.
+
+#### Are your values below or above the required amount? (This also applies to message components)
+
+Discord expects all values to be below maximum allowed.
+Going over this maximum amount of characters causes an exception.
+
+> [!NOTE]
+> All maximum and minimum value requirements can be found in the [Discord Developer Docs].
+> For components, structure documentation is found [here].
+
+[Discord Developer Docs]: https://discord.com/developers/docs/interactions/application-commands#application-commands
+[here]: https://discord.com/developers/docs/interactions/message-components#message-components
+
+#### Is your subcommand branching correct?
+
+Branching structure is covered properly here: xref:Guides.SlashCommands.SubCommand
+
+## My interaction commands are not showing up?
+
+If you registered your commands globally, it can take up to 1 hour for them to register.
+Did you register a guild command (should be instant), or waited more than an hour and still don't have them show up?
+
+- Try to check for any errors in the console, there is a good chance something might have been thrown.
+
+- Register your commands after the Ready event in the client. The client is not configured to register commands before this moment.
+
+- Check if no bad form exception is thrown; If so, refer to the above question.
+
+- Do you have the application commands scope checked when adding your bot to guilds?
+
+
+
+## There are many options for creating commands, which do I use?
+
+[!code-csharp[Register examples](samples/registerint.cs)]
+
+> [!NOTE]
+> You can use bulkoverwrite even if there are no commands in guild, nor globally.
+> The bulkoverwrite method disposes the old set of commands and replaces it with the new.
+
+## Do I need to create commands on startup?
+
+If you are registering your commands for the first time, it is required to create them once.
+After this, commands will exist indefinitely until you overwrite them.
+Overwriting is only required if you make changes to existing commands, or add new ones.
+
+## I can't see all of my user/message commands, why?
+
+Message and user commands have a limit of 5 per guild, and another 5 globally.
+If you have more than 5 guild-only message commands being registered, no more than 5 will actually show up.
+You can get up to 10 entries to show if you register 5 per guild, and another 5 globally.
diff --git a/docs/faq/basics/samples/registerint.cs b/docs/faq/basics/samples/registerint.cs
new file mode 100644
index 000000000..68fd2e608
--- /dev/null
+++ b/docs/faq/basics/samples/registerint.cs
@@ -0,0 +1,21 @@
+private async Task ReadyAsync()
+{
+ // pull your commands from some array, everyone has a different approach for this.
+ var commands = _builders.ToArray();
+
+ // write your list of commands globally in one go.
+ await _client.Rest.BulkOverwriteGlobalCommands(commands);
+
+ // write your array of commands to one guild in one go.
+ // You can do a foreach (... in _client.Guilds) approach to write to all guilds.
+ await _client.Rest.BulkOverwriteGuildCommands(commands, /* some guild ID */);
+
+ foreach (var c in commands)
+ {
+ // Create a global command, repeating usage for multiple commands.
+ await _client.Rest.CreateGlobalCommand(c);
+
+ // Create a guild command, repeating usage for multiple commands.
+ await _client.Rest.CreateGuildCommand(c, guildId);
+ }
+}
diff --git a/docs/faq/commands/dependency-injection.md b/docs/faq/commands/dependency-injection.md
index 0a5de3e32..d6b7f8b58 100644
--- a/docs/faq/commands/dependency-injection.md
+++ b/docs/faq/commands/dependency-injection.md
@@ -23,7 +23,7 @@ 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).
+as well as how it works in [Discord.Net](xref:Guides.TextCommands.DI#usage-in-modules).
A brief example of service and dependency injection can be seen below.
diff --git a/docs/faq/commands/general.md b/docs/faq/commands/general.md
index de6d48dc1..cff078746 100644
--- a/docs/faq/commands/general.md
+++ b/docs/faq/commands/general.md
@@ -1,9 +1,9 @@
---
uid: FAQ.Commands.General
-title: General Questions about Commands
+title: General Questions about chat Commands
---
-# Command-related Questions
+# Chat Command-related Questions
In the following section, you will find commonly asked questions and
answered regarding general command usage when using @Discord.Commands.
@@ -144,4 +144,4 @@ For #4, exceptions are caught in [CommandService.Log] event under
[LogMessage.Exception]: xref:Discord.LogMessage.Exception*
[ExecuteResult.Exception]: xref:Discord.Commands.ExecuteResult.Exception*
[CommandException]: xref:Discord.Commands.CommandException
-[IResult]: xref:Discord.Commands.IResult
\ No newline at end of file
+[IResult]: xref:Discord.Commands.IResult
diff --git a/docs/faq/commands/interaction.md b/docs/faq/commands/interaction.md
new file mode 100644
index 000000000..db61bc3f5
--- /dev/null
+++ b/docs/faq/commands/interaction.md
@@ -0,0 +1,52 @@
+---
+uid: FAQ.Commands.Interactions
+title: Interaction service
+---
+
+# Interaction commands in services
+
+A chapter talking about the interaction service framework.
+For questions about interactions in general, refer to the [Interactions FAQ]
+
+## Module dependencies aren't getting populated by Property Injection?
+
+Make sure the properties are publicly accessible and publicly settable.
+
+## How do I use this * interaction specific method/property?
+
+If your interaction context holds a down-casted version of the interaction object, you need to up-cast it.
+Ideally, use pattern matching to make sure its the type of interaction you are expecting it to be.
+
+## `InteractionService.ExecuteAsync()` always returns a successful result, how do i access the failed command execution results?
+
+If you are using `RunMode.Async` you need to setup your post-execution pipeline around `CommandExecuted` events.
+
+## How do I check if the executing user has * permission?
+
+Refer to the [documentation about preconditions]
+
+## How do I send the HTTP Response from inside the command modules.
+
+Set the `RestResponseCallback` property of [InteractionServiceConfig] with a delegate for handling HTTP Responses and use
+`RestInteractionModuleBase` to create your command modules. `RespondAsync()` and `DeferAsync()` methods of this module base will use the
+`RestResponseCallback` to create interaction responses.
+
+## Is there a cleaner way of creating parameter choices other than using `[Choice]`?
+
+The default `enum` [TypeConverter] of the Interaction Service will
+automatically register `enum`s as multiple choice options.
+
+## How do I add an optional `enum` parameter but make the default value not visible to the user?
+
+The default `enum` [TypeConverter] of the Interaction Service comes with `[Hide]` attribute that
+can be used to prevent certain enum values from getting registered.
+
+## How does the InteractionService determine the generic TypeConverter to use for a parameter type?
+
+It compares the _target base type_ key of the
+[TypeConverter] and chooses the one that sits highest on the inheritance hierarchy.
+
+[TypeConverter]: xref:Discord.Interactions.TypeConverter
+[Interactions FAQ]: xref: FAQ.Basics.Interactions
+[InteractionServiceConfig]: xref:Discord.Interactions.InteractionServiceConfig
+[documentation about preconditions]: xref: Guides.ChatCommands.Preconditions
diff --git a/docs/faq/misc/glossary.md b/docs/faq/misc/glossary.md
index 95bdbaa03..232690917 100644
--- a/docs/faq/misc/glossary.md
+++ b/docs/faq/misc/glossary.md
@@ -19,18 +19,18 @@ channels, and are often referred to as "servers".
* 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 **Text Channel** ([ITextChannel]) is a message channel from a Guild.
+* A **Thread Channel** ([IThreadChannel]) is a thread channel from a Guild.
+* A **News Channel** ([INewsChannel]) (also goes as announcement channel) is a news 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.
+* 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.
@@ -39,11 +39,15 @@ Group.
* 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 **Stage Channel** ([IStageChannel]) is a stage channel in a guild.
* A **Category Channel** ([ICategoryChannel]) (2.0+) is a category that
holds one or more sub-channels.
* A **Nested Channel** ([INestedChannel]) (2.0+) is a channel that can
exist under a category.
+> [!NOTE]
+> A Channel ([IChannel]) can be all types of channels.
+
[INestedChannel]: xref:Discord.INestedChannel
[IGuildChannel]: xref:Discord.IGuildChannel
[IMessageChannel]: xref:Discord.IMessageChannel
@@ -53,6 +57,33 @@ exist under a category.
[IPrivateChannel]: xref:Discord.IPrivateChannel
[IVoiceChannel]: xref:Discord.IVoiceChannel
[ICategoryChannel]: xref:Discord.ICategoryChannel
+[IChannel]: xref:Discord.IChannel
+[IThreadChannel]: xref:Discord.IThreadChannel
+[IStageChannel]: xref:Discord.IStageChannel
+[INewsChannel]: xref:Discord.INewsChannel
+
+## Message Types
+
+* An **User Message** ([IUserMessage]) is a message sent by a user.
+* A **System Message** ([ISystemMessage]) is a message sent by Discord itself.
+* A **Message** ([IMessage]) can be any of the above.
+
+[IUserMessage]: xref:Discord.IUserMessage
+[ISystemMessage]: xref:Discord.ISystemMessage
+[IMessage]: xref:Discord.IMessage
+
+## User Types
+
+* A **Guild User** ([IGuildUser]) is a user available inside a guild.
+* A **Group User** ([IGroupUser]) is a user available inside a group.
+ - This is rarely used due to the bot's inability to join groups.
+* A **Self User** ([ISelfUser]) is the bot user the client is currently logged in as.
+* An **User** ([IUser]) can be any of the above.
+
+[IGuildUser]: xref:Discord.IGuildUser
+[IGroupUser]: xref:Discord.IGroupUser
+[ISelfUser]: xref:Discord.ISelfUser
+[IUser]: xref:Discord.IUser
## Emoji Types
@@ -64,6 +95,15 @@ exist under a category.
[Emote]: xref:Discord.Emote
[Emoji]: xref:Discord.Emoji
+
+## Sticker Types
+
+* A **Sticker** ([ISticker]) is a standard Discord sticker.
+* A **Custom Sticker ([ICustomSticker]) is a Guild-unique sticker.
+
+[ISticker]: xref:Discord.ISticker
+[ICustomSticker]: xref:Discord.ICustomSticker
+
## Activity Types
* A **Game** ([Game]) refers to a user's game activity.
@@ -79,4 +119,4 @@ activity for listening to a song on Spotify.
[RichGame]: xref:Discord.RichGame
[StreamingGame]: xref:Discord.StreamingGame
[SpotifyGame]: xref:Discord.SpotifyGame
-[Rich Presence Intro]: https://discordapp.com/developers/docs/rich-presence/best-practices
\ No newline at end of file
+[Rich Presence Intro]: https://discord.com/developers/docs/rich-presence/best-practices
diff --git a/docs/faq/misc/legacy.md b/docs/faq/misc/legacy.md
index 5931579d3..ddb2d03c6 100644
--- a/docs/faq/misc/legacy.md
+++ b/docs/faq/misc/legacy.md
@@ -18,7 +18,7 @@ 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
+[release tag]: https://github.com/discord-net/Discord.Net/releases
## I came from an earlier version of Discord.Net 1.0, and DependencyMap doesn't seem to exist anymore in the later revision? What happened to it?
@@ -26,4 +26,4 @@ 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
\ No newline at end of file
+[DependencyInjection]: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection
diff --git a/docs/faq/toc.yml b/docs/faq/toc.yml
index 393e948f6..5a7e7e7f2 100644
--- a/docs/faq/toc.yml
+++ b/docs/faq/toc.yml
@@ -6,10 +6,14 @@
topicUid: FAQ.Basics.BasicOp
- name: Client Basics
topicUid: FAQ.Basics.ClientBasics
+ - name: Interactions
+ topicUid: FAQ.Basics.InteractionBasics
- name: Commands
items:
- - name: General
+ - name: String commands
topicUid: FAQ.Commands.General
+ - name: Interaction commands
+ topicUid: FAQ.Commands.Interactions
- name: Dependency Injection
topicUid: FAQ.Commands.DI
- name: Glossary
diff --git a/docs/guides/concepts/entities.md b/docs/guides/concepts/entities.md
index 5ad5b01f2..e3ca7db32 100644
--- a/docs/guides/concepts/entities.md
+++ b/docs/guides/concepts/entities.md
@@ -31,8 +31,7 @@ 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`.
-You can find out various types of entities in the @FAQ.Misc.Glossary
-page.
+You can find out various types of entities in the [Glossary page.](xref:FAQ.Glossary)
## Navigation
@@ -63,4 +62,4 @@ a variant of the type that you need.
## Sample
-[!code-csharp[Entity Sample](samples/entities.cs)]
\ No newline at end of file
+[!code-csharp[Entity Sample](samples/entities.cs)]
diff --git a/docs/guides/concepts/ratelimits.md b/docs/guides/concepts/ratelimits.md
new file mode 100644
index 000000000..afeb5f795
--- /dev/null
+++ b/docs/guides/concepts/ratelimits.md
@@ -0,0 +1,49 @@
+# Ratelimits
+
+Ratelimits are a core concept of any API - Discords API is no exception. each verified library must follow the ratelimit guidelines.
+
+### Using the ratelimit callback
+
+There is a new property within `RequestOptions` called RatelimitCallback. This callback is called when a request is made via the rest api. The callback is called with a `IRateLimitInfo` parameter:
+
+| Name | Type | Description |
+| ---------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
+| IsGlobal | bool | Whether or not this ratelimit info is global. |
+| Limit | int? | The number of requests that can be made. |
+| Remaining | int? | The number of remaining requests that can be made. |
+| RetryAfter | int? | The total time (in seconds) of when the current rate limit bucket will reset. Can have decimals to match previous millisecond ratelimit precision. |
+| Reset | DateTimeOffset? | The time at which the rate limit resets. |
+| ResetAfter | TimeSpan? | The absolute time when this ratelimit resets. |
+| Bucket | string | A unique string denoting the rate limit being encountered (non-inclusive of major parameters in the route path). |
+| Lag | TimeSpan? | The amount of lag for the request. This is used to denote the precise time of when the ratelimit expires. |
+| Endpoint | string | The endpoint that this ratelimit info came from. |
+
+Let's set up a ratelimit callback that will print out the ratelimit info to the console.
+
+```cs
+public async Task MyRatelimitCallback(IRateLimitInfo info)
+{
+ Console.WriteLine($"{info.IsGlobal} {info.Limit} {info.Remaining} {info.RetryAfter} {info.Reset} {info.ResetAfter} {info.Bucket} {info.Lag} {info.Endpoint}");
+}
+```
+
+Let's use this callback in a send message function
+
+```cs
+[Command("ping")]
+public async Task ping()
+{
+ var options = new RequestOptions()
+ {
+ RatelimitCallback = MyRatelimitCallback
+ };
+
+ await Context.Channel.SendMessageAsync("Pong!", options: options);
+}
+```
+
+Running this produces the following output:
+
+```
+False 5 4 2021-09-09 3:48:14 AM +00:00 00:00:05 a06de0de4a08126315431cc0c55ee3dc 00:00:00.9891364 channels/848511736872828929/messages
+```
diff --git a/docs/guides/emoji/emoji.md b/docs/guides/emoji/emoji.md
index 60a84409c..dbf654bbf 100644
--- a/docs/guides/emoji/emoji.md
+++ b/docs/guides/emoji/emoji.md
@@ -46,14 +46,16 @@ form; this can be obtained in several different ways.
### Emoji Declaration
After obtaining the Unicode representation of the emoji, you may
-create the @Discord.Emoji object by passing the string into its
+create the @Discord.Emoji object by passing the string with unicode into its
constructor (e.g. `new Emoji("👌");` or `new Emoji("\uD83D\uDC4C");`).
Your method of declaring an @Discord.Emoji should look similar to
this:
-
[!code-csharp[Emoji Sample](samples/emoji-sample.cs)]
+Also you can use `Emoji.Parse()` or `Emoji.TryParse()` methods
+for parsing emojis from strings like `:heart:`, `<3` or `❤`.
+
[FileFormat.Info]: https://www.fileformat.info/info/emoji/list.htm
## Emote
@@ -97,4 +99,4 @@ this:
## Additional Information
To learn more about emote and emojis and how they could be used,
-see the documentation of @Discord.IEmote.
\ No newline at end of file
+see the documentation of @Discord.IEmote.
diff --git a/docs/guides/getting_started/first-bot.md b/docs/guides/getting_started/first-bot.md
index bdae80c7f..e1af20d30 100644
--- a/docs/guides/getting_started/first-bot.md
+++ b/docs/guides/getting_started/first-bot.md
@@ -31,7 +31,7 @@ the Discord Applications Portal first.

-[Discord Applications Portal]: https://discordapp.com/developers/applications/
+[Discord Applications Portal]: https://discord.com/developers/applications/
## Adding your bot to a server
@@ -80,15 +80,11 @@ recommended for these operations to be 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.
+in your console application.
[!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 allows us to create a connection
-to Discord later on without having to worry about setting up the
-correct async implementation.
+As a result of this, your program will now start into an async context.
> [!WARNING]
> If your application throws any exceptions within an async context,
@@ -165,11 +161,11 @@ or any other blocking method, such as reading from the console.
> the source code for your bot.
>
> In the following example, we retrieve the token from a pre-defined
-> variable, which is **NOT** secure, especially if you plan on
+> variable, which is **NOT** secure, especially if you plan on
> distributing the application in any shape or form.
>
-> We recommend alternative storage such as
-> [Environment Variables], an external configuration file, or a
+> We recommend alternative storage such as
+> [Environment Variables], an external configuration file, or a
> secrets manager for safe-handling of secrets.
>
> [Environment Variables]: https://en.wikipedia.org/wiki/Environment_variable
@@ -221,4 +217,4 @@ should be to separate...
2. the modules (handle commands)
3. the services (persistent storage, pure functions, data manipulation)
-[CommandService]: xref:Discord.Commands.CommandService
\ No newline at end of file
+[CommandService]: xref:Discord.Commands.CommandService
diff --git a/docs/guides/getting_started/images/nightlies-vs-note.png b/docs/guides/getting_started/images/nightlies-vs-note.png
deleted file mode 100644
index 0dcf2dea3..000000000
Binary files a/docs/guides/getting_started/images/nightlies-vs-note.png and /dev/null differ
diff --git a/docs/guides/getting_started/images/nightlies-vs-step1.png b/docs/guides/getting_started/images/nightlies-vs-step1.png
deleted file mode 100644
index a399ca66c..000000000
Binary files a/docs/guides/getting_started/images/nightlies-vs-step1.png and /dev/null differ
diff --git a/docs/guides/getting_started/images/nightlies-vs-step2.png b/docs/guides/getting_started/images/nightlies-vs-step2.png
deleted file mode 100644
index 75cecbb8d..000000000
Binary files a/docs/guides/getting_started/images/nightlies-vs-step2.png and /dev/null differ
diff --git a/docs/guides/getting_started/images/nightlies-vs-step4.png b/docs/guides/getting_started/images/nightlies-vs-step4.png
deleted file mode 100644
index 6462ab994..000000000
Binary files a/docs/guides/getting_started/images/nightlies-vs-step4.png and /dev/null differ
diff --git a/docs/guides/getting_started/installing.md b/docs/guides/getting_started/installing.md
index 61e3bb6ec..0dde72380 100644
--- a/docs/guides/getting_started/installing.md
+++ b/docs/guides/getting_started/installing.md
@@ -11,9 +11,9 @@ may also compile this library yourself should you so desire.
## Supported Platforms
-Discord.Net targets [.NET Standard] both 1.3 and 2.0; this also means
-that creating applications using the latest version of [.NET Core] is
-the most recommended. If you are bound by Windows-specific APIs or
+Discord.Net targets [.NET 6.0] and [.NET 5.0], but is also available on older versions, like [.NET Standard] and [.NET Core]; this still means
+that creating applications using the latest version of .NET (6.0)
+is most recommended. If you are bound by Windows-specific APIs or
other limitations, you may also consider targeting [.NET Framework]
4.6.1 or higher.
@@ -22,10 +22,12 @@ other limitations, you may also consider targeting [.NET Framework]
> 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/
-[.NET Framework]: https://docs.microsoft.com/en-us/dotnet/framework/get-started/
+[mono]: https://www.mono-project.com/
+[.net 6.0]: https://docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-6
+[.net 5.0]: https://docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-5
+[.net standard]: https://docs.microsoft.com/en-us/dotnet/articles/standard/library
+[.net core]: https://docs.microsoft.com/en-us/dotnet/articles/core/
+[.net framework]: https://docs.microsoft.com/en-us/dotnet/framework/get-started/
[additional steps]: #installing-on-net-standard-11
## Installing with NuGet
@@ -37,37 +39,37 @@ Development builds of Discord.Net, as well as add-ons, will be
published to our [MyGet feed]. See
@Guides.GettingStarted.Installation.Nightlies to learn more.
-[official NuGet feed]: https://nuget.org
-[MyGet feed]: https://www.myget.org/feed/Packages/discord-net
+[official nuget feed]: https://nuget.org
+[myget feed]: https://www.myget.org/feed/Packages/discord-net
### [Using Visual Studio](#tab/vs-install)
1. Create a new solution for your bot
2. In the Solution Explorer, find the "Dependencies" element under your
- bot's project
+ bot's project
3. Right click on "Dependencies", and select "Manage NuGet packages"
- 
+ 
4. In the "Browse" tab, search for `Discord.Net`
5. Install the `Discord.Net` package
- 
+ 
### [Using JetBrains Rider](#tab/rider-install)
1. Create a new solution for your bot
2. Open the NuGet window (Tools > NuGet > Manage NuGet packages for Solution)
- 
+ 
3. In the "Packages" tab, search for `Discord.Net`
- 
+ 
4. Install by adding the package to your project
- 
+ 
### [Using Visual Studio Code](#tab/vs-code)
@@ -82,7 +84,7 @@ published to our [MyGet feed]. See
2. Navigate to where your `*.csproj` is located
3. Enter `dotnet add package Discord.Net`
-***
+---
## Compiling from Source
@@ -90,15 +92,15 @@ In order to compile Discord.Net, you will need the following:
### Using Visual Studio
-* [Visual Studio 2019](https://visualstudio.microsoft.com/)
-* [.NET Core SDK]
+- [Visual Studio 2019](https://visualstudio.microsoft.com/) or later.
+- [.NET 5 SDK]
-The .NET Core and Docker workload is required during Visual Studio
+The .NET 5 workload is required during Visual Studio
installation.
### Using Command Line
-* [.NET Core SDK]
+* [.NET 5 SDK]
## Additional Information
@@ -117,28 +119,28 @@ by installing one or more custom packages as listed below.
1. Download the latest [.NET Core SDK].
2. Create or move your existing project to use .NET Core.
3. Modify your `` tag to at least `netcoreapp2.1`, or
- by adding the `--framework netcoreapp2.1` switch when building.
+ by adding the `--framework netcoreapp2.1` switch when building.
#### [Custom Packages](#tab/custom-pkg)
-1. Install or compile the following packages:
+1. Install or compile the following packages:
- * `Discord.Net.Providers.WS4Net`
- * `Discord.Net.Providers.UDPClient` (Optional)
- * This is _only_ required if your bot will be utilizing voice chat.
+ - `Discord.Net.Providers.WS4Net`
+ - `Discord.Net.Providers.UDPClient` (Optional)
+ - This is _only_ required if your bot will be utilizing voice chat.
-2. Configure your [DiscordSocketClient] to use these custom providers
-over the default ones.
+2. Configure your [DiscordSocketClient] to use these custom providers
+ over the default ones.
- * To do this, set the `WebSocketProvider` and the optional
- `UdpSocketProvider` properties on the [DiscordSocketConfig] that you
- are passing into your client.
+ * To do this, set the `WebSocketProvider` and the optional
+ `UdpSocketProvider` properties on the [DiscordSocketConfig] that you
+ are passing into your client.
[!code-csharp[Example](samples/netstd11.cs)]
-[DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient
-[DiscordSocketConfig]: xref:Discord.WebSocket.DiscordSocketConfig
+[discordsocketclient]: xref:Discord.WebSocket.DiscordSocketClient
+[discordsocketconfig]: xref:Discord.WebSocket.DiscordSocketConfig
-***
+---
-[.NET Core SDK]: https://dotnet.microsoft.com/download
\ No newline at end of file
+[.NET 5 SDK]: https://dotnet.microsoft.com/download
diff --git a/docs/guides/getting_started/labs.md b/docs/guides/getting_started/labs.md
new file mode 100644
index 000000000..e52014312
--- /dev/null
+++ b/docs/guides/getting_started/labs.md
@@ -0,0 +1,30 @@
+---
+uid: Guides.GettingStarted.Installation.Labs
+title: Installing Labs builds
+---
+
+# Installing Discord.NET Labs
+
+Discord.NET Labs is the experimental repository that introduces new features & chips away at all bugs until ready for merging into Discord.NET.
+Are you looking to test or play with new features?
+
+> [!IMPORTANT]
+> It is very ill advised to use Discord.NET Labs in a production environment normally,
+> considering it can include bugs that have not been discovered yet, as features are freshly added.
+> However if approached correctly, will work as a pre-release to Discord.NET.
+> Make sure to report any bugs at the Labs [repository] or on [Discord]
+
+[Discord]: https://discord.gg/dnet
+[repository]: https://github.com/Discord-Net-Labs/Discord.Net-Labs
+
+## Installation:
+
+[NuGet] - This only includes releases, on which features are ready to test.
+
+> [!NOTE]
+> Installing NuGet packages is covered fully at [Installing Discord NET](xref:Guides.GettingStarted.Installation)
+
+[MyGet] - Available for current builds and unreleased features.
+
+[NuGet]: https://www.nuget.org/packages/Discord.Net.Labs/
+[MyGet]: https://www.myget.org/feed/Packages/discord-net-labs
diff --git a/docs/guides/getting_started/nightlies.md b/docs/guides/getting_started/nightlies.md
deleted file mode 100644
index 2b9fde87b..000000000
--- a/docs/guides/getting_started/nightlies.md
+++ /dev/null
@@ -1,97 +0,0 @@
----
-uid: Guides.GettingStarted.Installation.Nightlies
-title: Installing Nightly Build
----
-
-# Installing Discord.Net Nightly Build
-
-Before Discord.Net pushes a new set of features into the stable
-version, we use nightly builds to test the features with the
-community for an extensive period of time. Each nightly build is
-compiled by AppVeyor whenever a new commit is made and will be pushed
-to our MyGet feed.
-
-> [!IMPORTANT]
-> Although nightlies are generally stable and have more features
-> and bug fixes than the current stable build on NuGet, there
-> will be breaking changes during the development or
-> breaking bugs; these bugs are usually fixed as soon as they
-> are discovered, but you should still be aware of that.
-
-## Installing with MyGet (Recommended)
-
-MyGet is typically used by many development teams to publish their
-latest pre-release packages before the features are finalized and
-pushed to NuGet.
-
-The following is the feed link of Discord.Net,
-
-* `https://www.myget.org/F/discord-net/api/v3/index.json`
-
-Depending on which IDE you use, there are many different ways of
-adding the feed to your package source.
-
-### [Using Visual Studio](#tab/vs)
-
-1. Go to `Tools` > `NuGet Package Manager` > `Package Manager Settings`
-
- 
-
-2. Go to `Package Sources`
-
- 
-
-3. Click on the add icon
-4. Fill in the desired name and source as shown below and hit `Update`
-
- 
-
-> [!NOTE]
-> Remember to tick the `Include pre-release` checkbox to see the
-> nightly builds!
-> 
-
-### [Using dotnet CLI](#tab/cli)
-
-1. Launch a terminal of your choice
-2. Navigate to where your `*.csproj` is located
-3. Type `dotnet add package Discord.Net --source https://www.myget.org/F/discord-net/api/v3/index.json`
-
-### [Using Local NuGet.Config](#tab/local-nuget-config)
-
-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 is located.
-
-Paste the following snippets into this configuration file, adding any
-additional feeds if necessary.
-
-[!code[NuGet Configuration](samples/nuget.config)]
-
-After which, you may install the packages by directly modifying the
-project file and specifying a version, or by using
-the [Package Manager Console](https://docs.microsoft.com/en-us/nuget/tools/powershell-reference)
-(`Install-Package Discord.Net -IncludePrerelease`).
-
-***
-
-## Installing from AppVeyor Artifacts
-
-As mentioned in the first paragraph, we utilize AppVeyor to perform
-automated tests and publish the new build. During the publishing
-process, we also upload the NuGet packages onto
-AppVeyor's Artifact collection.
-
-The latest build status can be found within our [AppVeyor project].
-
-[AppVeyor project]: https://ci.appveyor.com/project/rogueexception/discord-net
-
-1. In the project, you may find our latest build including the
- aforementioned artifacts.
- 
-2. In the artifacts collection, you should see the latest packages
- packed in `*.nupkg` form which you could download from and use.
- 
diff --git a/docs/guides/getting_started/samples/first-bot/async-context.cs b/docs/guides/getting_started/samples/first-bot/async-context.cs
index 3c98c9e46..98a3cea15 100644
--- a/docs/guides/getting_started/samples/first-bot/async-context.cs
+++ b/docs/guides/getting_started/samples/first-bot/async-context.cs
@@ -1,7 +1,6 @@
public class Program
{
- public static void Main(string[] args)
- => new Program().MainAsync().GetAwaiter().GetResult();
+ public static Task Main(string[] args) => new Program().MainAsync();
public async Task MainAsync()
{
diff --git a/docs/guides/getting_started/samples/first-bot/complete.cs b/docs/guides/getting_started/samples/first-bot/complete.cs
index 871641e23..542056435 100644
--- a/docs/guides/getting_started/samples/first-bot/complete.cs
+++ b/docs/guides/getting_started/samples/first-bot/complete.cs
@@ -2,8 +2,7 @@ public class Program
{
private DiscordSocketClient _client;
- public static void Main(string[] args)
- => new Program().MainAsync().GetAwaiter().GetResult();
+ public static Task Main(string[] args) => new Program().MainAsync();
public async Task MainAsync()
{
diff --git a/docs/guides/getting_started/samples/first-bot/structure.cs b/docs/guides/getting_started/samples/first-bot/structure.cs
index 5165e2fdb..4e64b1732 100644
--- a/docs/guides/getting_started/samples/first-bot/structure.cs
+++ b/docs/guides/getting_started/samples/first-bot/structure.cs
@@ -10,11 +10,11 @@ using Discord.WebSocket;
class Program
{
// Program entry point
- static void Main(string[] args)
+ static Task Main(string[] args)
{
// Call the Program constructor, followed by the
// MainAsync method and wait until it finishes (which should be never).
- new Program().MainAsync().GetAwaiter().GetResult();
+ return new Program().MainAsync();
}
private readonly DiscordSocketClient _client;
diff --git a/docs/guides/guild_events/creating-guild-events.md b/docs/guides/guild_events/creating-guild-events.md
new file mode 100644
index 000000000..64ac0de9b
--- /dev/null
+++ b/docs/guides/guild_events/creating-guild-events.md
@@ -0,0 +1,31 @@
+---
+uid: Guides.GuildEvents.Creating
+title: Creating Guild Events
+---
+
+# Creating guild events
+
+You can create new guild events by using the `CreateEventAsync` function on a guild.
+
+### Parameters
+
+| Name | Type | Summary |
+| ------------- | --------------------------------- | ---------------------------------------------------------------------------- |
+| name | `string` | Sets the name of the event. |
+| startTime | `DateTimeOffset` | Sets the start time of the event. |
+| type | `GuildScheduledEventType` | Sets the type of the event. |
+| privacyLevel? | `GuildScheduledEventPrivacyLevel` | Sets the privacy level of the event |
+| description? | `string` | Sets the description of the event. |
+| endTime? | `DateTimeOffset?` | Sets the end time of the event. |
+| channelId? | `ulong?` | Sets the channel id of the event, only valid on stage or voice channel types |
+| location? | `string` | Sets the location of the event, only valid on external types |
+
+Lets create a basic test event.
+
+```cs
+var guild = client.GetGuild(guildId);
+
+var guildEvent = await guild.CreateEventAsync("test event", DateTimeOffset.UtcNow.AddDays(1), GuildScheduledEventType.External, endTime: DateTimeOffset.UtcNow.AddDays(2), location: "Space");
+```
+
+This code will create an event that lasts a day and starts tomorrow. It will be an external event thats in space.
diff --git a/docs/guides/guild_events/getting-event-users.md b/docs/guides/guild_events/getting-event-users.md
new file mode 100644
index 000000000..f4b5388a0
--- /dev/null
+++ b/docs/guides/guild_events/getting-event-users.md
@@ -0,0 +1,16 @@
+---
+uid: Guides.GuildEvents.GettingUsers
+title: Getting Guild Event Users
+---
+
+# Getting Event Users
+
+You can get a collection of users who are currently interested in the event by calling `GetUsersAsync`. This method works like any other get users method as in it returns an async enumerable. This method also supports pagination by user id.
+
+```cs
+// get all users and flatten the result into one collection.
+var users = await event.GetUsersAsync().FlattenAsync();
+
+// get users around the 613425648685547541 id.
+var aroundUsers = await event.GetUsersAsync(613425648685547541, Direction.Around).FlattenAsync();
+```
diff --git a/docs/guides/guild_events/intro.md b/docs/guides/guild_events/intro.md
new file mode 100644
index 000000000..b60a8c70d
--- /dev/null
+++ b/docs/guides/guild_events/intro.md
@@ -0,0 +1,41 @@
+---
+uid: Guides.GuildEvents.Intro
+title: Introduction to Guild Events
+---
+
+# Guild Events
+
+Guild events are a way to host events within a guild. They offer alot of features and flexibility.
+
+## Getting started with guild events
+
+You can access any events within a guild by calling `GetEventsAsync` on a guild.
+
+```cs
+var guildEvents = await guild.GetEventsAsync();
+```
+
+If your working with socket guilds you can just use the `Events` property:
+
+```cs
+var guildEvents = guild.Events;
+```
+
+There are also new gateway events that you can hook to receive guild scheduled events on.
+
+```cs
+// Fired when a guild event is cancelled.
+client.GuildScheduledEventCancelled += ...
+
+// Fired when a guild event is completed.
+client.GuildScheduledEventCompleted += ...
+
+// Fired when a guild event is started.
+client.GuildScheduledEventStarted += ...
+
+// Fired when a guild event is created.
+client.GuildScheduledEventCreated += ...
+
+// Fired when a guild event is updated.
+client.GuildScheduledEventUpdated += ...
+```
diff --git a/docs/guides/guild_events/modifying-events.md b/docs/guides/guild_events/modifying-events.md
new file mode 100644
index 000000000..05e14ec98
--- /dev/null
+++ b/docs/guides/guild_events/modifying-events.md
@@ -0,0 +1,23 @@
+---
+uid: Guides.GuildEvents.Modifying
+title: Modifying Guild Events
+---
+
+# Modifying Events
+
+You can modify events using the `ModifyAsync` method to modify the event, heres the properties you can modify:
+
+| Name | Type | Description |
+| ------------ | --------------------------------- | -------------------------------------------- |
+| ChannelId | `ulong?` | Gets or sets the channel id of the event. |
+| string | `string` | Gets or sets the location of this event. |
+| Name | `string` | Gets or sets the name of the event. |
+| PrivacyLevel | `GuildScheduledEventPrivacyLevel` | Gets or sets the privacy level of the event. |
+| StartTime | `DateTimeOffset` | Gets or sets the start time of the event. |
+| EndTime | `DateTimeOffset` | Gets or sets the end time of the event. |
+| Description | `string` | Gets or sets the description of the event. |
+| Type | `GuildScheduledEventType` | Gets or sets the type of the event. |
+| Status | `GuildScheduledEventStatus` | Gets or sets the status of the event. |
+
+> [!NOTE]
+> All of these properties are optional.
diff --git a/docs/guides/int_basics/application-commands/context-menu-commands/creating-context-menu-commands.md b/docs/guides/int_basics/application-commands/context-menu-commands/creating-context-menu-commands.md
new file mode 100644
index 000000000..4e6210ba8
--- /dev/null
+++ b/docs/guides/int_basics/application-commands/context-menu-commands/creating-context-menu-commands.md
@@ -0,0 +1,110 @@
+---
+uid: Guides.ContextCommands.Creating
+title: Creating Context Commands
+---
+
+# Creating context menu commands.
+
+There are two kinds of Context Menu Commands: User Commands and Message Commands.
+Each of these have a Global and Guild variant.
+Global menu commands are available for every guild that adds your app. An individual app's global commands are also available in DMs if that app has a bot that shares a mutual guild with the user.
+
+Guild commands are specific to the guild you specify when making them. Guild commands are not available in DMs. Command names are unique per application within each scope (global and guild). That means:
+
+- Your app cannot have two global commands with the same name
+- Your app cannot have two guild commands within the same name on the same guild
+- Your app can have a global and guild command with the same name
+- Multiple apps can have commands with the same names
+
+[!IMPORTANT]
+> Apps can have a maximum of 5 global context menu commands,
+> and an additional 5 guild-specific context menu commands per guild.
+
+## UserCommandBuilder
+
+The context menu user command builder will help you create user commands. The builder has these available fields and methods:
+
+| Name | Type | Description |
+| -------- | -------- | ------------------------------------------------------------------------------------------------ |
+| Name | string | The name of this context menu command. |
+| WithName | Function | Sets the field name. |
+| Build | Function | Builds the builder into the appropriate `UserCommandProperties` class used to make Menu commands |
+
+## MessageCommandBuilder
+
+The context menu message command builder will help you create message commands. The builder has these available fields and methods:
+
+| Name | Type | Description |
+| -------- | -------- | --------------------------------------------------------------------------------------------------- |
+| Name | string | The name of this context menu command. |
+| WithName | Function | Sets the field name. |
+| Build | Function | Builds the builder into the appropriate `MessageCommandProperties` class used to make Menu commands |
+
+> [!NOTE]
+> Context Menu command names can be upper and lowercase, and use spaces.
+> They cannot be registered pre-ready.
+
+Let's use the user command builder to make a global and guild command.
+
+```cs
+// Let's hook the ready event for creating our commands in.
+client.Ready += Client_Ready;
+
+...
+
+public async Task Client_Ready()
+{
+ // Let's build a guild command! We're going to need a guild so lets just put that in a variable.
+ var guild = client.GetGuild(guildId);
+
+ // Next, lets create our user and message command builder. This is like the embed builder but for context menu commands.
+ var guildUserCommand = new UserCommandBuilder();
+ var guildMessageCommand = new MessageCommandBuilder();
+
+ // Note: Names have to be all lowercase and match the regular expression ^[\w -]{3,32}$
+ guildUserCommand.WithName("Guild User Command");
+ guildMessageCommand.WithName("Guild Message Command");
+
+ // Descriptions are not used with User and Message commands
+ //guildCommand.WithDescription("");
+
+ // Let's do our global commands
+ var globalUserCommand = new UserCommandBuilder();
+ globalCommand.WithName("Global User Command");
+ var globalMessageCommand = new MessageCommandBuilder();
+ globalMessageCommand.WithName("Global Message Command");
+
+
+ try
+ {
+ // Now that we have our builder, we can call the BulkOverwriteApplicationCommandAsync to make our context commands. Note: this will overwrite all your previous commands with this array.
+ await guild.BulkOverwriteApplicationCommandAsync(new ApplicationCommandProperties[]
+ {
+ guildUserCommand.Build(),
+ guildMessageCommand.Build()
+ });
+
+ // With global commands we dont need the guild.
+ await client.BulkOverwriteGlobalApplicationCommandsAsync(new ApplicationCommandProperties[]
+ {
+ globalUserCommand.Build(),
+ globalMessageCommand.Build()
+ })
+ }
+ catch(ApplicationCommandException exception)
+ {
+ // If our command was invalid, we should catch an ApplicationCommandException. This exception contains the path of the error as well as the error message. You can serialize the Error field in the exception to get a visual of where your error is.
+ var json = JsonConvert.SerializeObject(exception.Error, Formatting.Indented);
+
+ // You can send this error somewhere or just print it to the console, for this example we're just going to print it.
+ Console.WriteLine(json);
+ }
+}
+
+```
+
+> [!NOTE]
+> Application commands only need to be created once. They do _not_ have to be
+> 'created' on every startup or connection.
+> The example simple shows creating them in the ready event
+> as it's simpler than creating normal bot commands to register application commands.
diff --git a/docs/guides/int_basics/application-commands/context-menu-commands/receiving-context-menu-command-events.md b/docs/guides/int_basics/application-commands/context-menu-commands/receiving-context-menu-command-events.md
new file mode 100644
index 000000000..d4e973d04
--- /dev/null
+++ b/docs/guides/int_basics/application-commands/context-menu-commands/receiving-context-menu-command-events.md
@@ -0,0 +1,33 @@
+---
+uid: Guides.ContextCommands.Reveiving
+title: Receiving Context Commands
+---
+
+# Receiving Context Menu events
+
+User commands and Message commands have their own unique event just like the other interaction types. For user commands the event is `UserCommandExecuted` and for message commands the event is `MessageCommandExecuted`.
+
+```cs
+// For message commands
+client.MessageCommandExecuted += MessageCommandHandler;
+
+// For user commands
+client.UserCommandExecuted += UserCommandHandler;
+
+...
+
+public async Task MessageCommandHandler(SocketMessageCommand arg)
+{
+ Console.Writeline("Message command received!");
+}
+
+public async Task UserCommandHandler(SocketUserCommand arg)
+{
+ Console.Writeline("User command received!");
+}
+```
+
+User commands contain a SocketUser object called `Member` in their data class, showing the user that was clicked to run the command.
+Message commands contain a SocketMessage object called `Message` in their data class, showing the message that was clicked to run the command.
+
+Both return the user who ran the command, the guild (if any), channel, etc.
\ No newline at end of file
diff --git a/docs/guides/int_basics/application-commands/intro.md b/docs/guides/int_basics/application-commands/intro.md
new file mode 100644
index 000000000..f55d0a2fc
--- /dev/null
+++ b/docs/guides/int_basics/application-commands/intro.md
@@ -0,0 +1,51 @@
+---
+uid: Guides.SlashCommands.Intro
+title: Introduction to slash commands
+---
+
+
+# Getting started with application commands.
+
+This guide will show you how to use application commands.
+If you have extra questions that aren't covered here you can come to our
+[Discord](https://discord.com/invite/dvSfUTet3K) server and ask around there.
+
+## What is an application command?
+
+Application commands consist of three different types. Slash commands, context menu User commands and context menu Message commands.
+Slash commands are made up of a name, description, and a block of options, which you can think of like arguments to a function.
+The name and description help users find your command among many others, and the options validate user input as they fill out your command.
+Message and User commands are only a name, to the user. So try to make the name descriptive.
+They're accessed by right clicking (or long press, on mobile) a user or a message, respectively.
+
+> [!IMPORTANT]
+> Context menu commands are currently not supported on mobile.
+
+All three varieties of application commands have both Global and Guild variants.
+Your global commands are available in every guild that adds your application.
+You can also make commands for a specific guild; they're only available in that guild.
+The User and Message commands are more limited in quantity than the slash commands.
+For specifics, check out their respective guide pages.
+
+An Interaction is the message that your application receives when a user uses a command.
+It includes the values that the user submitted, as well as some metadata about this particular instance of the command being used:
+the guild_id,
+channel_id,
+member and other fields.
+You can find all the values in our data models.
+
+## Authorizing your bot for application commands
+
+There is a new special OAuth2 scope for applications called `applications.commands`.
+In order to make Application Commands work within a guild, the guild must authorize your application
+with the `applications.commands` scope. The bot scope is not enough.
+
+Head over to your discord applications OAuth2 screen and make sure to select the `application.commands` scope.
+
+
+
+From there you can then use the link to add your bot to a server.
+
+> [!NOTE]
+> In order for users in your guild to use your slash commands, they need to have
+> the "Use Application Commands" permission on the guild.
diff --git a/docs/guides/int_basics/application-commands/slash-commands/bulk-overwrite-of-global-slash-commands.md b/docs/guides/int_basics/application-commands/slash-commands/bulk-overwrite-of-global-slash-commands.md
new file mode 100644
index 000000000..6a64b9c68
--- /dev/null
+++ b/docs/guides/int_basics/application-commands/slash-commands/bulk-overwrite-of-global-slash-commands.md
@@ -0,0 +1,41 @@
+---
+uid: Guides.SlashCommands.BulkOverwrite
+title: Slash Command Bulk Overwrites
+---
+
+If you have too many global commands then you might want to consider using the bulk overwrite function.
+
+```cs
+public async Task Client_Ready()
+{
+ List applicationCommandProperties = new();
+ try
+ {
+ // Simple help slash command.
+ SlashCommandBuilder globalCommandHelp = new SlashCommandBuilder();
+ globalCommandHelp.WithName("help");
+ globalCommandHelp.WithDescription("Shows information about the bot.");
+ applicationCommandProperties.Add(globalCommandHelp.Build());
+
+ // Slash command with name as its parameter.
+ SlashCommandOptionBuilder slashCommandOptionBuilder = new();
+ slashCommandOptionBuilder.WithName("name");
+ slashCommandOptionBuilder.WithType(ApplicationCommandOptionType.String);
+ slashCommandOptionBuilder.WithDescription("Add a family");
+ slashCommandOptionBuilder.WithRequired(true); // Only add this if you want it to be required
+
+ SlashCommandBuilder globalCommandAddFamily = new SlashCommandBuilder();
+ globalCommandAddFamily.WithName("add-family");
+ globalCommandAddFamily.WithDescription("Add a family");
+ globalCommandAddFamily.AddOptions(slashCommandOptionBuilder);
+ applicationCommandProperties.Add(globalCommandAddFamily.Build());
+
+ await _client.BulkOverwriteGlobalApplicationCommandsAsync(applicationCommandProperties.ToArray());
+ }
+ catch (ApplicationCommandException exception)
+ {
+ var json = JsonConvert.SerializeObject(exception.Error, Formatting.Indented);
+ Console.WriteLine(json);
+ }
+}
+```
diff --git a/docs/guides/int_basics/application-commands/slash-commands/choice-slash-command.md b/docs/guides/int_basics/application-commands/slash-commands/choice-slash-command.md
new file mode 100644
index 000000000..3951e1141
--- /dev/null
+++ b/docs/guides/int_basics/application-commands/slash-commands/choice-slash-command.md
@@ -0,0 +1,85 @@
+---
+uid: Guides.SlashCommands.Choices
+title: Slash Command Choices
+---
+
+# Slash Command Choices.
+
+With slash command options you can add choices, making the user select between some set values. Lets create a command that asks how much they like our bot!
+
+Let's set up our slash command:
+
+```cs
+private async Task Client_Ready()
+{
+ ulong guildId = 848176216011046962;
+
+ var guildCommand = new SlashCommandBuilder()
+ .WithName("feedback")
+ .WithDescription("Tell us how much you are enjoying this bot!")
+ .AddOption(new SlashCommandOptionBuilder()
+ .WithName("rating")
+ .WithDescription("The rating your willing to give our bot")
+ .WithRequired(true)
+ .AddChoice("Terrible", 1)
+ .AddChoice("Meh", 2)
+ .AddChoice("Good", 3)
+ .AddChoice("Lovely", 4)
+ .AddChoice("Excellent!", 5)
+ .WithType(ApplicationCommandOptionType.Integer)
+ ).Build();
+
+ try
+ {
+ await client.Rest.CreateGuildCommand(guildCommand.Build(), guildId);
+ }
+ catch(ApplicationCommandException exception)
+ {
+ var json = JsonConvert.SerializeObject(exception.Error, Formatting.Indented);
+ Console.WriteLine(json);
+ }
+}
+```
+> [!NOTE]
+> Your `ApplicationCommandOptionType` specifies which type your choices are, you need to use `ApplicationCommandOptionType.Integer` for choices whos values are whole numbers, `ApplicationCommandOptionType.Number` for choices whos values are doubles, and `ApplicationCommandOptionType.String` for string values.
+
+We have defined 5 choices for the user to pick from, each choice has a value assigned to it. The value can either be a string or an int. In our case we're going to use an int. This is what the command looks like:
+
+
+
+Lets add our code for handling the interaction.
+
+```cs
+private async Task SlashCommandHandler(SocketSlashCommand command)
+{
+ // Let's add a switch statement for the command name so we can handle multiple commands in one event.
+ switch(command.Data.Name)
+ {
+ case "list-roles":
+ await HandleListRoleCommand(command);
+ break;
+ case "settings":
+ await HandleSettingsCommand(command);
+ break;
+ case "feedback":
+ await HandleFeedbackCommand(command);
+ break;
+ }
+}
+
+private async Task HandleFeedbackCommand(SocketSlashCommand command)
+{
+ var embedBuilder = new EmbedBuilder()
+ .WithAuthor(command.User)
+ .WithTitle("Feedback")
+ .WithDescription($"Thanks for your feedback! You rated us {command.Data.Options.First().Value}/5")
+ .WithColor(Color.Green)
+ .WithCurrentTimestamp();
+
+ await command.RespondAsync(embed: embedBuilder.Build());
+}
+```
+
+And this is the result:
+
+
diff --git a/docs/guides/int_basics/application-commands/slash-commands/creating-slash-commands.md b/docs/guides/int_basics/application-commands/slash-commands/creating-slash-commands.md
new file mode 100644
index 000000000..9e35de285
--- /dev/null
+++ b/docs/guides/int_basics/application-commands/slash-commands/creating-slash-commands.md
@@ -0,0 +1,98 @@
+---
+uid: Guides.SlashCommands.Creating
+title: Creating Slash Commands
+---
+
+# Creating your first slash commands.
+
+There are two kinds of Slash Commands: global commands and guild commands.
+Global commands are available for every guild that adds your app. An individual app's global commands are also available in DMs if that app has a bot that shares a mutual guild with the user.
+
+Guild commands are specific to the guild you specify when making them. Guild commands are not available in DMs. Command names are unique per application within each scope (global and guild). That means:
+
+- Your app cannot have two global commands with the same name
+- Your app cannot have two guild commands within the same name on the same guild
+- Your app can have a global and guild command with the same name
+- Multiple apps can have commands with the same names
+
+**Note**: Apps can have a maximum of 100 global commands, and an additional 100 guild-specific commands per guild.
+
+**Note**: Global commands will take up to 1 hour to create, delete or modify on guilds. If you need to update a command quickly for testing you can create it as a guild command.
+
+If you don't have the code for a bot ready yet please follow [this guide](https://docs.stillu.cc/guides/getting_started/first-bot.html).
+
+## SlashCommandBuilder
+
+The slash command builder will help you create slash commands. The builder has these available fields and methods:
+
+| Name | Type | Description |
+| --------------------- | -------------------------------- | -------------------------------------------------------------------------------------------- |
+| MaxNameLength | const int | The maximum length of a name for a slash command allowed by Discord. |
+| MaxDescriptionLength | const int | The maximum length of a commands description allowed by Discord. |
+| MaxOptionsCount | const int | The maximum count of command options allowed by Discord |
+| Name | string | The name of this slash command. |
+| Description | string | A 1-100 length description of this slash command |
+| Options | List\ | The options for this command. |
+| DefaultPermission | bool | Whether the command is enabled by default when the app is added to a guild. |
+| WithName | Function | Sets the field name. |
+| WithDescription | Function | Sets the description of the current command. |
+| WithDefaultPermission | Function | Sets the default permission of the current command. |
+| AddOption | Function | Adds an option to the current slash command. |
+| Build | Function | Builds the builder into a `SlashCommandCreationProperties` class used to make slash commands |
+
+> [!NOTE]
+> Slash command names must be all lowercase!
+
+## Creating a Slash Command
+
+Let's use the slash command builder to make a global and guild command.
+
+```cs
+// Let's hook the ready event for creating our commands in.
+client.Ready += Client_Ready;
+
+...
+
+public async Task Client_Ready()
+{
+ // Let's build a guild command! We're going to need a guild so lets just put that in a variable.
+ var guild = client.GetGuild(guildId);
+
+ // Next, lets create our slash command builder. This is like the embed builder but for slash commands.
+ var guildCommand = new SlashCommandBuilder();
+
+ // Note: Names have to be all lowercase and match the regular expression ^[\w-]{3,32}$
+ guildCommand.WithName("first-command");
+
+ // Descriptions can have a max length of 100.
+ guildCommand.WithDescription("This is my first guild slash command!");
+
+ // Let's do our global command
+ var globalCommand = new SlashCommandBuilder();
+ globalCommand.WithName("first-global-command");
+ globalCommand.WithDescription("This is my frist global slash command");
+
+ try
+ {
+ // Now that we have our builder, we can call the CreateApplicationCommandAsync method to make our slash command.
+ await guild.CreateApplicationCommandAsync(guildCommand.Build());
+
+ // With global commands we dont need the guild.
+ await client.CreateGlobalApplicationCommandAsync(globalCommand.Build());
+ // Using the ready event is a simple implementation for the sake of the example. Suitable for testing and development.
+ // For a production bot, it is recommended to only run the CreateGlobalApplicationCommandAsync() once for each command.
+ }
+ catch(ApplicationCommandException exception)
+ {
+ // If our command was invalid, we should catch an ApplicationCommandException. This exception contains the path of the error as well as the error message. You can serialize the Error field in the exception to get a visual of where your error is.
+ var json = JsonConvert.SerializeObject(exception.Error, Formatting.Indented);
+
+ // You can send this error somewhere or just print it to the console, for this example we're just going to print it.
+ Console.WriteLine(json);
+ }
+}
+
+```
+
+> [!NOTE]
+> Slash commands only need to be created once. They do _not_ have to be 'created' on every startup or connection. The example simple shows creating them in the ready event as it's simpler than creating normal bot commands to register slash commands. The global commands take up to an hour to register every time the CreateGlobalApplicationCommandAsync() is called for a given command.
diff --git a/docs/guides/int_basics/application-commands/slash-commands/images/ephemeral1.png b/docs/guides/int_basics/application-commands/slash-commands/images/ephemeral1.png
new file mode 100644
index 000000000..61eab94b6
Binary files /dev/null and b/docs/guides/int_basics/application-commands/slash-commands/images/ephemeral1.png differ
diff --git a/docs/guides/int_basics/application-commands/slash-commands/images/feedback1.png b/docs/guides/int_basics/application-commands/slash-commands/images/feedback1.png
new file mode 100644
index 000000000..08e5b8c21
Binary files /dev/null and b/docs/guides/int_basics/application-commands/slash-commands/images/feedback1.png differ
diff --git a/docs/guides/int_basics/application-commands/slash-commands/images/feedback2.png b/docs/guides/int_basics/application-commands/slash-commands/images/feedback2.png
new file mode 100644
index 000000000..3e75c87db
Binary files /dev/null and b/docs/guides/int_basics/application-commands/slash-commands/images/feedback2.png differ
diff --git a/docs/guides/int_basics/application-commands/slash-commands/images/listroles1.png b/docs/guides/int_basics/application-commands/slash-commands/images/listroles1.png
new file mode 100644
index 000000000..43015e203
Binary files /dev/null and b/docs/guides/int_basics/application-commands/slash-commands/images/listroles1.png differ
diff --git a/docs/guides/int_basics/application-commands/slash-commands/images/listroles2.png b/docs/guides/int_basics/application-commands/slash-commands/images/listroles2.png
new file mode 100644
index 000000000..d0b954380
Binary files /dev/null and b/docs/guides/int_basics/application-commands/slash-commands/images/listroles2.png differ
diff --git a/docs/guides/int_basics/application-commands/slash-commands/images/oauth.png b/docs/guides/int_basics/application-commands/slash-commands/images/oauth.png
new file mode 100644
index 000000000..e0f8224a8
Binary files /dev/null and b/docs/guides/int_basics/application-commands/slash-commands/images/oauth.png differ
diff --git a/docs/guides/int_basics/application-commands/slash-commands/images/settings1.png b/docs/guides/int_basics/application-commands/slash-commands/images/settings1.png
new file mode 100644
index 000000000..0eb4d711a
Binary files /dev/null and b/docs/guides/int_basics/application-commands/slash-commands/images/settings1.png differ
diff --git a/docs/guides/int_basics/application-commands/slash-commands/images/settings2.png b/docs/guides/int_basics/application-commands/slash-commands/images/settings2.png
new file mode 100644
index 000000000..5ced63134
Binary files /dev/null and b/docs/guides/int_basics/application-commands/slash-commands/images/settings2.png differ
diff --git a/docs/guides/int_basics/application-commands/slash-commands/images/settings3.png b/docs/guides/int_basics/application-commands/slash-commands/images/settings3.png
new file mode 100644
index 000000000..485110814
Binary files /dev/null and b/docs/guides/int_basics/application-commands/slash-commands/images/settings3.png differ
diff --git a/docs/guides/int_basics/application-commands/slash-commands/images/slashcommand1.png b/docs/guides/int_basics/application-commands/slash-commands/images/slashcommand1.png
new file mode 100644
index 000000000..0c4e0aec7
Binary files /dev/null and b/docs/guides/int_basics/application-commands/slash-commands/images/slashcommand1.png differ
diff --git a/docs/guides/int_basics/application-commands/slash-commands/images/slashcommand2.png b/docs/guides/int_basics/application-commands/slash-commands/images/slashcommand2.png
new file mode 100644
index 000000000..828d8a2ce
Binary files /dev/null and b/docs/guides/int_basics/application-commands/slash-commands/images/slashcommand2.png differ
diff --git a/docs/guides/int_basics/application-commands/slash-commands/parameters.md b/docs/guides/int_basics/application-commands/slash-commands/parameters.md
new file mode 100644
index 000000000..6afd83729
--- /dev/null
+++ b/docs/guides/int_basics/application-commands/slash-commands/parameters.md
@@ -0,0 +1,102 @@
+---
+uid: Guides.SlashCommands.Parameters
+title: Slash Command Parameters
+---
+
+# Slash command parameters
+
+Slash commands can have a bunch of parameters, each their own type. Let's first go over the types of parameters we can have.
+
+| Name | Description |
+| --------------- | -------------------------------------------------- |
+| SubCommand | A subcommand inside of a subcommand group. |
+| SubCommandGroup | The parent command group of subcommands. |
+| String | A string of text. |
+| Integer | A number. |
+| Boolean | True or False. |
+| User | A user |
+| Channel | A channel, this includes voice text and categories |
+| Role | A role. |
+| Mentionable | A role or a user. |
+
+Each one of the parameter types has its own DNET type in the `SocketSlashCommandDataOption`'s Value field:
+| Name | C# Type |
+| --------------- | ------------------------------------------------ |
+| SubCommand | NA |
+| SubCommandGroup | NA |
+| String | `string` |
+| Integer | `int` |
+| Boolean | `bool` |
+| User | `SocketGuildUser` or `SocketUser` |
+| Role | `SocketRole` |
+| Channel | `SocketChannel` |
+| Mentionable | `SocketUser`, `SocketGuildUser`, or `SocketRole` |
+
+Let's start by making a command that takes in a user and lists their roles.
+
+```cs
+client.Ready += Client_Ready;
+
+...
+
+public async Task Client_Ready()
+{
+ ulong guildId = 848176216011046962;
+
+ var guildCommand = new SlashCommandBuilder()
+ .WithName("list-roles")
+ .WithDescription("Lists all roles of a user.")
+ .AddOption("user", ApplicationCommandOptionType.User, "The users whos roles you want to be listed", isRequired: true);
+
+ try
+ {
+ await client.Rest.CreateGuildCommand(guildCommand.Build(), guildId);
+ }
+ catch(ApplicationCommandException exception)
+ {
+ var json = JsonConvert.SerializeObject(exception.Error, Formatting.Indented);
+ Console.WriteLine(json);
+ }
+}
+
+```
+
+
+
+That seems to be working, now Let's handle the interaction.
+
+```cs
+private async Task SlashCommandHandler(SocketSlashCommand command)
+{
+ // Let's add a switch statement for the command name so we can handle multiple commands in one event.
+ switch(command.Data.Name)
+ {
+ case "list-roles":
+ await HandleListRoleCommand(command);
+ break;
+ }
+}
+
+private async Task HandleListRoleCommand(SocketSlashCommand command)
+{
+ // We need to extract the user parameter from the command. since we only have one option and it's required, we can just use the first option.
+ var guildUser = (SocketGuildUser)command.Data.Options.First().Value;
+
+ // We remove the everyone role and select the mention of each role.
+ var roleList = string.Join(",\n", guildUser.Roles.Where(x => !x.IsEveryone).Select(x => x.Mention));
+
+ var embedBuiler = new EmbedBuilder()
+ .WithAuthor(guildUser.ToString(), guildUser.GetAvatarUrl() ?? guildUser.GetDefaultAvatarUrl())
+ .WithTitle("Roles")
+ .WithDescription(roleList)
+ .WithColor(Color.Green)
+ .WithCurrentTimestamp();
+
+ // Now, Let's respond with the embed.
+ await command.RespondAsync(embed: embedBuiler.Build());
+}
+```
+
+
+
+That has worked! Next, we will go over responding ephemerally.
diff --git a/docs/guides/int_basics/application-commands/slash-commands/responding-ephemerally.md b/docs/guides/int_basics/application-commands/slash-commands/responding-ephemerally.md
new file mode 100644
index 000000000..10b04a8d2
--- /dev/null
+++ b/docs/guides/int_basics/application-commands/slash-commands/responding-ephemerally.md
@@ -0,0 +1,23 @@
+---
+uid: Guides.SlashCommands.Ephemeral
+title: Ephemeral Responses
+---
+
+# Responding ephemerally
+
+What is an ephemeral response? Basically, only the user who executed the command can see the result of it, this is pretty simple to implement.
+
+> [!NOTE]
+> You don't have to run arg.DeferAsync() to capture the interaction, you can use arg.RespondAsync() with a message to capture it, this also follows the ephemeral rule.
+
+When responding with either `FollowupAsync` or `RespondAsync` you can pass in an `ephemeral` property. When setting it to true it will respond ephemerally, false and it will respond non-ephemerally.
+
+Let's use this in our list role command.
+
+```cs
+await command.RespondAsync(embed: embedBuiler.Build(), ephemeral: true);
+```
+
+Running the command now only shows the message to us!
+
+
\ No newline at end of file
diff --git a/docs/guides/int_basics/application-commands/slash-commands/responding-to-slash-commands.md b/docs/guides/int_basics/application-commands/slash-commands/responding-to-slash-commands.md
new file mode 100644
index 000000000..3dbc579fe
--- /dev/null
+++ b/docs/guides/int_basics/application-commands/slash-commands/responding-to-slash-commands.md
@@ -0,0 +1,40 @@
+---
+uid: Guides.SlashCommands.Receiving
+title: Receiving and Responding to Slash Commands
+---
+
+# Responding to interactions.
+
+Interactions are the base thing sent over by Discord. Slash commands are one of the interaction types. We can listen to the `SlashCommandExecuted` event to respond to them. Lets add this to our code:
+
+```cs
+client.SlashCommandExecuted += SlashCommandHandler;
+
+...
+
+private async Task SlashCommandHandler(SocketSlashCommand command)
+{
+
+}
+```
+
+With every type of interaction there is a `Data` field. This is where the relevant information lives about our command that was executed. In our case, `Data` is a `SocketSlashCommandData` instance. In the data class, we can access the name of the command triggered as well as the options if there were any. For this example, we're just going to respond with the name of the command executed.
+
+```cs
+private async Task SlashCommandHandler(SocketSlashCommand command)
+{
+ await command.RespondAsync($"You executed {command.Data.Name}");
+}
+```
+
+Let's try this out!
+
+
+
+
+
+> [!NOTE]
+> After receiving an interaction, you must respond to acknowledge it. You can choose to respond with a message immediately using `RespondAsync()` or you can choose to send a deferred response with `DeferAsync()`.
+> If choosing a deferred response, the user will see a loading state for the interaction, and you'll have up to 15 minutes to edit the original deferred response using `ModifyOriginalResponseAsync()`. You can read more about response types [here](https://discord.com/developers/docs/interactions/slash-commands#interaction-response)
+
+This seems to be working! Next, we will look at parameters for slash commands.
diff --git a/docs/guides/int_basics/application-commands/slash-commands/subcommands.md b/docs/guides/int_basics/application-commands/slash-commands/subcommands.md
new file mode 100644
index 000000000..83d7b283c
--- /dev/null
+++ b/docs/guides/int_basics/application-commands/slash-commands/subcommands.md
@@ -0,0 +1,219 @@
+---
+uid: Guides.SlashCommands.SubCommand
+title: Sub Commands
+---
+
+# Subcommands
+
+Subcommands allow you to have multiple commands available in a single command. They can be useful for representing sub options for a command. For example: A settings command. Let's first look at some limitations with subcommands set by discord.
+
+- An app can have up to 25 subcommand groups on a top-level command
+- An app can have up to 25 subcommands within a subcommand group
+- commands can have up to 25 `options`
+- options can have up to 25 `choices`
+
+```
+VALID
+
+command
+|
+|__ subcommand
+|
+|__ subcommand
+
+----
+
+command
+|
+|__ subcommand-group
+ |
+ |__ subcommand
+|
+|__ subcommand-group
+ |
+ |__ subcommand
+
+
+-------
+
+INVALID
+
+
+command
+|
+|__ subcommand-group
+ |
+ |__ subcommand-group
+|
+|__ subcommand-group
+ |
+ |__ subcommand-group
+
+----
+
+INVALID
+
+command
+|
+|__ subcommand
+ |
+ |__ subcommand-group
+|
+|__ subcommand
+ |
+ |__ subcommand-group
+```
+
+Let's write a settings command that can change 3 fields in our bot.
+
+```cs
+public string FieldA { get; set; } = "test";
+public int FieldB { get; set; } = 10;
+public bool FieldC { get; set; } = true;
+
+public async Task Client_Ready()
+{
+ ulong guildId = 848176216011046962;
+
+ var guildCommand = new SlashCommandBuilder()
+ .WithName("settings")
+ .WithDescription("Changes some settings within the bot.")
+ .AddOption(new SlashCommandOptionBuilder()
+ .WithName("field-a")
+ .WithDescription("Gets or sets the field A")
+ .WithType(ApplicationCommandOptionType.SubCommandGroup)
+ .AddOption(new SlashCommandOptionBuilder()
+ .WithName("set")
+ .WithDescription("Sets the field A")
+ .WithType(ApplicationCommandOptionType.SubCommand)
+ .AddOption("value", ApplicationCommandOptionType.String, "the value to set the field", isRequired: true)
+ ).AddOption(new SlashCommandOptionBuilder()
+ .WithName("get")
+ .WithDescription("Gets the value of field A.")
+ .WithType(ApplicationCommandOptionType.SubCommand)
+ )
+ ).AddOption(new SlashCommandOptionBuilder()
+ .WithName("field-b")
+ .WithDescription("Gets or sets the field B")
+ .WithType(ApplicationCommandOptionType.SubCommandGroup)
+ .AddOption(new SlashCommandOptionBuilder()
+ .WithName("set")
+ .WithDescription("Sets the field B")
+ .WithType(ApplicationCommandOptionType.SubCommand)
+ .AddOption("value", ApplicationCommandOptionType.Integer, "the value to set the fie to.", isRequired: true)
+ ).AddOption(new SlashCommandOptionBuilder()
+ .WithName("get")
+ .WithDescription("Gets the value of field B.")
+ .WithType(ApplicationCommandOptionType.SubCommand)
+ )
+ ).AddOption(new SlashCommandOptionBuilder()
+ .WithName("field-c")
+ .WithDescription("Gets or sets the field C")
+ .WithType(ApplicationCommandOptionType.SubCommandGroup)
+ .AddOption(new SlashCommandOptionBuilder()
+ .WithName("set")
+ .WithDescription("Sets the field C")
+ .WithType(ApplicationCommandOptionType.SubCommand)
+ .AddOption("value", ApplicationCommandOptionType.Boolean, "the value to set the fie to.", isRequired: true)
+ ).AddOption(new SlashCommandOptionBuilder()
+ .WithName("get")
+ .WithDescription("Gets the value of field C.")
+ .WithType(ApplicationCommandOptionType.SubCommand)
+ )
+ );
+
+ try
+ {
+ await client.Rest.CreateGuildCommand(guildCommand.Build(), guildId);
+ }
+ catch(ApplicationCommandException exception)
+ {
+ var json = JsonConvert.SerializeObject(exception.Error, Formatting.Indented);
+ Console.WriteLine(json);
+ }
+}
+```
+
+All that code generates a command that looks like this:
+
+
+Now that we have our command made, we need to handle the multiple options with this command. So lets add this into our handler:
+
+```cs
+private async Task SlashCommandHandler(SocketSlashCommand command)
+{
+ // Let's add a switch statement for the command name so we can handle multiple commands in one event.
+ switch(command.Data.Name)
+ {
+ case "list-roles":
+ await HandleListRoleCommand(command);
+ break;
+ case "settings":
+ await HandleSettingsCommand(command);
+ break;
+ }
+}
+
+private async Task HandleSettingsCommand(SocketSlashCommand command)
+{
+ // First lets extract our variables
+ var fieldName = command.Data.Options.First().Name;
+ var getOrSet = command.Data.Options.First().Options.First().Name;
+ // Since there is no value on a get command, we use the ? operator because "Options" can be null.
+ var value = command.Data.Options.First().Options.First().Options?.FirstOrDefault().Value;
+
+ switch (fieldName)
+ {
+ case "field-a":
+ {
+ if(getOrSet == "get")
+ {
+ await command.RespondAsync($"The value of `field-a` is `{FieldA}`");
+ }
+ else if (getOrSet == "set")
+ {
+ this.FieldA = (string)value;
+ await command.RespondAsync($"`field-a` has been set to `{FieldA}`");
+ }
+ }
+ break;
+ case "field-b":
+ {
+ if (getOrSet == "get")
+ {
+ await command.RespondAsync($"The value of `field-b` is `{FieldB}`");
+ }
+ else if (getOrSet == "set")
+ {
+ this.FieldB = (int)value;
+ await command.RespondAsync($"`field-b` has been set to `{FieldB}`");
+ }
+ }
+ break;
+ case "field-c":
+ {
+ if (getOrSet == "get")
+ {
+ await command.RespondAsync($"The value of `field-c` is `{FieldC}`");
+ }
+ else if (getOrSet == "set")
+ {
+ this.FieldC = (bool)value;
+ await command.RespondAsync($"`field-c` has been set to `{FieldC}`");
+ }
+ }
+ break;
+ }
+}
+
+```
+
+Now, let's try this out! Running the 3 get commands seems to get the default values we set.
+
+
+
+Now let's try changing each to a different value.
+
+
+
+That has worked! Next, let't look at choices in commands.
diff --git a/docs/guides/int_basics/intro.md b/docs/guides/int_basics/intro.md
new file mode 100644
index 000000000..62b2dfdb5
--- /dev/null
+++ b/docs/guides/int_basics/intro.md
@@ -0,0 +1,10 @@
+---
+uid: Guides.Interactions.Intro
+title: Introduction to Interactions
+---
+
+# Interactions
+
+Placeholder text does the brrr.
+
+Links to different sections of guides: msg comp / slash commands.
diff --git a/docs/guides/int_basics/message-components/advanced.md b/docs/guides/int_basics/message-components/advanced.md
new file mode 100644
index 000000000..49b3f31a6
--- /dev/null
+++ b/docs/guides/int_basics/message-components/advanced.md
@@ -0,0 +1,87 @@
+---
+uid: Guides.MessageComponents.Advanced
+title: Advanced Concepts
+---
+
+# Advanced
+
+Lets say you have some components on an ephemeral slash command, and you want to modify the message that the button is on. The issue with this is that ephemeral messages are not stored and can not be get via rest or other means.
+
+Luckily, Discord thought of this and introduced a way to modify them with interactions.
+
+### Using the UpdateAsync method
+
+Components come with an `UpdateAsync` method that can update the message that the component was on. You can use it like a `ModifyAsync` method.
+
+Lets use it with a command, we first create our command, in this example im just going to use a message command:
+
+```cs
+var command = new MessageCommandBuilder()
+ .WithName("testing").Build();
+
+await client.GetGuild(guildId).BulkOverwriteApplicationCommandAsync(new [] { command, buttonCommand });
+```
+
+Next, we listen for this command, and respond with some components when its used:
+
+```cs
+var menu = new SelectMenuBuilder()
+{
+ CustomId = "select-1",
+ Placeholder = "Select Somthing!",
+ MaxValues = 1,
+ MinValues = 1,
+};
+
+menu.AddOption("Meh", "1", "Its not gaming.")
+ .AddOption("Ish", "2", "Some would say that this is gaming.")
+ .AddOption("Moderate", "3", "It could pass as gaming")
+ .AddOption("Confirmed", "4", "We are gaming")
+ .AddOption("Excellent", "5", "It is renowned as gaming nation wide", new Emoji("🔥"));
+
+var components = new ComponentBuilder()
+ .WithSelectMenu(menu);
+
+
+await arg.RespondAsync("On a scale of one to five, how gaming is this?", component: componBuild(), ephemeral: true);
+break;
+```
+
+Now, let's listen to the select menu executed event and add a case for `select-1`
+
+```cs
+client.SelectMenuExecuted += SelectMenuHandler;
+
+...
+
+public async Task SelectMenuHandler(SocketMessageComponent arg)
+{
+ switch (arg.Data.CustomId)
+ {
+ case "select-1":
+ var value = arg.Data.Values.First();
+ var menu = new SelectMenuBuilder()
+ {
+ CustomId = "select-1",
+ Placeholder = $"{(arg.Message.Components.First().Components.First() as SelectMenu).Options.FirstOrDefault(x => x.Value == value).Label}",
+ MaxValues = 1,
+ MinValues = 1,
+ Disabled = true
+ };
+
+ menu.AddOption("Meh", "1", "Its not gaming.")
+ .AddOption("Ish", "2", "Some would say that this is gaming.")
+ .AddOption("Moderate", "3", "It could pass as gaming")
+ .AddOption("Confirmed", "4", "We are gaming")
+ .AddOption("Excellent", "5", "It is renowned as gaming nation wide", new Emoji("🔥"));
+
+ // We use UpdateAsync to update the message and its original content and components.
+ await arg.UpdateAsync(x =>
+ {
+ x.Content = $"Thank you {arg.User.Mention} for rating us {value}/5 on the gaming scale";
+ x.Components = new ComponentBuilder().WithSelectMenu(menu).Build();
+ });
+ break;
+ }
+}
+```
diff --git a/docs/guides/int_basics/message-components/buttons-in-depth.md b/docs/guides/int_basics/message-components/buttons-in-depth.md
new file mode 100644
index 000000000..5da48c69b
--- /dev/null
+++ b/docs/guides/int_basics/message-components/buttons-in-depth.md
@@ -0,0 +1,45 @@
+---
+uid: Guides.MessageComponents.Buttons
+title: Buttons in Depth
+---
+
+# Buttons in depth
+
+There are many changes you can make to buttons, lets take a look at the parameters in the `WithButton` function"
+| Name | Type | Description |
+|----------|---------------|----------------------------------------------------------------|
+| label | `string` | The label text for the button. |
+| customId | `string` | The custom id of the button. |
+| style | `ButtonStyle` | The style of the button. |
+| emote | `IEmote` | A IEmote to be used with this button. |
+| url | `string` | A URL to be used only if the `ButtonStyle` is a Link. |
+| disabled | `bool` | Whether or not the button is disabled. |
+| row | `int` | The row to place the button if it has enough room, otherwise 0 |
+
+### Label
+
+This is the front facing text that the user sees. The maximum length is 80 characters.
+
+### CustomId
+
+This is the property sent to you by discord when a button is clicked. It is not required for link buttons as they do not emit an event. The maximum length is 100 characters.
+
+### Style
+
+Styling your buttons are important for indicating different actions:
+
+
+
+You can do this by using the `ButtonStyle` which has all the styles defined.
+
+### Emote
+
+You can specify an `IEmote` when creating buttons to add them to your button. They have the same restrictions as putting guild based emotes in messages.
+
+### Url
+
+If you use the link style with your button you can specify a url. When this button is clicked the user is taken to that url.
+
+### Disabled
+
+You can specify if your button is disabled, meaning users won't be able to click on it.
diff --git a/docs/guides/int_basics/message-components/images/image1.png b/docs/guides/int_basics/message-components/images/image1.png
new file mode 100644
index 000000000..a161d8a61
Binary files /dev/null and b/docs/guides/int_basics/message-components/images/image1.png differ
diff --git a/docs/guides/int_basics/message-components/images/image2.png b/docs/guides/int_basics/message-components/images/image2.png
new file mode 100644
index 000000000..9303de91b
Binary files /dev/null and b/docs/guides/int_basics/message-components/images/image2.png differ
diff --git a/docs/guides/int_basics/message-components/images/image3.png b/docs/guides/int_basics/message-components/images/image3.png
new file mode 100644
index 000000000..7480e1da9
Binary files /dev/null and b/docs/guides/int_basics/message-components/images/image3.png differ
diff --git a/docs/guides/int_basics/message-components/images/image4.png b/docs/guides/int_basics/message-components/images/image4.png
new file mode 100644
index 000000000..c54ab791f
Binary files /dev/null and b/docs/guides/int_basics/message-components/images/image4.png differ
diff --git a/docs/guides/int_basics/message-components/images/image5.png b/docs/guides/int_basics/message-components/images/image5.png
new file mode 100644
index 000000000..096b7587f
Binary files /dev/null and b/docs/guides/int_basics/message-components/images/image5.png differ
diff --git a/docs/guides/int_basics/message-components/images/image6.png b/docs/guides/int_basics/message-components/images/image6.png
new file mode 100644
index 000000000..1536096d0
Binary files /dev/null and b/docs/guides/int_basics/message-components/images/image6.png differ
diff --git a/docs/guides/int_basics/message-components/intro.md b/docs/guides/int_basics/message-components/intro.md
new file mode 100644
index 000000000..cc22d54c5
--- /dev/null
+++ b/docs/guides/int_basics/message-components/intro.md
@@ -0,0 +1,66 @@
+---
+uid: Guides.MessageComponents.Intro
+title: Getting Started with Components
+---
+
+# Message Components
+
+Message components are a framework for adding interactive elements to a message your app or bot sends. They're accessible, customizable, and easy to use.
+
+## What is a Component
+
+Components are a new parameter you can use when sending messages with your bot. There are currently 2 different types of components you can use: Buttons and Select Menus.
+
+## Creating components
+
+Lets create a simple component that has a button. First thing we need is a way to trigger the message, this can be done via commands or simply a ready event. Lets make a command that triggers our button message.
+
+```cs
+[Command("spawner")]
+public async Task Spawn()
+{
+ // Reply with some components
+}
+```
+
+We now have our command, but we need to actually send the buttons with the command. To do that, lets look at the `ComponentBuilder` class:
+
+| Name | Description |
+| ---------------- | --------------------------------------------------------------------------- |
+| `FromMessage` | Creates a new builder from a message. |
+| `FromComponents` | Creates a new builder from the provided list of components. |
+| `WithSelectMenu` | Adds a `SelectMenuBuilder` to the `ComponentBuilder` at the specific row. |
+| `WithButton` | Adds a `ButtonBuilder` to the `ComponentBuilder` at the specific row. |
+| `Build` | Builds this builder into a `MessageComponent` used to send your components. |
+
+We see that we can use the `WithButton` function so lets do that. looking at its parameters it takes:
+
+- `label` - The display text of the button.
+- `customId` - The custom id of the button, this is whats sent by discord when your button is clicked.
+- `style` - The discord defined style of the button.
+- `emote` - An emote to be displayed with the button.
+- `url` - The url of the button if its a link button.
+- `disabled` - Whether or not the button is disabled.
+- `row` - The row the button will occupy.
+
+Since were just making a busic button, we dont have to specify anything else besides the label and custom id.
+
+```cs
+var builder = new ComponentBuilder()
+ .WithButton("label", "custom-id");
+```
+
+Lets add this to our command:
+
+```cs
+[Command("spawner")]
+public async Task Spawn()
+{
+ var builder = new ComponentBuilder()
+ .WithButton("label", "custom-id");
+
+ await ReplyAsync("Here is a button!", components: builder.Build());
+}
+```
+
+
diff --git a/docs/guides/int_basics/message-components/responding-to-buttons.md b/docs/guides/int_basics/message-components/responding-to-buttons.md
new file mode 100644
index 000000000..7546c6fe2
--- /dev/null
+++ b/docs/guides/int_basics/message-components/responding-to-buttons.md
@@ -0,0 +1,37 @@
+---
+uid: Guides.MessageComponents.Responding
+title: Responding to Components
+---
+
+# Responding to button clicks
+
+Responding to buttons is pretty simple, there are a couple ways of doing it and we can cover both.
+
+### Method 1: Hooking the InteractionCreated Event
+
+We can hook the `ButtonExecuted` event for button type interactions:
+
+```cs
+client.ButtonExecuted += MyButtonHandler;
+```
+
+Now, lets write our handler.
+
+```cs
+public async Task MyButtonHandler(SocketMessageComponent component)
+{
+ // We can now check for our custom id
+ switch(component.Data.CustomId)
+ {
+ // Since we set our buttons custom id as 'custom-id', we can check for it like this:
+ case "custom-id":
+ // Lets respond by sending a message saying they clicked the button
+ await component.RespondAsync($"{component.User.Mention} has clicked the button!");
+ break;
+ }
+}
+```
+
+Running it and clicking the button:
+
+
\ No newline at end of file
diff --git a/docs/guides/int_basics/message-components/select-menus.md b/docs/guides/int_basics/message-components/select-menus.md
new file mode 100644
index 000000000..6dfe151b1
--- /dev/null
+++ b/docs/guides/int_basics/message-components/select-menus.md
@@ -0,0 +1,76 @@
+---
+uid: Guides.MessageComponents.SelectMenus
+title: Select Menus
+---
+
+# Select menus
+
+Select menus allow users to select from a range of options, this can be quite useful with configuration commands etc.
+
+## Creating a select menu
+
+We can use a `SelectMenuBuilder` to create our menu.
+
+```cs
+var menuBuilder = new SelectMenuBuilder()
+ .WithPlaceholder("Select an option")
+ .WithCustomId("menu-1")
+ .WithMinValues(1)
+ .WithMaxValues(1)
+ .AddOption("Option A", "opt-a", "Option B is lying!")
+ .AddOption("Option B", "opt-b", "Option A is telling the truth!");
+
+var builder = new ComponentBuilder()
+ .WithSelectMenu(menuBuilder);
+```
+
+Lets add this to a command:
+
+```cs
+[Command("spawner")]
+public async Task Spawn()
+{
+ var menuBuilder = new SelectMenuBuilder()
+ .WithPlaceholder("Select an option")
+ .WithCustomId("menu-1")
+ .WithMinValues(1)
+ .WithMaxValues(1)
+ .AddOption("Option A", "opt-a", "Option B is lying!")
+ .AddOption("Option B", "opt-b", "Option A is telling the truth!");
+
+ var builder = new ComponentBuilder()
+ .WithSelectMenu(menuBuilder);
+
+ await ReplyAsync("Whos really lying?", components: builder.Build());
+}
+```
+
+Running this produces this result:
+
+
+
+And opening the menu we see:
+
+
+
+Lets handle the selection of an option, We can hook the `SelectMenuExecuted` event to handle our select menu:
+
+```cs
+client.SelectMenuExecuted += MyMenuHandler;
+```
+
+The `SelectMenuExecuted` also supplies a `SocketMessageComponent` argument, we can confirm that its a select menu by checking the `ComponentType` inside of the data field if we need, but the library will do that for us and only execute our handler if its a select menu.
+
+The values that the user has selected will be inside of the `Values` collection in the Data field. we can list all of them back to the user for this example.
+
+```cs
+public async Task MyMenuHandler(SocketMessageComponent arg)
+{
+ var text = string.Join(", ", arg.Data.Values);
+ await arg.RespondAsync($"You have selected {text}");
+}
+```
+
+Running this produces this result:
+
+
diff --git a/docs/guides/int_framework/autocompletion.md b/docs/guides/int_framework/autocompletion.md
new file mode 100644
index 000000000..834db2b4f
--- /dev/null
+++ b/docs/guides/int_framework/autocompletion.md
@@ -0,0 +1,47 @@
+---
+uid: Guides.IntFw.AutoCompletion
+title: Command Autocompletion
+---
+
+# AutocompleteHandlers
+
+[Autocompleters] provide a similar pattern to TypeConverters.
+[Autocompleters] are cached, singleton services and they are used by the
+Interaction Service to handle Autocomplete Interations targeted to a specific Slash Command parameter.
+
+To start using AutocompleteHandlers, use the `[AutocompleteAttribute(Type type)]` overload of the [AutocompleteAttribute].
+This will dynamically link the parameter to the [AutocompleteHandler] type.
+
+AutocompleteHandlers raise the `AutocompleteHandlerExecuted` event on execution. This event can be also used to create a post-execution logic, just like the `*CommandExecuted` events.
+
+## Creating AutocompleteHandlers
+
+A valid AutocompleteHandlers must inherit [AutocompleteHandler] base type and implement all of its abstract methods.
+
+### GenerateSuggestionsAsync()
+
+The Interactions Service uses this method to generate a response of an Autocomplete Interaction.
+This method should return `AutocompletionResult.FromSuccess(IEnumerable)` to
+display parameter suggestions to the user. If there are no suggestions to be presented to the user, you have two results:
+
+1. Returning the parameterless `AutocompletionResult.FromSuccess()` will display a "No options match your search." message to the user.
+2. Returning `AutocompleteResult.FromError()` will make the Interaction Service **not** respond to the interaction,
+consequently displaying the user a "Loading options failed." message. `AutocompletionResult.FromError()` is solely used for error handling purposes. Discord currently doesn't allow
+you to display custom error messages. This result type will be directly returned to the `AutocompleteHandlerExecuted` method.
+
+## Resolving AutocompleteHandler Dependencies
+
+AutocompleteHandler dependencies are resolved using the same dependency injection
+pattern as the Interaction Modules.
+Property injection and constructor injection are both valid ways to get service dependencies.
+
+Because [AutocompleterHandlers] are constructed at service startup,
+class dependencies are resolved only once.
+
+> [!NOTE]
+> If you need to access per-request dependencies you can use the
+> IServiceProvider parameter of the `GenerateSuggestionsAsync()` method.
+
+[AutoCompleteHandlers]: xref:Discord.Interactions.AutocompleteHandler
+[AutoCompleteHandler]: xref:Discord.Interactions.AutocompleteHandler
+[AutoCompleteAttribute]:
diff --git a/docs/guides/int_framework/dependency-injection.md b/docs/guides/int_framework/dependency-injection.md
new file mode 100644
index 000000000..31d001f4b
--- /dev/null
+++ b/docs/guides/int_framework/dependency-injection.md
@@ -0,0 +1,13 @@
+---
+uid: Guides.IntFw.DI
+title: Dependency Injection
+---
+
+# Dependency Injection
+
+Dependency injection in the Interaction Service is mostly based on that of the Text-based command service,
+for which further information is found [here](xref:Guides.TextCommands.DI).
+
+> [!NOTE]
+> The 2 are nearly identical, except for one detail:
+> [Resolving Module Dependencies](xref:Guides.IntFw.Intro#resolving-module-dependencies)
diff --git a/docs/guides/int_framework/intro.md b/docs/guides/int_framework/intro.md
new file mode 100644
index 000000000..7dfd7ac6e
--- /dev/null
+++ b/docs/guides/int_framework/intro.md
@@ -0,0 +1,353 @@
+---
+uid: Guides.IntFw.Intro
+title: Introduction to the Interaction Service
+---
+
+# Getting Started
+
+The Interaction Service provides an attribute based framework for creating Discord Interaction handlers.
+
+To start using the Interaction Service, you need to create a service instance.
+Optionally you can provide the [InteractionService] constructor with a
+[InteractionServiceConfig] to change the services behaviour to suit your needs.
+
+```csharp
+...
+// _client here is DiscordSocketClient.
+// A different approach to passing in a restclient is also possible.
+var _interactionService = new InteractionService(_client.Rest);
+
+...
+```
+
+## Modules
+
+Attribute based Interaction handlers must be defined within a command module class.
+Command modules are responsible for executing the Interaction handlers and providing them with the necessary execution info and helper functions.
+
+Command modules are transient objects.
+A new module instance is created before a command execution starts then it will be disposed right after the method returns.
+
+Every module class must:
+
+- be public
+- inherit [InteractionModuleBase]
+
+Optionally you can override the included :
+
+- OnModuleBuilding (executed after the module is built)
+- BeforeExecute (executed before a command execution starts)
+- AfterExecute (executed after a command execution concludes)
+
+methods to configure the modules behaviour.
+
+Every command module exposes a set of helper methods, namely:
+
+- `RespondAsync()` => Respond to the interaction
+- `FollowupAsync()` => Create a followup message for an interaction
+- `ReplyAsync()` => Send a message to the origin channel of the interaction
+- `DeleteOriginalResponseAsync()` => Delete the original interaction response
+
+## Commands
+
+Valid **Interaction Commands** must comply with the following requirements:
+
+| | return type | max parameter count | allowed parameter types | attribute |
+|-------------------------------|------------------------------|---------------------|-------------------------------|--------------------------|
+|[Slash Command](#slash-commands)| `Task`/`Task` | 25 | any* | `[SlashCommand]` |
+|[User Command](#user-commands) | `Task`/`Task` | 1 | Implementations of `IUser` | `[UserCommand]` |
+|[Message Command](#message-commands)| `Task`/`Task` | 1 | Implementations of `IMessage` | `[MessageCommand]` |
+|[Component Interaction Command](#component-interaction-commands)| `Task`/`Task` | inf | `string` or `string[]` | `[ComponentInteraction]` |
+|[Autocomplete Command](#autocomplete-commands)| `Task`/`Task` | - | - | `[AutocompleteCommand]`|
+
+> [!NOTE]
+> a `TypeConverter` that is capable of parsing type in question must be registered to the [InteractionService] instance.
+> You should avoid using long running code in your command module.
+> Depending on your setup, long running code may block the Gateway thread of your bot, interrupting its connection to Discord.
+
+## Slash Commands
+
+Slash Commands are created using the [SlashCommandAttribute].
+Every Slash Command must declare a name and a description.
+You can check Discords **Application Command Naming Guidelines**
+[here](https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-naming).
+
+[!code-csharp[Slash Command](samples/intro/slashcommand.cs)]
+
+### Parameters
+
+Slash Commands can have up to 25 method parameters. You must name your parameters in accordance with
+[Discords Naming Guidelines](https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-naming).
+[InteractionService] also features a pascal casing seperator for formatting parameter names with
+pascal casing into Discord compliant parameter names('parameterName' => 'parameter-name').
+By default, your methods can feature the following parameter types:
+
+- Implementations of [IUser]
+- Implementations of [IChannel]
+- Implementations of [IRole]
+- Implementations of [IMentionable]
+- `string`
+- `float`, `double`, `decimal`
+- `bool`
+- `char`
+- `sbyte`, `byte`
+- `int16`, `int32`, `int64`
+- `uint16`, `uint32`, `uint64`
+- `enum` (Values are registered as multiple choice options and are enforced by Discord. Use `[HideAttribute]` on enum values to prevent them from getting registered.)
+- `DateTime`
+- `TimeSpan`
+
+---
+
+**You can use more specialized implementations of [IChannel] to restrict the allowed channel types for a channel type option.*
+| interface | Channel Type |
+|---------------------|-------------------------------|
+| `IStageChannel` | Stage Channels |
+| `IVoiceChannel` | Voice Channels |
+| `IDMChannel` | DM Channels |
+| `IGroupChannel` | Group Channels |
+| `ICategory Channel` | Category Channels |
+| `INewsChannel` | News Channels |
+| `IThreadChannel` | Public, Private, News Threads |
+| `ITextChannel` | Text Channels |
+
+---
+
+#### Optional Parameters
+
+Parameters with default values (ie. `int count = 0`) will be displayed as optional parameters on Discord Client.
+
+#### Parameter Summary
+
+By using the [SummaryAttribute] you can customize the displayed name and description of a parameter
+
+[!code-csharp[Summary Attribute](samples/intro/summaryattribute.cs)]
+
+#### Parameter Choices
+
+[ChoiceAttribute] can be used to add choices to a parameter.
+
+[!code-csharp[Choice Attribute](samples/intro/groupattribute.cs)]
+
+This Slash Command will be displayed exactly the same as the previous example.
+
+#### Channel Types
+
+Channel types for an [IChannel] parameter can also be restricted using the [ChannelTypesAttribute].
+
+[!code-csharp[Channel Attribute](samples/intro/channelattribute.cs)]
+
+In this case, user can only input Stage Channels and Text Channels to this parameter.
+
+#### Min/Max Value
+
+You can specify the permitted max/min value for a number type parameter using the [MaxValueAttribute] and [MinValueAttribute].
+
+## User Commands
+
+A valid User Command must have the following structure:
+
+[!code-csharp[User Command](samples/intro/usercommand.cs)]
+
+> [!WARNING]
+> User commands can only have one parameter and its type must be an implementation of [IUser].
+
+## Message Commands
+
+A valid Message Command must have the following structure:
+
+[!code-csharp[Message Command](samples/intro/messagecommand.cs)]
+
+> [!WARNING]
+> Message commands can only have one parameter and its type must be an implementation of [IMessage].
+
+## Component Interaction Commands
+
+Component Interaction Commands are used to handle interactions that originate from **Discord Message Component**s.
+This pattern is particularly useful if you will be reusing a set a **Custom ID**s.
+
+Component Interaction Commands support wild card matching,
+by default `*` character can be used to create a wild card pattern.
+Interaction Service will use lazy matching to capture the words corresponding to the wild card character.
+And the captured words will be passed on to the command method in the same order they were captured.
+
+[!code-csharp[Button](samples/intro/button.cs)]
+
+You may use as many wild card characters as you want.
+
+> [!NOTE]
+> If Interaction Service recieves a component interaction with **player:play,rickroll** custom id,
+> `op` will be *play* and `name` will be *rickroll*
+
+## Select Menus
+
+Unlike button interactions, select menu interactions also contain the values of the selected menu items.
+In this case, you should structure your method to accept a string array.
+
+[!code-csharp[Dropdown](samples/intro/dropdown.cs)]
+
+> [!NOTE]
+> Wildcards may also be used to match a select menu ID,
+> though keep in mind that the array containing the select menu values should be the last parameter.
+
+## Autocomplete Commands
+
+Autocomplete commands must be parameterless methods. A valid Autocomplete command must have the following structure:
+
+[!code-csharp[Autocomplete Command](samples/intro/autocomplete.cs)]
+
+Alternatively, you can use the [AutocompleteHandlers] to simplify this workflow.
+
+## Interaction Context
+
+Every command module provides its commands with an execution context.
+This context property includes general information about the underlying interaction that triggered the command execution.
+The base command context.
+
+You can design your modules to work with different implementation types of [IInteractionContext].
+To achieve this, make sure your module classes inherit from the generic variant of the [InteractionModuleBase].
+
+> [!NOTE]
+> Context type must be consistent throughout the project, or you will run into issues during runtime.
+
+The [InteractionService] ships with 4 different kinds of [InteractionContext]:
+
+1. [InteractionContext]]: A bare-bones execution context consisting of only implementation neutral interfaces
+2. [SocketInteractionContext]: An execution context for use with [DiscordSocketClient]. Socket entities are exposed in this context without the need of casting them.
+3. [ShardedInteractionContext]: [DiscordShardedClient] variant of the [SocketInteractionContext]
+4. [RestInteractionContext]: An execution context designed to be used with a [DiscordRestClient] and webhook based interactions pattern
+
+You can create custom Interaction Contexts by implementing the [IInteractionContext] interface.
+
+One problem with using the concrete type InteractionContexts is that you cannot access the information that is specific to different interaction types without casting. Concrete type interaction contexts are great for creating shared interaction modules but you can also use the generic variants of the built-in interaction contexts to create interaction specific interaction modules.
+
+> [!INFO]
+> Message component interactions have access to a special method called `UpdateAsync()` to update the body of the method the interaction originated from.
+> Normally this wouldn't be accessable without casting the `Context.Interaction`.
+
+[!code-csharp[Context Example](samples/intro/context.cs)]
+
+## Loading Modules
+
+[InteractionService] can automatically discover and load modules that inherit [InteractionModuleBase] from an `Assembly`.
+Call `InteractionService.AddModulesAsync()` to use this functionality.
+
+> [!NOTE]
+> You can also manually add Interaction modules using the `InteractionService.AddModuleAsync()`
+> method by providing the module type you want to load.
+
+## Resolving Module Dependencies
+
+Module dependencies are resolved using the Constructor Injection and Property Injection patterns.
+Meaning, the constructor parameters and public settable properties of a module will be assigned using the `IServiceProvider`.
+For more information on dependency injection, read the [DependencyInjection] guides.
+
+> [!NOTE]
+> On every command execution, module dependencies are resolved using a new service scope which allows you to utilize scoped service instances, just like in Asp.Net.
+> Including the precondition checks, every module method is executed using the same service scope and service scopes are disposed right after the `AfterExecute` method returns.
+
+## Module Groups
+
+Module groups allow you to create sub-commands and sub-commands groups.
+By nesting commands inside a module that is tagged with [GroupAttribute] you can create prefixed commands.
+
+> [!WARNING]
+> Although creating nested module stuctures are allowed,
+> you are not permitted to use more than 2 [GroupAttribute]'s in module hierarchy.
+
+## Executing Commands
+
+Any of the following socket events can be used to execute commands:
+
+- [InteractionCreated]
+- [ButtonExecuted]
+- [SelectMenuExecuted]
+- [AutocompleteExecuted]
+- [UserCommandExecuted]
+- [MessageCommandExecuted]
+
+Commands can be either executed on the gateway thread or on a seperate thread from the thread pool. This behaviour can be configured by changing the *RunMode* property of `InteractionServiceConfig` or by setting the *runMode* parameter of a command attribute.
+
+You can also configure the way [InteractionService] executes the commands.
+By default, commands are executed using `ConstructorInfo.Invoke()` to create module instances and
+`MethodInfo.Invoke()` method for executing the method bodies.
+By setting, `InteractionServiceConfig.UseCompiledLambda` to `true`, you can make [InteractionService] create module instances and execute commands using
+*Compiled Lambda* expressions. This cuts down on command execution time but it might add some memory overhead.
+
+Time it takes to create a module instance and execute a `Task.Delay(0)` method using the Reflection methods compared to Compiled Lambda expressions:
+
+| Method | Mean | Error | StdDev |
+|----------------- |----------:|---------:|---------:|
+| ReflectionInvoke | 225.93 ns | 4.522 ns | 7.040 ns |
+| CompiledLambda | 48.79 ns | 0.981 ns | 1.276 ns |
+
+## Registering Commands to Discord
+
+Application commands loaded to the Interaction Service can be registered to Discord using a number of different methods.
+In most cases `RegisterCommandsGloballyAsync()` and `RegisterCommandsToGuildAsync()` are the methods to use.
+Command registration methods can only be used after the gateway client is ready or the rest client is logged in.
+
+[!code-csharp[Registering Commands Example](samples/intro/registering.cs)]
+
+Methods like `AddModulesToGuildAsync()`, `AddCommandsToGuildAsync()`, `AddModulesGloballyAsync()` and `AddCommandsGloballyAsync()`
+can be used to register cherry picked modules or commands to global/guild scopes.
+
+> [!NOTE]
+> In debug environment, since Global commands can take up to 1 hour to register/update,
+> it is adviced to register your commands to a test guild for your changes to take effect immediately.
+> You can use preprocessor directives to create a simple logic for registering commands as seen above
+
+## Interaction Utility
+
+Interaction Service ships with a static `InteractionUtiliy`
+class which contains some helper methods to asynchronously waiting for Discord Interactions.
+For instance, `WaitForInteractionAsync()` method allows you to wait for an Interaction for a given amount of time.
+This method returns the first encountered Interaction that satisfies the provided predicate.
+
+> [!WARNING]
+> If you are running the Interaction Service on `RunMode.Sync` you should avoid using this method in your commands,
+> as it will block the gateway thread and interrupt your bots connection.
+
+## Webhook Based Interactions
+
+Instead of using the gateway to recieve Discord Interactions, Discord allows you to recieve Interaction events over Webhooks.
+Interaction Service also supports this Interaction type but to be able to
+respond to the Interactions within your command modules you need to perform the following:
+
+- Make your modules inherit `RestInteractionModuleBase`
+- Set the `ResponseCallback` property of `InteractionServiceConfig` so that the `ResponseCallback`
+delegate can be used to create HTTP responses from a deserialized json object string.
+- Use the interaction endpoints of the module base instead of the interaction object (ie. `RespondAsync()`, `FollowupAsync()`...).
+
+[AutocompleteHandlers]: xref:Guides.IntFw.AutoCompletion
+[DependencyInjection]: xref:Guides.TextCommands.DI
+
+[GroupAttribute]: xref:Discord.Interactions.GroupAttribute
+[InteractionService]: xref:Discord.Interactions.InteractionService
+[InteractionServiceConfig]: xref:Discord.Interactions.InteractionServiceConfig
+[InteractionModuleBase]: xref:Discord.Interactions.InteractionModuleBase
+[SlashCommandAttribute]: xref:Discord.Interactions.SlashCommandAttribute
+[InteractionCreated]: xref:Discord.WebSocket.BaseSocketClient
+[ButtonExecuted]: xref:Discord.WebSocket.BaseSocketClient
+[SelectMenuExecuted]: xref:Discord.WebSocket.BaseSocketClient
+[AutocompleteExecuted]: xref:Discord.WebSocket.BaseSocketClient
+[UserCommandExecuted]: xref:Discord.WebSocket.BaseSocketClient
+[MessageCommandExecuted]: xref:Discord.WebSocket.BaseSocketClient
+[DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient
+[DiscordRestClient]: xref:Discord.Rest.DiscordRestClient
+[SocketInteractionContext]: xref:Discord.Interactions.SocketInteractionContext
+[ShardedInteractionContext]: xref:Discord.Interactions.ShardedInteractionContext
+[InteractionContext]: xref:Discord.Interactions.InteractionContext
+[IInteractionContect]: xref:Discord.Interactions.IInteractionContext
+[RestInteractionContext]: xref:Discord.Rest.RestInteractionContext
+[SummaryAttribute]: xref:Discord.Interactions.SummaryAttribute
+[ChoiceAttribute]: xref:Discord.Interactions.ChoiceAttribute
+[ChannelTypesAttribute]: xref:Discord.Interactions.ChannelTypesAttribute
+[MaxValueAttribute]: xref:Discord.Interactions.MaxValueAttribute
+[MinValueAttribute]: xref:Discord.Interactions.MinValueAttribute
+
+[IChannel]: xref:Discord.IChannel
+[IRole]: xref:Discord.IRole
+[IUser]: xref:Discord.IUser
+[IMessage]: xref:Discord.IMessage
+[IMentionable]: xref:Discord.IMentionable
diff --git a/docs/guides/int_framework/post-execution.md b/docs/guides/int_framework/post-execution.md
new file mode 100644
index 000000000..70da8ad1e
--- /dev/null
+++ b/docs/guides/int_framework/post-execution.md
@@ -0,0 +1,69 @@
+---
+uid: Guides.IntFw.PostExecution
+title: Post-Command execution
+---
+
+# Post-Execution Logic
+
+Interaction Service uses [IResult] to provide information about the state of command execution.
+These can be used to log internal exceptions or provide some insight to the command user.
+
+If you are running your commands using `RunMode.Sync` these command results can be retrieved from
+the return value of [InteractionService.ExecuteCommandAsync] method or by
+registering delegates to Interaction Service events.
+
+If you are using the `RunMode.Async` to run your commands,
+you must use the Interaction Service events to get the execution results. When using `RunMode.Async`,
+[InteractionService.ExecuteCommandAsync] will always return a successful result.
+
+[InteractionService.ExecuteCommandAsync]: xref:Discord.Interactions.InteractionService.ExecuteCommandAsync*
+
+## Results
+
+Interaction Result come in a handful of different flavours:
+
+1. [AutocompletionResult]: returned by Autocompleters
+2. [ExecuteResult]: contains the result of method body execution process
+3. [PreconditionGroupResult]: returned by Precondition groups
+4. [PreconditionResult]: returned by preconditions
+5. [RuntimeResult]: a user implementable result for returning user defined results
+6. [SearchResult]: returned by command lookup map
+7. [TypeConverterResult]: returned by TypeConverters
+
+> [!NOTE]
+> You can either use the [IResult.Error] property of an Interaction result or create type check for the
+> afformentioned result types to branch out your post-execution logic to handle different situations.
+
+
+[AutocompletionResult]: xref:Discord.AutocompleteResult
+[ExecuteResult]: xref:Discord.Interactions.ExecuteResult
+[PreconditionGroupResult]: xref:Discord.Interactions.PreconditionGroupResult
+[PreconditionResult]: xref:Discord.Interactions.PreconditionResult
+[SearchResult]: xref:Discord.Interactions.SearchResult`1
+[TypeConverterResult]: xref:Discord.Interactions.TypeConverterResult
+[IResult.Error]: xref:Discord.Interactions.IResult.Error*
+
+## CommandExecuted Events
+
+Every time a command gets executed, Interaction Service raises a `CommandExecuted` event.
+These events can be used to create a post-execution pipeline.
+
+[!code-csharp[Error Review](samples/postexecution/error_review.cs)]
+
+## Log Event
+
+InteractionService regularly outputs information about the occuring events to keep the developer informed.
+
+## Runtime Result
+
+Interaction commands allow you to return `Task` to pass on additional information about the command execution
+process back to your post-execution logic.
+
+Custom [RuntimeResult] classes can be created by inheriting the base [RuntimeResult] class.
+
+If command execution process reaches the method body of the command and no exceptions are thrown during
+the execution of the method body, [RuntimeResult] returned by your command will be accessible by casting/type-checking the
+[IResult] parameter of the `CommandExecuted` event delegate.
+
+[RuntimeResult]: xref:Discord.Interactions.RuntimeResult
+[IResult]: xref:Discord.Interactions.IResult
diff --git a/docs/guides/int_framework/preconditions.md b/docs/guides/int_framework/preconditions.md
new file mode 100644
index 000000000..75e572798
--- /dev/null
+++ b/docs/guides/int_framework/preconditions.md
@@ -0,0 +1,77 @@
+---
+uid: Guides.IntFw.Preconditions
+title: Preconditions
+---
+
+# Preconditions
+
+Precondition logic is the same as it is for Text-based commands.
+A list of attributes and usage is still given for people who are new to both.
+
+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.Interactions.PreconditionAttribute
+[ParameterPreconditionAttribute]: xref:Discord.Interactions.ParameterPreconditionAttribute
+
+## Bundled Preconditions
+
+@Discord.Interactions ships with several bundled Preconditions for you
+to use.
+
+* @Discord.Interactions.RequireContextAttribute
+* @Discord.Interactions.RequireOwnerAttribute
+* @Discord.Interactions.RequireBotPermissionAttribute
+* @Discord.Interactions.RequireUserPermissionAttribute
+* @Discord.Interactions.RequireNsfwAttribute
+* @Discord.Interactions.RequireRoleAttribute
+
+## Using Preconditions
+
+To use a precondition, simply apply any valid precondition candidate to
+a command method signature as an attribute.
+
+[!code-csharp[Precondition usage](samples/preconditions/precondition_usage.cs)]
+
+## ORing Preconditions
+
+When writing commands, you may want to allow some of them to be
+executed when only some of the precondition checks are passed.
+
+This is where the [Group] property of a precondition attribute comes in
+handy. By assigning two or more preconditions to a group, the command
+system will allow the command to be executed when one of the
+precondition passes.
+
+### Example - ORing Preconditions
+
+[!code-csharp[OR Precondition](samples/preconditions/group_precondition.cs)]
+
+[Group]: xref:Discord.Commands.PreconditionAttribute.Group
+
+## 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.
+
+[CheckPermissionsAsync]: xref:Discord.Commands.PreconditionAttribute.CheckPermissionsAsync*
+[PreconditionResult.FromSuccess]: xref:Discord.Commands.PreconditionResult.FromSuccess*
+[PreconditionResult.FromError]: xref:Discord.Commands.PreconditionResult.FromError*
diff --git a/docs/guides/int_framework/samples/intro/autocomplete.cs b/docs/guides/int_framework/samples/intro/autocomplete.cs
new file mode 100644
index 000000000..f93c56eaa
--- /dev/null
+++ b/docs/guides/int_framework/samples/intro/autocomplete.cs
@@ -0,0 +1,9 @@
+[AutocompleteCommand("parameter_name", "command_name")]
+public async Task Autocomplete()
+{
+ IEnumerable results;
+
+ ...
+
+ await (Context.Interaction as SocketAutocompleteInteraction).RespondAsync(results);
+}
diff --git a/docs/guides/int_framework/samples/intro/button.cs b/docs/guides/int_framework/samples/intro/button.cs
new file mode 100644
index 000000000..2b04d081b
--- /dev/null
+++ b/docs/guides/int_framework/samples/intro/button.cs
@@ -0,0 +1,5 @@
+[ComponentInteraction("player:*,*")]
+public async Task Play(string op, string name)
+{
+ ...
+}
diff --git a/docs/guides/int_framework/samples/intro/channelattribute.cs b/docs/guides/int_framework/samples/intro/channelattribute.cs
new file mode 100644
index 000000000..09eb0275f
--- /dev/null
+++ b/docs/guides/int_framework/samples/intro/channelattribute.cs
@@ -0,0 +1,5 @@
+[SlashCommand("name", "Description")]
+public async Task Command([ChannelTypes(ChannelType.Stage, ChannelType.Text)] IChannel channel)
+{
+ ...
+}
diff --git a/docs/guides/int_framework/samples/intro/context.cs b/docs/guides/int_framework/samples/intro/context.cs
new file mode 100644
index 000000000..5976ffc5c
--- /dev/null
+++ b/docs/guides/int_framework/samples/intro/context.cs
@@ -0,0 +1,14 @@
+discordClient.ButtonExecuted += async (interaction) =>
+{
+ var ctx = new SocketInteractionContext(discordClient, interaction);
+ await _interactionService.ExecuteAsync(ctx, serviceProvider);
+};
+
+public class MessageComponentModule : InteractionModuleBase>
+{
+ [ComponentInteraction("custom_id")]
+ public async Command()
+ {
+ Context.Interaction.UpdateAsync(...);
+ }
+}
diff --git a/docs/guides/int_framework/samples/intro/dropdown.cs b/docs/guides/int_framework/samples/intro/dropdown.cs
new file mode 100644
index 000000000..2b0af477f
--- /dev/null
+++ b/docs/guides/int_framework/samples/intro/dropdown.cs
@@ -0,0 +1,11 @@
+[ComponentInteraction("role_selection")]
+public async Task RoleSelection(string[] selectedRoles)
+{
+ ...
+}
+
+[ComponentInteraction("role_selection_*")]
+public async Task RoleSelection(string id, string[] selectedRoles)
+{
+ ...
+}
diff --git a/docs/guides/int_framework/samples/intro/groupattribute.cs b/docs/guides/int_framework/samples/intro/groupattribute.cs
new file mode 100644
index 000000000..86a492c31
--- /dev/null
+++ b/docs/guides/int_framework/samples/intro/groupattribute.cs
@@ -0,0 +1,21 @@
+[SlashCommand("blep", "Send a random adorable animal photo")]
+public async Task Blep([Choice("Dog", "dog"), Choice("Cat", "cat"), Choice("Penguin", "penguin")] string animal)
+{
+ ...
+}
+
+// In most cases, you can use an enum to replace the seperate choice attributes in a command.
+
+public enum Animal
+{
+ Cat,
+ Dog,
+ Penguin
+}
+
+[SlashCommand("blep", "Send a random adorable animal photo")]
+public async Task Blep(Animal animal)
+{
+ ...
+}
+```
diff --git a/docs/guides/int_framework/samples/intro/messagecommand.cs b/docs/guides/int_framework/samples/intro/messagecommand.cs
new file mode 100644
index 000000000..5cb0c4b60
--- /dev/null
+++ b/docs/guides/int_framework/samples/intro/messagecommand.cs
@@ -0,0 +1,5 @@
+[MessageCommand("Bookmark")]
+public async Task Bookmark(IMessage msg)
+{
+ ...
+}
diff --git a/docs/guides/int_framework/samples/intro/registering.cs b/docs/guides/int_framework/samples/intro/registering.cs
new file mode 100644
index 000000000..f603df7bf
--- /dev/null
+++ b/docs/guides/int_framework/samples/intro/registering.cs
@@ -0,0 +1,5 @@
+#if DEBUG
+ await interactionService.RegisterCommandsToGuildAsync();
+#else
+ await interactionService.RegisterCommandsGloballyAsync();
+#endif
diff --git a/docs/guides/int_framework/samples/intro/slashcommand.cs b/docs/guides/int_framework/samples/intro/slashcommand.cs
new file mode 100644
index 000000000..5f4f7fb0f
--- /dev/null
+++ b/docs/guides/int_framework/samples/intro/slashcommand.cs
@@ -0,0 +1,5 @@
+[SlashCommand("echo", "Echo an input")]
+public async Task Echo(string input)
+{
+ await RespondAsync(input);
+}
diff --git a/docs/guides/int_framework/samples/intro/summaryattribute.cs b/docs/guides/int_framework/samples/intro/summaryattribute.cs
new file mode 100644
index 000000000..8a9b7d3e1
--- /dev/null
+++ b/docs/guides/int_framework/samples/intro/summaryattribute.cs
@@ -0,0 +1 @@
+[Summary(description: "this is a parameter description")] string input
diff --git a/docs/guides/int_framework/samples/intro/usercommand.cs b/docs/guides/int_framework/samples/intro/usercommand.cs
new file mode 100644
index 000000000..02c4a1e63
--- /dev/null
+++ b/docs/guides/int_framework/samples/intro/usercommand.cs
@@ -0,0 +1,5 @@
+[UserCommand("Say Hello")]
+public async Task SayHello(IUser user)
+{
+ ...
+}
diff --git a/docs/guides/int_framework/samples/postexecution/error_review.cs b/docs/guides/int_framework/samples/postexecution/error_review.cs
new file mode 100644
index 000000000..dd397b2c9
--- /dev/null
+++ b/docs/guides/int_framework/samples/postexecution/error_review.cs
@@ -0,0 +1,28 @@
+interactionService.SlashCommandExecuted += SlashCommandExecuted;
+
+async Task SlashCommandExecuted(SlashCommandInfo arg1, Discord.IInteractionContext arg2, IResult arg3)
+{
+ if (!arg3.IsSuccess)
+ {
+ switch (arg3.Error)
+ {
+ case InteractionCommandError.UnmetPrecondition:
+ await arg2.Interaction.RespondAsync($"Unmet Precondition: {arg3.ErrorReason}");
+ break;
+ case InteractionCommandError.UnknownCommand:
+ await arg2.Interaction.RespondAsync("Unknown command");
+ break;
+ case InteractionCommandError.BadArgs:
+ await arg2.Interaction.RespondAsync("Invalid number or arguments");
+ break;
+ case InteractionCommandError.Exception:
+ await arg2.Interaction.RespondAsync("Command exception:{arg3.ErrorReason}");
+ break;
+ case InteractionCommandError.Unsuccessful:
+ await arg2.Interaction.RespondAsync("Command could not be executed");
+ break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/docs/guides/commands/samples/preconditions/group_precondition.cs b/docs/guides/int_framework/samples/preconditions/group_precondition.cs
similarity index 100%
rename from docs/guides/commands/samples/preconditions/group_precondition.cs
rename to docs/guides/int_framework/samples/preconditions/group_precondition.cs
diff --git a/docs/guides/int_framework/samples/preconditions/precondition_usage.cs b/docs/guides/int_framework/samples/preconditions/precondition_usage.cs
new file mode 100644
index 000000000..bea2918dc
--- /dev/null
+++ b/docs/guides/int_framework/samples/preconditions/precondition_usage.cs
@@ -0,0 +1,3 @@
+[RequireOwner]
+[SlashCommand("hi")]
+public Task SayHiAsync() => RespondAsync("hello owner!");
diff --git a/docs/guides/int_framework/samples/typeconverters/enum_converter.cs b/docs/guides/int_framework/samples/typeconverters/enum_converter.cs
new file mode 100644
index 000000000..6e1b9ded6
--- /dev/null
+++ b/docs/guides/int_framework/samples/typeconverters/enum_converter.cs
@@ -0,0 +1,30 @@
+internal sealed class EnumConverter : TypeConverter where T : struct, Enum
+{
+ public override ApplicationCommandOptionType GetDiscordType() => ApplicationCommandOptionType.String;
+
+ public override Task ReadAsync(IInteractionCommandContext context, SocketSlashCommandDataOption option, IServiceProvider services)
+ {
+ if (Enum.TryParse((string)option.Value, out var result))
+ return Task.FromResult(TypeConverterResult.FromSuccess(result));
+ else
+ return Task.FromResult(TypeConverterResult.FromError(InteractionCommandError.ConvertFailed, $"Value {option.Value} cannot be converted to {nameof(T)}"));
+ }
+
+ public override void Write(ApplicationCommandOptionProperties properties, IParameterInfo parameterInfo)
+ {
+ var names = Enum.GetNames(typeof(T));
+ if (names.Length <= 25)
+ {
+ var choices = new List();
+
+ foreach (var name in names)
+ choices.Add(new ApplicationCommandOptionChoiceProperties
+ {
+ Name = name,
+ Value = name
+ });
+
+ properties.Choices = choices;
+ }
+ }
+}
diff --git a/docs/guides/int_framework/typeconverters.md b/docs/guides/int_framework/typeconverters.md
new file mode 100644
index 000000000..96bdcb906
--- /dev/null
+++ b/docs/guides/int_framework/typeconverters.md
@@ -0,0 +1,118 @@
+---
+uid: Guides.IntFw.TypeConverters
+title: Parameter Type Converters
+---
+
+# TypeConverters
+
+[TypeConverters] are responsible for registering command parameters to Discord and parsing the user inputs into method parameters.
+
+By default, TypeConverters for the following types are provided with @Discord.Interactions library.
+
+- Implementations of [IUser]
+- Implementations of [IChannel]
+- Implementations of [IRole]
+- Implementations of [IMentionable]
+- `string`
+- `float`, `double`, `decimal`
+- `bool`
+- `char`
+- `sbyte`, `byte`
+- `int16`, `int32`, `int64`
+- `uint16`, `uint32`, `uint64`
+- `enum`
+- `DateTime`
+- `TimeSpan`
+
+## Creating TypeConverters
+
+Depending on your needs, there are two types of TypeConverters you can create:
+
+- Concrete type
+- Generic type
+
+A valid converter must inherit [TypeConverter] base type. And override the abstract base methods.
+
+### CanConvertTo() Method
+
+This method is used by Interaction Service to search for alternative Type Converters.
+
+Interaction Services determines the most suitable [TypeConverter] for a parameter type in the following order:
+
+1. It searches for a [TypeConverter] that is registered to specifically target that parameter type
+2. It searches for a [TypeConverter] that returns `true` when its `CanConvertTo()` method is invoked for thaty parameter type.
+3. It searches for a generic `TypeConverter` with a matching type constraint. If there are more multiple matches,
+the one whose type constraint is the most specialized will be chosen.
+
+> [!NOTE}
+> Alternatively, you can use the generic variant (`TypeConverter`) of the
+> [TypeConverter] base class which implements the following method body for `CanConvertTo()` method
+
+```csharp
+public sealed override bool CanConvertTo (Type type) =>
+ typeof(T).IsAssignableFrom(type);
+```
+
+### GetDiscordType() Method
+
+This method is used by [InteractionService] to determine the
+[Discord Application Command Option type](https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-type)
+of a parameter type.
+
+### ReadAsync() Method
+
+This method is used by [InteractionService] to parse the user input.
+This method should return @Discord.Interactions.TypeConverterResult.FromSuccess* if the parsing operation is successful,
+otherwise it should return @Discord.Interactions.TypeConverterResult.FromError* .
+The inner logic of this method is totally up to you,
+however you should avoid using long running code.
+
+### Write() Method
+
+This method is used to configure the **Discord Application Command Option** before it gets registered to Discord.
+Command Option is configured by modifying the `ApplicationCommandOptionProperties` instance.
+
+> [!WARNING]
+> The default parameter building pipeline is isolated and will not be disturbed by the [TypeConverter] workflow.
+> But changes made in this method will override the values generated by the
+> [InteractionService] for a **Discord Application Command Option**.
+
+## Example Enum TypeConverter
+
+[!code-csharp[Enum Converter](samples/typeconverters/enum_converter.cs)]
+
+> [!IMPORTANT]
+> TypeConverters must be registered prior to module discovery.
+> If Interaction Service encounters a parameter type that doesn't belong to any of the
+> registered [TypeConverters] during this phase, it will throw an exception.
+
+## Concrete TypeConverters
+
+Registering Concrete TypeConverters are as simple as creating an instance of your custom converter and invoking `AddTypeConverter()` method.
+
+```csharp
+interactionService.AddTypeConverter(new StringArrayConverter());
+```
+
+## Generic TypeConverters
+
+To register a generic `TypeConverter`, you need to invoke the `AddGenericTypeConverter()` method of the Interaction Service class.
+You need to pass the type of your `TypeConverter` and a target base type to this method.
+
+For instance, to register the previously mentioned enum converter the following can be used:
+
+```csharp
+interactionService.AddGenericTypeConverter(typeof(EnumConverter<>));
+```
+
+Interaction service checks if the target base type satisfies the type constraints of the Generic `TypeConverter` class.
+
+> [!NOTE]
+> Dependencies of Generic TypeConverters are also resolved using the Dependency Injection pattern.
+
+[TypeConverter]: xref:Discord.Interactions.TypeConverter
+[InteractionService]: xref:Discord.Interactions.InteractionService
+[IChannel]: xref:Discord.IChannel
+[IRole]: xref:Discord.IRole
+[IUser]: xref:Discord.IUser
+[IMentionable]: xref:Discord.IMentionable
diff --git a/docs/guides/introduction/intro.md b/docs/guides/introduction/intro.md
index 0a4ca26e9..0bc1b90f6 100644
--- a/docs/guides/introduction/intro.md
+++ b/docs/guides/introduction/intro.md
@@ -23,7 +23,7 @@ in [our GitHub repository].
> Please note that you should *not* try to blindly copy paste
> the code. The examples are meant to be a template or a guide.
-[our GitHub repository]: https://github.com/RogueException/Discord.Net/tree/dev/samples
+[our GitHub repository]: https://github.com/discord-net/Discord.Net/
[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/
@@ -44,8 +44,5 @@ resources to get you started.
## 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
\ No newline at end of file
+Please visit us at our [Discord](https://discord.gg/dnet) server.
+Describe the problem in details to us, what you've tried and what you need help with.
diff --git a/docs/guides/commands/dependency-injection.md b/docs/guides/text_commands/dependency-injection.md
similarity index 85%
rename from docs/guides/commands/dependency-injection.md
rename to docs/guides/text_commands/dependency-injection.md
index 5dc5b02d2..3253643ef 100644
--- a/docs/guides/commands/dependency-injection.md
+++ b/docs/guides/text_commands/dependency-injection.md
@@ -1,14 +1,18 @@
---
-uid: Guides.Commands.DI
+uid: Guides.TextCommands.DI
title: Dependency Injection
---
# Dependency Injection
-The Command Service is bundled with a very barebone Dependency
+The Text 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.
+> [!WARNING]
+> If you were brought here from the Interaction Service guides,
+> make sure to replace all namespaces that imply `Discord.Commands` with `Discord.Interactions`
+
## Setup
1. Create a @Microsoft.Extensions.DependencyInjection.ServiceCollection.
@@ -44,4 +48,4 @@ manner.
[!code-csharp[Injection Modules](samples/dependency-injection/dependency_module.cs)]
[!code-csharp[Disallow Dependency Injection](samples/dependency-injection/dependency_module_noinject.cs)]
-[DontInjectAttribute]: xref:Discord.Commands.DontInjectAttribute
\ No newline at end of file
+[DontInjectAttribute]: xref:Discord.Commands.DontInjectAttribute
diff --git a/docs/guides/commands/intro.md b/docs/guides/text_commands/intro.md
similarity index 96%
rename from docs/guides/commands/intro.md
rename to docs/guides/text_commands/intro.md
index abe7065c1..6632c127a 100644
--- a/docs/guides/commands/intro.md
+++ b/docs/guides/text_commands/intro.md
@@ -1,9 +1,9 @@
---
-uid: Guides.Commands.Intro
-title: Introduction to Command Service
+uid: Guides.TextCommands.Intro
+title: Introduction to the Chat Command Service
---
-# The Command Service
+# The Text Command Service
[Discord.Commands](xref:Discord.Commands) provides an attribute-based
command parser.
@@ -134,7 +134,7 @@ 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
+The `Priority` attributes are sorted in descending order; the higher
priority will be called first.
### Command Context
@@ -187,7 +187,7 @@ service provider.
### Module Constructors
-Modules are constructed using @Guides.Commands.DI. Any parameters
+Modules are constructed using [Dependency Injection](xref:Guides.TextCommands.DI). Any parameters
that are placed in the Module's constructor must be injected into an
@System.IServiceProvider first.
diff --git a/docs/guides/commands/namedarguments.md b/docs/guides/text_commands/namedarguments.md
similarity index 98%
rename from docs/guides/commands/namedarguments.md
rename to docs/guides/text_commands/namedarguments.md
index 890a8463f..18281d664 100644
--- a/docs/guides/commands/namedarguments.md
+++ b/docs/guides/text_commands/namedarguments.md
@@ -1,5 +1,5 @@
---
-uid: Guides.Commands.NamedArguments
+uid: Guides.TextCommands.NamedArguments
title: Named Arguments
---
diff --git a/docs/guides/commands/post-execution.md b/docs/guides/text_commands/post-execution.md
similarity index 97%
rename from docs/guides/commands/post-execution.md
rename to docs/guides/text_commands/post-execution.md
index 782d256b2..49fe2f5f9 100644
--- a/docs/guides/commands/post-execution.md
+++ b/docs/guides/text_commands/post-execution.md
@@ -1,9 +1,9 @@
---
-uid: Guides.Commands.PostExecution
+uid: Guides.TextCommands.PostExecution
title: Post-command Execution Handling
---
-# Post-execution Handling for Commands
+# Post-execution Handling for Text Commands
When developing commands, you may want to consider building a
post-execution handling system so you can have finer control
@@ -117,4 +117,4 @@ of the command.
[CommandExecuted]: xref:Discord.Commands.CommandService.CommandExecuted
[ExecuteAsync]: xref:Discord.Commands.CommandService.ExecuteAsync*
[ExecuteResult]: xref:Discord.Commands.ExecuteResult
-[Command Guide]: xref:Guides.Commands.Intro
\ No newline at end of file
+[Command Guide]: xref:Guides.TextCommands.Intro
diff --git a/docs/guides/commands/preconditions.md b/docs/guides/text_commands/preconditions.md
similarity index 98%
rename from docs/guides/commands/preconditions.md
rename to docs/guides/text_commands/preconditions.md
index 8e8298b86..4be7ca2bb 100644
--- a/docs/guides/commands/preconditions.md
+++ b/docs/guides/text_commands/preconditions.md
@@ -1,5 +1,5 @@
---
-uid: Guides.Commands.Preconditions
+uid: Guides.TextCommands.Preconditions
title: Preconditions
---
diff --git a/docs/guides/commands/samples/dependency-injection/dependency_map_setup.cs b/docs/guides/text_commands/samples/dependency-injection/dependency_map_setup.cs
similarity index 100%
rename from docs/guides/commands/samples/dependency-injection/dependency_map_setup.cs
rename to docs/guides/text_commands/samples/dependency-injection/dependency_map_setup.cs
diff --git a/docs/guides/commands/samples/dependency-injection/dependency_module.cs b/docs/guides/text_commands/samples/dependency-injection/dependency_module.cs
similarity index 100%
rename from docs/guides/commands/samples/dependency-injection/dependency_module.cs
rename to docs/guides/text_commands/samples/dependency-injection/dependency_module.cs
diff --git a/docs/guides/commands/samples/dependency-injection/dependency_module_noinject.cs b/docs/guides/text_commands/samples/dependency-injection/dependency_module_noinject.cs
similarity index 100%
rename from docs/guides/commands/samples/dependency-injection/dependency_module_noinject.cs
rename to docs/guides/text_commands/samples/dependency-injection/dependency_module_noinject.cs
diff --git a/docs/guides/commands/samples/intro/command_handler.cs b/docs/guides/text_commands/samples/intro/command_handler.cs
similarity index 100%
rename from docs/guides/commands/samples/intro/command_handler.cs
rename to docs/guides/text_commands/samples/intro/command_handler.cs
diff --git a/docs/guides/commands/samples/intro/empty-module.cs b/docs/guides/text_commands/samples/intro/empty-module.cs
similarity index 100%
rename from docs/guides/commands/samples/intro/empty-module.cs
rename to docs/guides/text_commands/samples/intro/empty-module.cs
diff --git a/docs/guides/commands/samples/intro/groups.cs b/docs/guides/text_commands/samples/intro/groups.cs
similarity index 100%
rename from docs/guides/commands/samples/intro/groups.cs
rename to docs/guides/text_commands/samples/intro/groups.cs
diff --git a/docs/guides/commands/samples/intro/module.cs b/docs/guides/text_commands/samples/intro/module.cs
similarity index 100%
rename from docs/guides/commands/samples/intro/module.cs
rename to docs/guides/text_commands/samples/intro/module.cs
diff --git a/docs/guides/commands/samples/post-execution/command_exception_log.cs b/docs/guides/text_commands/samples/post-execution/command_exception_log.cs
similarity index 100%
rename from docs/guides/commands/samples/post-execution/command_exception_log.cs
rename to docs/guides/text_commands/samples/post-execution/command_exception_log.cs
diff --git a/docs/guides/commands/samples/post-execution/command_executed_adv_demo.cs b/docs/guides/text_commands/samples/post-execution/command_executed_adv_demo.cs
similarity index 100%
rename from docs/guides/commands/samples/post-execution/command_executed_adv_demo.cs
rename to docs/guides/text_commands/samples/post-execution/command_executed_adv_demo.cs
diff --git a/docs/guides/commands/samples/post-execution/command_executed_demo.cs b/docs/guides/text_commands/samples/post-execution/command_executed_demo.cs
similarity index 100%
rename from docs/guides/commands/samples/post-execution/command_executed_demo.cs
rename to docs/guides/text_commands/samples/post-execution/command_executed_demo.cs
diff --git a/docs/guides/commands/samples/post-execution/customresult_base.cs b/docs/guides/text_commands/samples/post-execution/customresult_base.cs
similarity index 100%
rename from docs/guides/commands/samples/post-execution/customresult_base.cs
rename to docs/guides/text_commands/samples/post-execution/customresult_base.cs
diff --git a/docs/guides/commands/samples/post-execution/customresult_extended.cs b/docs/guides/text_commands/samples/post-execution/customresult_extended.cs
similarity index 100%
rename from docs/guides/commands/samples/post-execution/customresult_extended.cs
rename to docs/guides/text_commands/samples/post-execution/customresult_extended.cs
diff --git a/docs/guides/commands/samples/post-execution/customresult_usage.cs b/docs/guides/text_commands/samples/post-execution/customresult_usage.cs
similarity index 100%
rename from docs/guides/commands/samples/post-execution/customresult_usage.cs
rename to docs/guides/text_commands/samples/post-execution/customresult_usage.cs
diff --git a/docs/guides/commands/samples/post-execution/post-execution_basic.cs b/docs/guides/text_commands/samples/post-execution/post-execution_basic.cs
similarity index 100%
rename from docs/guides/commands/samples/post-execution/post-execution_basic.cs
rename to docs/guides/text_commands/samples/post-execution/post-execution_basic.cs
diff --git a/docs/guides/text_commands/samples/preconditions/group_precondition.cs b/docs/guides/text_commands/samples/preconditions/group_precondition.cs
new file mode 100644
index 000000000..bae102b9a
--- /dev/null
+++ b/docs/guides/text_commands/samples/preconditions/group_precondition.cs
@@ -0,0 +1,9 @@
+// The following example only requires the user to either have the
+// Administrator permission in this guild or own the bot application.
+[RequireUserPermission(GuildPermission.Administrator, Group = "Permission")]
+[RequireOwner(Group = "Permission")]
+public class AdminModule : ModuleBase
+{
+ [Command("ban")]
+ public Task BanAsync(IUser user) => Context.Guild.AddBanAsync(user);
+}
\ No newline at end of file
diff --git a/docs/guides/commands/samples/preconditions/precondition_usage.cs b/docs/guides/text_commands/samples/preconditions/precondition_usage.cs
similarity index 100%
rename from docs/guides/commands/samples/preconditions/precondition_usage.cs
rename to docs/guides/text_commands/samples/preconditions/precondition_usage.cs
diff --git a/docs/guides/commands/samples/preconditions/require_role.cs b/docs/guides/text_commands/samples/preconditions/require_role.cs
similarity index 100%
rename from docs/guides/commands/samples/preconditions/require_role.cs
rename to docs/guides/text_commands/samples/preconditions/require_role.cs
diff --git a/docs/guides/commands/samples/typereaders/typereader-register.cs b/docs/guides/text_commands/samples/typereaders/typereader-register.cs
similarity index 100%
rename from docs/guides/commands/samples/typereaders/typereader-register.cs
rename to docs/guides/text_commands/samples/typereaders/typereader-register.cs
diff --git a/docs/guides/commands/samples/typereaders/typereader.cs b/docs/guides/text_commands/samples/typereaders/typereader.cs
similarity index 100%
rename from docs/guides/commands/samples/typereaders/typereader.cs
rename to docs/guides/text_commands/samples/typereaders/typereader.cs
diff --git a/docs/guides/commands/typereaders.md b/docs/guides/text_commands/typereaders.md
similarity index 97%
rename from docs/guides/commands/typereaders.md
rename to docs/guides/text_commands/typereaders.md
index f942c9341..bf911dac7 100644
--- a/docs/guides/commands/typereaders.md
+++ b/docs/guides/text_commands/typereaders.md
@@ -1,5 +1,5 @@
---
-uid: Guides.Commands.TypeReaders
+uid: Guides.TextCommands.TypeReaders
title: Type Readers
---
@@ -67,4 +67,4 @@ To register a TypeReader, invoke [CommandService.AddTypeReader].
### Example - Adding a Type Reader
-[!code-csharp[Adding TypeReaders](samples/typereaders/typereader-register.cs)]
\ No newline at end of file
+[!code-csharp[Adding TypeReaders](samples/typereaders/typereader-register.cs)]
diff --git a/docs/guides/toc.yml b/docs/guides/toc.yml
index a6c38768f..cf4ea5516 100644
--- a/docs/guides/toc.yml
+++ b/docs/guides/toc.yml
@@ -1,12 +1,14 @@
- name: Introduction
topicUid: Guides.Introduction
+- name: V2 to V3 Guide
+ topicUid: Guides.V2V3Guide
- name: Getting Started
items:
- name: Installation
topicUid: Guides.GettingStarted.Installation
items:
- - name: Nightly Builds
- topicUid: Guides.GettingStarted.Installation.Nightlies
+ - name: Nightly builds
+ topicUid: Guides.GettingStarted.Installation.Labs
- name: Your First Bot
topicUid: Guides.GettingStarted.FirstBot
- name: Terminology
@@ -21,23 +23,83 @@
topicUid: Guides.Concepts.ManageConnections
- name: Entities
topicUid: Guides.Concepts.Entities
-- name: Working with Commands
+- name: Working with Text-based Commands
items:
- name: Introduction
- topicUid: Guides.Commands.Intro
+ topicUid: Guides.TextCommands.Intro
- name: TypeReaders
- topicUid: Guides.Commands.TypeReaders
+ topicUid: Guides.TextCommands.TypeReaders
- name: Named Arguments
- topicUid: Guides.Commands.NamedArguments
+ topicUid: Guides.TextCommands.NamedArguments
- name: Preconditions
- topicUid: Guides.Commands.Preconditions
+ topicUid: Guides.TextCommands.Preconditions
- name: Dependency Injection
- topicUid: Guides.Commands.DI
+ topicUid: Guides.TextCommands.DI
- name: Post-execution Handling
- topicUid: Guides.Commands.PostExecution
+ topicUid: Guides.TextCommands.PostExecution
+- name: Working with the Interaction Framework
+ items:
+ - name: Introduction
+ topicUid: Guides.IntFw.Intro
+ - name: Auto-Completion
+ topicUid: Guides.IntFw.AutoCompletion
+ - name: TypeConverters
+ topicUid: Guides.IntFw.TypeConverters
+ - name: Preconditions
+ topicUid: Guides.IntFw.Preconditions
+ - name: Dependency Injection
+ topicUid: Guides.IntFw.DI
+ - name: Post-execution Handling
+ topicUid: Guides.IntFw.PostExecution
+- name: Slash Command Basics
+ items:
+ - name: Introduction
+ topicUid: Guides.SlashCommands.Intro
+ - name: Creating slash commands
+ topicUid: Guides.SlashCommands.Creating
+ - name: Receiving and responding to slash commands
+ topicUid: Guides.SlashCommands.Receiving
+ - name: Slash command parameters
+ topicUid: Guides.SlashCommands.Parameters
+ - name: Ephemeral responses
+ topicUid: Guides.SlashCommands.Ephemeral
+ - name: Sub commands
+ topicUid: Guides.SlashCommands.SubCommand
+ - name: Slash command choices
+ topicUid: Guides.SlashCommands.Choices
+ - name: Slash commands Bulk Overwrites
+ topicUid: Guides.SlashCommands.BulkOverwrite
+- name: Context Command Basics
+ items:
+ - name: Creating Context Commands
+ topicUid: Guides.ContextCommands.Creating
+ - name: Receiving Context Commands
+ topicUid: Guides.ContextCommands.Reveiving
+- name: Message Component Basics
+ items:
+ - name: Introduction
+ topicUid: Guides.MessageComponents.Intro
+ - name: Responding to Components
+ topicUid: Guides.MessageComponents.Responding
+ - name: Buttons in depth
+ topicUid: Guides.MessageComponents.Buttons
+ - name: Select menus
+ topicUid: Guides.MessageComponents.SelectMenus
+ - name: Advanced Concepts
+ topicUid: Guides.MessageComponents.Advanced
+- name: Guild Events
+ items:
+ - name: Introduction
+ topicUid: Guides.GuildEvents.Intro
+ - name: Creating Events
+ topicUid: Guides.GuildEvents.Creating
+ - name: Getting Event Users
+ topicUid: Guides.GuildEvents.GettingUsers
+ - name: Modifying Events
+ topicUid: Guides.GuildEvents.Modifying
- name: Emoji
topicUid: Guides.Emoji
- name: Voice
topicUid: Guides.Voice.SendingVoice
- name: Deployment
- topicUid: Guides.Deployment
\ No newline at end of file
+ topicUid: Guides.Deployment
diff --git a/docs/guides/v2_v3_guide/v2_to_v3_guide.md b/docs/guides/v2_v3_guide/v2_to_v3_guide.md
new file mode 100644
index 000000000..915c4a57f
--- /dev/null
+++ b/docs/guides/v2_v3_guide/v2_to_v3_guide.md
@@ -0,0 +1,85 @@
+---
+uid: Guides.V2V3Guide
+title: V2 -> V3 Guide
+---
+
+# V2 to V3 Guide
+
+V3 is designed to be a more feature complete, more reliable,
+and more flexible library than any previous version.
+
+Below are the most notable breaking changes that you would need to update your code to work with V3.
+
+### GatewayIntents
+
+As Discord.NET has upgraded from Discord API v6 to API v9,
+`GatewayIntents` must now be specified in the socket config, as well as on the [developer portal].
+
+```cs
+
+// Where ever you declared your websocket client.
+DiscordSocketClient _client;
+
+...
+
+var config = new DiscordSocketConfig()
+{
+ .. // Other config options can be presented here.
+ GatewayIntents = GatewayIntents.All
+}
+
+_client = new DiscordSocketClient(config);
+
+```
+#### Common intents:
+
+- AllUnprivileged: This is a group of most common intents, that do NOT require any [developer portal] intents to be enabled.
+This includes intents that receive messages such as: `GatewayIntents.GuildMessages, GatewayIntents.DirectMessages`
+- GuildMembers: An intent disabled by default, as you need to enable it in the [developer portal].
+- GuildPresences: Also disabled by default, this intent together with `GuildMembers` are the only intents not included in `AllUnprivileged`.
+- All: All intents, it is ill adviced to use this without care, as it *can* cause a memory leak from presence.
+The library will give responsive warnings if you specify unnecessary intents.
+
+
+> [!NOTE]
+> All gateway intents, their Discord API counterpart and their enum value are listed
+> [HERE](xref:Discord.GatewayIntents)
+
+#### Stacking intents:
+
+It is common that you require several intents together.
+The example below shows how this can be done.
+
+```cs
+
+GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.GuildMembers | ..
+
+```
+
+> [!NOTE]
+> Further documentation on the ` | ` operator can be found
+> [HERE](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/bitwise-and-shift-operators)
+
+[developer portal]: https://discord.com/developers/
+
+### ReactionAdded Event
+
+The reaction added event has been changed to have both parameters cacheable.
+This allows you to download the channel and message if they aren't cached instead of them being null.
+
+### UserIsTyping Event
+
+The user is typing event has been changed to have both parameters cacheable.
+This allows you to download the user and channel if they aren't cached instead of them being null.
+
+### Presence
+
+There is a new event called `PresenceUpdated` that is called when a user's presence changes,
+instead of `GuildMemberUpdated` or `UserUpdated`.
+If your code relied on these events to get presence data then you need to update it to work with the new event.
+
+## Migrating your commands to slash command
+
+The new InteractionService was designed to act like the previous service for text-based commands.
+Your pre-existing code will continue to work, but you will need to migrate your modules and response functions to use the new
+InteractionService methods. Docs on this can be found in the Guides section.
diff --git a/docs/guides/voice/sending-voice.md b/docs/guides/voice/sending-voice.md
index 476f2f42e..555adbca2 100644
--- a/docs/guides/voice/sending-voice.md
+++ b/docs/guides/voice/sending-voice.md
@@ -18,7 +18,7 @@ when developing on .NET Core, this is where you execute `dotnet run`
from; typically the same directory as your csproj).
For Windows Users, precompiled binaries are available for your
-convienence [here](https://discord.foxbot.me/binaries/).
+convienence [here](https://github.com/discord-net/Discord.Net/tree/dev/voice-natives).
For Linux Users, you will need to compile [Sodium] and [Opus] from
source, or install them from your package manager.
diff --git a/docs/index.md b/docs/index.md
index 34350df70..32b0498ea 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -3,20 +3,20 @@ uid: Root.Landing
title: Home
---
-# Discord.Net Documentation
+# Discord.NET Documentation
-[](https://github.com/discord-net/Discord.Net)
+[](https://github.com/discord-net/Discord.Net)
[](https://www.nuget.org/packages/Discord.Net)
[](https://www.myget.org/feed/Packages/discord-net)
[](https://dev.azure.com/discord-net/Discord.Net/_build/latest?definitionId=1&branchName=dev)
-[](https://discord.gg/jkrBmQR)
+[](https://discord.gg/dnet)
-## What is Discord.Net?
+## What is Discord.NET?
Discord.Net is an asynchronous, multi-platform .NET Library used to
-interface with the [Discord API](https://discordapp.com/).
+interface with the [Discord API](https://discord.com/).
## Where to begin?
@@ -26,3 +26,57 @@ If this is your first time using Discord.Net, you should refer to the
More experienced users might want to refer to the
[API Documentation](xref:API.Docs) for a breakdown of the individual
objects in the library.
+
+## Nightlies
+
+Nightlies are builds of Discord.NET that are still in an experimental phase, and have not been released.
+These are not included in the main repository, and are instead taken over by [Discord.NET Labs].
+
+Discord.NET Labs is an experimental fork of Discord.NET that implements the newest discord features
+for testing and development to eventually get merged into Discord.NET.
+
+[Installing Discord.NET Labs](xref:Guides.GettingStarted.Installation.Labs)
+
+[Discord.NET Labs]: https://github.com/Discord-Net-Labs/Discord.Net-Labs
+
+## Questions?
+
+Frequently asked questions are covered in the
+FAQ. Read it thoroughly because most common questions are already answered there.
+
+If you still have unanswered questions after reading the [FAQ](xref:FAQ.Basics.GetStarted), further support is available on
+[Discord](https://discord.gg/dnet).
+
+## Commonly used features
+
+#### Interaction Framework
+
+A counterpart to staple command service of Discord.NET, the Interaction Framework implements the same
+feature-rich structure to register & handle interactions like Slash commands & buttons.
+
+- Read about the Interaction Framework
+ [here](xref:Guides.IntFw.Intro)
+
+#### Slash Commands
+
+Slash commands are purposed to take over the normal prefixed commands in Discord and comes with good functionality to serve as a replacement.
+Being interactions, they are handled as SocketInteractions. Creating and receiving slashcommands is covered below.
+
+- Find out more about slash commands in the
+ [Slash Command Guides](xref:Guides.SlashCommands.Intro)
+
+#### Context Message & User Ccommands
+
+These commands can be pointed at messages and users, in custom application tabs.
+Being interactions as well, they are able to be handled just like slash commands. They do not have options however.
+
+- Learn how to create and handle these commands in the
+ [Context Command Guides](xref:Guides.ContextCommands.Creating)
+
+#### Message Components
+
+Components of a message such as buttons and dropdowns, which can be interacted with and responded to.
+Message components can be set in rows and multiple can exist on a single message!
+
+- Explanation on how to add & respond to message components can be found in the
+ [Message Component Guides](xref:Guides.MessageComponents.Intro)
diff --git a/docs/marketing/logo/SVG/Combinationmark White Border.svg b/docs/marketing/logo/SVG/Combinationmark White Border.svg
new file mode 100644
index 000000000..787803d4b
--- /dev/null
+++ b/docs/marketing/logo/SVG/Combinationmark White Border.svg
@@ -0,0 +1,21 @@
+
+
+
+
+
+ Combinationmark White
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/toc.yml b/docs/toc.yml
index bea010c5a..810995383 100644
--- a/docs/toc.yml
+++ b/docs/toc.yml
@@ -8,4 +8,4 @@
href: api/
topicUid: API.Docs
- name: Changelog
- topicHref: ../CHANGELOG.md
\ No newline at end of file
+ topicHref: ../CHANGELOG.md
diff --git a/samples/01_basic_ping_bot/01_basic_ping_bot.csproj b/samples/01_basic_ping_bot/01_basic_ping_bot.csproj
index 4b4e35e3f..6e1a6365f 100644
--- a/samples/01_basic_ping_bot/01_basic_ping_bot.csproj
+++ b/samples/01_basic_ping_bot/01_basic_ping_bot.csproj
@@ -2,7 +2,7 @@
Exe
- netcoreapp3.0
+ net5.0
diff --git a/samples/02_commands_framework/02_commands_framework.csproj b/samples/02_commands_framework/02_commands_framework.csproj
index 84b30aa99..30c25e846 100644
--- a/samples/02_commands_framework/02_commands_framework.csproj
+++ b/samples/02_commands_framework/02_commands_framework.csproj
@@ -2,11 +2,11 @@
Exe
- netcoreapp3.0
+ net5.0
-
+
diff --git a/samples/02_commands_framework/Modules/PublicModule.cs b/samples/02_commands_framework/Modules/PublicModule.cs
index b9263649f..18423f609 100644
--- a/samples/02_commands_framework/Modules/PublicModule.cs
+++ b/samples/02_commands_framework/Modules/PublicModule.cs
@@ -31,7 +31,7 @@ namespace _02_commands_framework.Modules
[Command("userinfo")]
public async Task UserInfoAsync(IUser user = null)
{
- user = user ?? Context.User;
+ user ??= Context.User;
await ReplyAsync(user.ToString());
}
diff --git a/samples/02_commands_framework/Program.cs b/samples/02_commands_framework/Program.cs
index 67cb87764..8a2f37dce 100644
--- a/samples/02_commands_framework/Program.cs
+++ b/samples/02_commands_framework/Program.cs
@@ -39,7 +39,7 @@ namespace _02_commands_framework
services.GetRequiredService().Log += LogAsync;
// Tokens should be considered secret data and never hard-coded.
- // We can read from the environment variable to avoid hardcoding.
+ // We can read from the environment variable to avoid hard coding.
await client.LoginAsync(TokenType.Bot, Environment.GetEnvironmentVariable("token"));
await client.StartAsync();
diff --git a/samples/03_sharded_client/03_sharded_client.csproj b/samples/03_sharded_client/03_sharded_client.csproj
index a6599c117..c4c42516e 100644
--- a/samples/03_sharded_client/03_sharded_client.csproj
+++ b/samples/03_sharded_client/03_sharded_client.csproj
@@ -2,12 +2,12 @@
Exe
- netcoreapp3.0
+ net5.0
_03_sharded_client
-
+
diff --git a/samples/03_sharded_client/Modules/PublicModule.cs b/samples/03_sharded_client/Modules/PublicModule.cs
index 60e57563a..fad2ba98c 100644
--- a/samples/03_sharded_client/Modules/PublicModule.cs
+++ b/samples/03_sharded_client/Modules/PublicModule.cs
@@ -9,7 +9,7 @@ namespace _03_sharded_client.Modules
[Command("info")]
public async Task InfoAsync()
{
- var msg = $@"Hi {Context.User}! There are currently {Context.Client.Shards} shards!
+ var msg = $@"Hi {Context.User}! There are currently {Context.Client.Shards.Count} shards!
This guild is being served by shard number {Context.Client.GetShardFor(Context.Guild).ShardId}";
await ReplyAsync(msg);
}
diff --git a/samples/03_sharded_client/Services/CommandHandlingService.cs b/samples/03_sharded_client/Services/CommandHandlingService.cs
index 1230cbcff..adc91b12c 100644
--- a/samples/03_sharded_client/Services/CommandHandlingService.cs
+++ b/samples/03_sharded_client/Services/CommandHandlingService.cs
@@ -54,7 +54,7 @@ namespace _03_sharded_client.Services
if (!command.IsSpecified)
return;
- // the command was succesful, we don't care about this result, unless we want to log that a command succeeded.
+ // the command was successful, we don't care about this result, unless we want to log that a command succeeded.
if (result.IsSuccess)
return;
diff --git a/samples/04_interactions_framework/04_interactions_framework.csproj b/samples/04_interactions_framework/04_interactions_framework.csproj
new file mode 100644
index 000000000..780ab69bd
--- /dev/null
+++ b/samples/04_interactions_framework/04_interactions_framework.csproj
@@ -0,0 +1,25 @@
+
+
+
+ Exe
+ net5.0
+ _04_interactions_framework
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/04_interactions_framework/CommandHandler.cs b/samples/04_interactions_framework/CommandHandler.cs
new file mode 100644
index 000000000..735557da5
--- /dev/null
+++ b/samples/04_interactions_framework/CommandHandler.cs
@@ -0,0 +1,146 @@
+using Discord;
+using Discord.Interactions;
+using Discord.WebSocket;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace _04_interactions_framework
+{
+ public class CommandHandler
+ {
+ private readonly DiscordSocketClient _client;
+ private readonly InteractionService _commands;
+ private readonly IServiceProvider _services;
+
+ public CommandHandler(DiscordSocketClient client, InteractionService commands, IServiceProvider services)
+ {
+ _client = client;
+ _commands = commands;
+ _services = services;
+ }
+
+ public async Task InitializeAsync ( )
+ {
+ // Add the public modules that inherit InteractionModuleBase to the InteractionService
+ await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
+
+ // Process the InteractionCreated payloads to execute Interactions commands
+ _client.InteractionCreated += HandleInteraction;
+
+ // Process the command execution results
+ _commands.SlashCommandExecuted += SlashCommandExecuted;
+ _commands.ContextCommandExecuted += ContextCommandExecuted;
+ _commands.ComponentCommandExecuted += ComponentCommandExecuted;
+ }
+
+ private Task ComponentCommandExecuted (ComponentCommandInfo arg1, Discord.IInteractionContext arg2, IResult arg3)
+ {
+ if (!arg3.IsSuccess)
+ {
+ switch (arg3.Error)
+ {
+ case InteractionCommandError.UnmetPrecondition:
+ // implement
+ break;
+ case InteractionCommandError.UnknownCommand:
+ // implement
+ break;
+ case InteractionCommandError.BadArgs:
+ // implement
+ break;
+ case InteractionCommandError.Exception:
+ // implement
+ break;
+ case InteractionCommandError.Unsuccessful:
+ // implement
+ break;
+ default:
+ break;
+ }
+ }
+
+ return Task.CompletedTask;
+ }
+
+ private Task ContextCommandExecuted (ContextCommandInfo arg1, Discord.IInteractionContext arg2, IResult arg3)
+ {
+ if (!arg3.IsSuccess)
+ {
+ switch (arg3.Error)
+ {
+ case InteractionCommandError.UnmetPrecondition:
+ // implement
+ break;
+ case InteractionCommandError.UnknownCommand:
+ // implement
+ break;
+ case InteractionCommandError.BadArgs:
+ // implement
+ break;
+ case InteractionCommandError.Exception:
+ // implement
+ break;
+ case InteractionCommandError.Unsuccessful:
+ // implement
+ break;
+ default:
+ break;
+ }
+ }
+
+ return Task.CompletedTask;
+ }
+
+ private Task SlashCommandExecuted (SlashCommandInfo arg1, Discord.IInteractionContext arg2, IResult arg3)
+ {
+ if (!arg3.IsSuccess)
+ {
+ switch (arg3.Error)
+ {
+ case InteractionCommandError.UnmetPrecondition:
+ // implement
+ break;
+ case InteractionCommandError.UnknownCommand:
+ // implement
+ break;
+ case InteractionCommandError.BadArgs:
+ // implement
+ break;
+ case InteractionCommandError.Exception:
+ // implement
+ break;
+ case InteractionCommandError.Unsuccessful:
+ // implement
+ break;
+ default:
+ break;
+ }
+ }
+
+ return Task.CompletedTask;
+ }
+
+ private async Task HandleInteraction (SocketInteraction arg)
+ {
+ try
+ {
+ // Create an execution context that matches the generic type parameter of your InteractionModuleBase modules
+ var ctx = new SocketInteractionContext(_client, arg);
+ await _commands.ExecuteCommandAsync(ctx, _services);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine(ex);
+
+ // If a Slash Command execution fails it is most likely that the original interaction acknowledgement will persist. It is a good idea to delete the original
+ // response, or at least let the user know that something went wrong during the command execution.
+ if(arg.Type == InteractionType.ApplicationCommand)
+ await arg.GetOriginalResponseAsync().ContinueWith(async (msg) => await msg.Result.DeleteAsync());
+ }
+ }
+ }
+}
diff --git a/samples/04_interactions_framework/ExampleEnum.cs b/samples/04_interactions_framework/ExampleEnum.cs
new file mode 100644
index 000000000..2ea5733c0
--- /dev/null
+++ b/samples/04_interactions_framework/ExampleEnum.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace _04_interactions_framework
+{
+ public enum ExampleEnum
+ {
+ First,
+ Second,
+ Third,
+ Fourth
+ }
+}
diff --git a/samples/04_interactions_framework/Modules/UtilityModule.cs b/samples/04_interactions_framework/Modules/UtilityModule.cs
new file mode 100644
index 000000000..d6cbb1a9f
--- /dev/null
+++ b/samples/04_interactions_framework/Modules/UtilityModule.cs
@@ -0,0 +1,93 @@
+using Discord;
+using Discord.Interactions;
+using Discord.WebSocket;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace _04_interactions_framework.Modules
+{
+ // Interation modules must be public and inherit from an IInterationModuleBase
+ public class UtilityModule : InteractionModuleBase
+ {
+ // Dependencies can be accessed through Property injection, public properties with public setters will be set by the service provider
+ public InteractionService Commands { get; set; }
+
+ private CommandHandler _handler;
+
+ // Constructor injection is also a valid way to access the dependecies
+ public UtilityModule ( CommandHandler handler )
+ {
+ _handler = handler;
+ }
+
+ // Slash Commands are declared using the [SlashCommand], you need to provide a name and a description, both following the Discord guidelines
+ [SlashCommand("ping", "Recieve a pong")]
+ // By setting the DefaultPermission to false, you can disable the command by default. No one can use the command until you give them permission
+ [DefaultPermission(false)]
+ public async Task Ping ( )
+ {
+ await RespondAsync("pong");
+ }
+
+ // You can use a number of parameter types in you Slash Command handlers (string, int, double, bool, IUser, IChannel, IMentionable, IRole, Enums) by default. Optionally,
+ // you can implement your own TypeConverters to support a wider range of parameter types. For more information, refer to the library documentation.
+ // Optional method parameters(parameters with a default value) also will be displayed as optional on Discord.
+
+ // [Summary] lets you customize the name and the description of a parameter
+ [SlashCommand("echo", "Repeat the input")]
+ public async Task Echo(string echo, [Summary(description: "mention the user")]bool mention = false)
+ {
+ await RespondAsync(echo + (mention ? Context.User.Mention : string.Empty));
+ }
+
+ // [Group] will create a command group. [SlashCommand]s and [ComponentInteraction]s will be registered with the group prefix
+ [Group("test_group", "This is a command group")]
+ public class GroupExample : InteractionModuleBase
+ {
+ // You can create command choices either by using the [Choice] attribute or by creating an enum. Every enum with 25 or less values will be registered as a multiple
+ // choice option
+ [SlashCommand("choice_example", "Enums create choices")]
+ public async Task ChoiceExample(ExampleEnum input)
+ {
+ await RespondAsync(input.ToString());
+ }
+ }
+
+ // User Commands can only have one parameter, which must be a type of SocketUser
+ [UserCommand("SayHello")]
+ public async Task SayHello(IUser user)
+ {
+ await RespondAsync($"Hello, {user.Mention}");
+ }
+
+ // Message Commands can only have one parameter, which must be a type of SocketMessage
+ [MessageCommand("Delete")]
+ [RequireOwner]
+ public async Task DeleteMesage(IMessage message)
+ {
+ await message.DeleteAsync();
+ await RespondAsync("Deleted message.");
+ }
+
+ // Use [ComponentInteraction] to handle message component interactions. Message component interaction with the matching customId will be executed.
+ // Alternatively, you can create a wild card pattern using the '*' character. Interaction Service will perform a lazy regex search and capture the matching strings.
+ // You can then access these capture groups from the method parameters, in the order they were captured. Using the wild card pattern, you can cherry pick component interactions.
+ [ComponentInteraction("musicSelect:*,*")]
+ public async Task ButtonPress(string id, string name)
+ {
+ // ...
+ await RespondAsync($"Playing song: {name}/{id}");
+ }
+
+ // Select Menu interactions, contain ids of the menu options that were selected by the user. You can access the option ids from the method parameters.
+ // You can also use the wild card pattern with Select Menus, in that case, the wild card captures will be passed on to the method first, followed by the option ids.
+ [ComponentInteraction("roleSelect")]
+ public async Task RoleSelect(params string[] selections)
+ {
+ // implement
+ }
+ }
+}
diff --git a/samples/04_interactions_framework/Program.cs b/samples/04_interactions_framework/Program.cs
new file mode 100644
index 000000000..5dedbfae9
--- /dev/null
+++ b/samples/04_interactions_framework/Program.cs
@@ -0,0 +1,85 @@
+using Discord;
+using Discord.Interactions;
+using Discord.WebSocket;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace _04_interactions_framework
+{
+ class Program
+ {
+ static void Main ( string[] args )
+ {
+ // One of the more flexable ways to access the configuration data is to use the Microsoft's Configuration model,
+ // this way we can avoid hard coding the environment secrets. I opted to use the Json and environment variable providers here.
+ IConfiguration config = new ConfigurationBuilder()
+ .AddEnvironmentVariables(prefix: "DC_")
+ .AddJsonFile("appsettings.json", optional: true)
+ .Build();
+
+ RunAsync(config).GetAwaiter().GetResult();
+ }
+
+ static async Task RunAsync (IConfiguration configuration )
+ {
+ // Dependency injection is a key part of the Interactions framework but it needs to be disposed at the end of the app's lifetime.
+ using var services = ConfigureServices(configuration);
+
+ var client = services.GetRequiredService();
+ var commands = services.GetRequiredService();
+
+ client.Log += LogAsync;
+ commands.Log += LogAsync;
+
+ // Slash Commands and Context Commands are can be automatically registered, but this process needs to happen after the client enters the READY state.
+ // Since Global Commands take around 1 hour to register, we should use a test guild to instantly update and test our commands. To determine the method we should
+ // register the commands with, we can check whether we are in a DEBUG environment and if we are, we can register the commands to a predetermined test guild.
+ client.Ready += async ( ) =>
+ {
+ if (IsDebug())
+ // Id of the test guild can be provided from the Configuration object
+ await commands.RegisterCommandsToGuildAsync(configuration.GetValue("testGuild"), true);
+ else
+ await commands.RegisterCommandsGloballyAsync(true);
+ };
+
+ // Here we can initialize the service that will register and execute our commands
+ await services.GetRequiredService().InitializeAsync();
+
+ // Bot token can be provided from the Configuration object we set up earlier
+ await client.LoginAsync(TokenType.Bot, configuration["token"]);
+ await client.StartAsync();
+
+ await Task.Delay(Timeout.Infinite);
+ }
+
+ static Task LogAsync(LogMessage message)
+ {
+ Console.WriteLine(message.ToString());
+ return Task.CompletedTask;
+ }
+
+ static ServiceProvider ConfigureServices ( IConfiguration configuration )
+ {
+ return new ServiceCollection()
+ .AddSingleton(configuration)
+ .AddSingleton()
+ .AddSingleton(x => new InteractionService(x.GetRequiredService()))
+ .AddSingleton()
+ .BuildServiceProvider();
+ }
+
+ static bool IsDebug ( )
+ {
+ #if DEBUG
+ return true;
+ #else
+ return false;
+ #endif
+ }
+ }
+}
diff --git a/samples/04_interactions_framework/RequireOwnerAttribute.cs b/samples/04_interactions_framework/RequireOwnerAttribute.cs
new file mode 100644
index 000000000..2f2493838
--- /dev/null
+++ b/samples/04_interactions_framework/RequireOwnerAttribute.cs
@@ -0,0 +1,27 @@
+using Discord;
+using Discord.Interactions;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace _04_interactions_framework
+{
+ public class RequireOwnerAttribute : PreconditionAttribute
+ {
+ public override async Task CheckRequirementsAsync (IInteractionContext context, ICommandInfo commandInfo, IServiceProvider services)
+ {
+ switch (context.Client.TokenType)
+ {
+ case TokenType.Bot:
+ var application = await context.Client.GetApplicationInfoAsync().ConfigureAwait(false);
+ if (context.User.Id != application.Owner.Id)
+ return PreconditionResult.FromError(ErrorMessage ?? "Command can only be run by the owner of the bot.");
+ return PreconditionResult.FromSuccess();
+ default:
+ return PreconditionResult.FromError($"{nameof(RequireOwnerAttribute)} is not supported by this {nameof(TokenType)}.");
+ }
+ }
+ }
+}
diff --git a/samples/04_webhook_client/Program.cs b/samples/04_webhook_client/Program.cs
index c2e5faa03..f3a50036c 100644
--- a/samples/04_webhook_client/Program.cs
+++ b/samples/04_webhook_client/Program.cs
@@ -14,10 +14,10 @@ namespace _04_webhook_client
public async Task MainAsync()
{
- // The webhook url follows the format https://discordapp.com/api/webhooks/{id}/{token}
+ // The webhook url follows the format https://discord.com/api/webhooks/{id}/{token}
// Because anyone with the webhook URL can use your webhook
- // you should NOT hard code the URL or ID + token into your application.
- using (var client = new DiscordWebhookClient("https://discordapp.com/api/webhooks/123/abc123"))
+ // you should NOT hard code the URL or ID + token into your application.
+ using (var client = new DiscordWebhookClient("https://discord.com/api/webhooks/123/abc123"))
{
var embed = new EmbedBuilder
{
@@ -26,7 +26,7 @@ namespace _04_webhook_client
};
// Webhooks are able to send multiple embeds per message
- // As such, your embeds must be passed as a collection.
+ // As such, your embeds must be passed as a collection.
await client.SendMessageAsync(text: "Send a message to this webhook!", embeds: new[] { embed.Build() });
}
}
diff --git a/samples/idn/Inspector.cs b/samples/idn/Inspector.cs
index 3806e0e79..1544c8d07 100644
--- a/samples/idn/Inspector.cs
+++ b/samples/idn/Inspector.cs
@@ -3,7 +3,7 @@ using System.Linq;
using System.Reflection;
using System.Text;
-namespace idn
+namespace Idn
{
public static class Inspector
{
diff --git a/samples/idn/Program.cs b/samples/idn/Program.cs
index ffd8fd1af..abc315a2d 100644
--- a/samples/idn/Program.cs
+++ b/samples/idn/Program.cs
@@ -13,7 +13,7 @@ using System.Threading;
using System.Text;
using System.Diagnostics;
-namespace idn
+namespace Idn
{
public class Program
{
diff --git a/samples/idn/idn.csproj b/samples/idn/idn.csproj
index 984c86383..fafb3df3f 100644
--- a/samples/idn/idn.csproj
+++ b/samples/idn/idn.csproj
@@ -2,11 +2,11 @@
Exe
- netcoreapp3.1
+ net5.0
-
+
diff --git a/src/Discord.Net.Analyzers/Discord.Net.Analyzers.csproj b/src/Discord.Net.Analyzers/Discord.Net.Analyzers.csproj
index 1b2ee45bf..5fe98fc86 100644
--- a/src/Discord.Net.Analyzers/Discord.Net.Analyzers.csproj
+++ b/src/Discord.Net.Analyzers/Discord.Net.Analyzers.csproj
@@ -1,4 +1,4 @@
-
+
Discord.Net.Analyzers
@@ -7,7 +7,7 @@
netstandard2.0;netstandard2.1
-
+
diff --git a/src/Discord.Net.Commands/Attributes/AliasAttribute.cs b/src/Discord.Net.Commands/Attributes/AliasAttribute.cs
index 16eb3ba73..c4b78f534 100644
--- a/src/Discord.Net.Commands/Attributes/AliasAttribute.cs
+++ b/src/Discord.Net.Commands/Attributes/AliasAttribute.cs
@@ -16,7 +16,7 @@ namespace Discord.Commands
///
/// [Command("stats")]
/// [Alias("stat", "info")]
- /// public async Task GetStatsAsync(IUser user)
+ /// public Task GetStatsAsync(IUser user)
/// {
/// // ...pull stats
/// }
diff --git a/src/Discord.Net.Commands/Builders/CommandBuilder.cs b/src/Discord.Net.Commands/Builders/CommandBuilder.cs
index 3f1ca883a..1d946a33d 100644
--- a/src/Discord.Net.Commands/Builders/CommandBuilder.cs
+++ b/src/Discord.Net.Commands/Builders/CommandBuilder.cs
@@ -7,6 +7,7 @@ namespace Discord.Commands.Builders
{
public class CommandBuilder
{
+ #region CommandBuilder
private readonly List _preconditions;
private readonly List _parameters;
private readonly List _attributes;
@@ -27,8 +28,9 @@ namespace Discord.Commands.Builders
public IReadOnlyList Parameters => _parameters;
public IReadOnlyList Attributes => _attributes;
public IReadOnlyList Aliases => _aliases;
+ #endregion
- //Automatic
+ #region Automatic
internal CommandBuilder(ModuleBuilder module)
{
Module = module;
@@ -38,7 +40,9 @@ namespace Discord.Commands.Builders
_attributes = new List();
_aliases = new List();
}
- //User-defined
+ #endregion
+
+ #region User-defined
internal CommandBuilder(ModuleBuilder module, string primaryAlias, Func callback)
: this(module)
{
@@ -132,7 +136,7 @@ namespace Discord.Commands.Builders
var firstMultipleParam = _parameters.FirstOrDefault(x => x.IsMultiple);
if ((firstMultipleParam != null) && (firstMultipleParam != lastParam))
throw new InvalidOperationException($"Only the last parameter in a command may have the Multiple flag. Parameter: {firstMultipleParam.Name} in {PrimaryAlias}");
-
+
var firstRemainderParam = _parameters.FirstOrDefault(x => x.IsRemainder);
if ((firstRemainderParam != null) && (firstRemainderParam != lastParam))
throw new InvalidOperationException($"Only the last parameter in a command may have the Remainder flag. Parameter: {firstRemainderParam.Name} in {PrimaryAlias}");
@@ -140,5 +144,6 @@ namespace Discord.Commands.Builders
return new CommandInfo(this, info, service);
}
+ #endregion
}
}
diff --git a/src/Discord.Net.Commands/Builders/ModuleBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleBuilder.cs
index 6dc50db31..f2a7eeb1f 100644
--- a/src/Discord.Net.Commands/Builders/ModuleBuilder.cs
+++ b/src/Discord.Net.Commands/Builders/ModuleBuilder.cs
@@ -7,6 +7,8 @@ namespace Discord.Commands.Builders
{
public class ModuleBuilder
{
+ #region ModuleBuilder
+ private string _group;
private readonly List _commands;
private readonly List _submodules;
private readonly List _preconditions;
@@ -18,7 +20,14 @@ namespace Discord.Commands.Builders
public string Name { get; set; }
public string Summary { get; set; }
public string Remarks { get; set; }
- public string Group { get; set; }
+ public string Group { get => _group;
+ set
+ {
+ _aliases.Remove(_group);
+ _group = value;
+ AddAliases(value);
+ }
+ }
public IReadOnlyList Commands => _commands;
public IReadOnlyList Modules => _submodules;
@@ -27,8 +36,9 @@ namespace Discord.Commands.Builders
public IReadOnlyList Aliases => _aliases;
internal TypeInfo TypeInfo { get; set; }
+ #endregion
- //Automatic
+ #region Automatic
internal ModuleBuilder(CommandService service, ModuleBuilder parent)
{
Service = service;
@@ -40,7 +50,9 @@ namespace Discord.Commands.Builders
_attributes = new List();
_aliases = new List();
}
- //User-defined
+ #endregion
+
+ #region User-defined
internal ModuleBuilder(CommandService service, ModuleBuilder parent, string primaryAlias)
: this(service, parent)
{
@@ -132,5 +144,6 @@ namespace Discord.Commands.Builders
public ModuleInfo Build(CommandService service, IServiceProvider services) => BuildImpl(service, services);
internal ModuleInfo Build(CommandService service, IServiceProvider services, ModuleInfo parent) => BuildImpl(service, services, parent);
+ #endregion
}
}
diff --git a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs
index aec8dcbe3..22c58f5c7 100644
--- a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs
+++ b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs
@@ -116,9 +116,8 @@ namespace Discord.Commands
builder.AddAliases(alias.Aliases);
break;
case GroupAttribute group:
- builder.Name = builder.Name ?? group.Prefix;
+ builder.Name ??= group.Prefix;
builder.Group = group.Prefix;
- builder.AddAliases(group.Prefix);
break;
case PreconditionAttribute precondition:
builder.AddPrecondition(precondition);
@@ -135,7 +134,8 @@ namespace Discord.Commands
if (builder.Name == null)
builder.Name = typeInfo.Name;
- var validCommands = typeInfo.DeclaredMethods.Where(IsValidCommandDefinition);
+ // Get all methods (including from inherited members), that are valid commands
+ var validCommands = typeInfo.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Where(IsValidCommandDefinition);
foreach (var method in validCommands)
{
@@ -157,7 +157,7 @@ namespace Discord.Commands
case CommandAttribute command:
builder.AddAliases(command.Text);
builder.RunMode = command.RunMode;
- builder.Name = builder.Name ?? command.Text;
+ builder.Name ??= command.Text;
builder.IgnoreExtraArgs = command.IgnoreExtraArgs ?? service._ignoreExtraArgs;
break;
case NameAttribute name:
@@ -290,7 +290,7 @@ namespace Discord.Commands
return reader;
}
- //We dont have a cached type reader, create one
+ //We don't have a cached type reader, create one
reader = ReflectionUtils.CreateObject(typeReaderType.GetTypeInfo(), service, services);
service.AddTypeReader(paramType, reader, false);
diff --git a/src/Discord.Net.Commands/Builders/ParameterBuilder.cs b/src/Discord.Net.Commands/Builders/ParameterBuilder.cs
index 4ad5bfac0..afe3a5af6 100644
--- a/src/Discord.Net.Commands/Builders/ParameterBuilder.cs
+++ b/src/Discord.Net.Commands/Builders/ParameterBuilder.cs
@@ -8,6 +8,7 @@ namespace Discord.Commands.Builders
{
public class ParameterBuilder
{
+ #region ParameterBuilder
private readonly List _preconditions;
private readonly List _attributes;
@@ -24,8 +25,9 @@ namespace Discord.Commands.Builders
public IReadOnlyList Preconditions => _preconditions;
public IReadOnlyList Attributes => _attributes;
+#endregion
- //Automatic
+ #region Automatic
internal ParameterBuilder(CommandBuilder command)
{
_preconditions = new List();
@@ -33,7 +35,9 @@ namespace Discord.Commands.Builders
Command = command;
}
- //User-defined
+ #endregion
+
+ #region User-defined
internal ParameterBuilder(CommandBuilder command, string name, Type type)
: this(command)
{
@@ -50,7 +54,7 @@ namespace Discord.Commands.Builders
if (type.GetTypeInfo().IsValueType)
DefaultValue = Activator.CreateInstance(type);
else if (type.IsArray)
- type = ParameterType.GetElementType();
+ DefaultValue = Array.CreateInstance(type.GetElementType(), 0);
ParameterType = type;
}
@@ -127,10 +131,11 @@ namespace Discord.Commands.Builders
internal ParameterInfo Build(CommandInfo info)
{
- if ((TypeReader ?? (TypeReader = GetReader(ParameterType))) == null)
+ if ((TypeReader ??= GetReader(ParameterType)) == null)
throw new InvalidOperationException($"No type reader found for type {ParameterType.Name}, one must be specified");
return new ParameterInfo(this, info, Command.Module.Service);
}
+ #endregion
}
}
diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs
index 1d4b0e15a..fce67b9b2 100644
--- a/src/Discord.Net.Commands/CommandService.cs
+++ b/src/Discord.Net.Commands/CommandService.cs
@@ -29,6 +29,7 @@ namespace Discord.Commands
///