diff --git a/Discord.Net.sln b/Discord.Net.sln
index 245515c7c..75f0a4391 100644
--- a/Discord.Net.sln
+++ b/Discord.Net.sln
@@ -30,7 +30,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01_basic_ping_bot", "sample
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02_commands_framework", "samples\02_commands_framework\02_commands_framework.csproj", "{4E1F1F40-B1DD-40C9-A4B1-A2046A4C9C76}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "03_sharded_client", "samples\03_sharded_client\03_sharded_client.csproj", "{9B4C4AFB-3D15-44C6-9E36-12ED625AAA26}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "03_sharded_client", "samples\03_sharded_client\03_sharded_client.csproj", "{9B4C4AFB-3D15-44C6-9E36-12ED625AAA26}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{D1F0271E-0EE2-4B66-AC3D-9871B7E1C4CF}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Examples", "src\Discord.Net.Examples\Discord.Net.Examples.csproj", "{7EA96B2B-4D71-458D-9423-839362DC38BE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -174,6 +178,18 @@ Global
{9B4C4AFB-3D15-44C6-9E36-12ED625AAA26}.Release|x64.Build.0 = Release|Any CPU
{9B4C4AFB-3D15-44C6-9E36-12ED625AAA26}.Release|x86.ActiveCfg = Release|Any CPU
{9B4C4AFB-3D15-44C6-9E36-12ED625AAA26}.Release|x86.Build.0 = Release|Any CPU
+ {7EA96B2B-4D71-458D-9423-839362DC38BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7EA96B2B-4D71-458D-9423-839362DC38BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7EA96B2B-4D71-458D-9423-839362DC38BE}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {7EA96B2B-4D71-458D-9423-839362DC38BE}.Debug|x64.Build.0 = Debug|Any CPU
+ {7EA96B2B-4D71-458D-9423-839362DC38BE}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {7EA96B2B-4D71-458D-9423-839362DC38BE}.Debug|x86.Build.0 = Debug|Any CPU
+ {7EA96B2B-4D71-458D-9423-839362DC38BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7EA96B2B-4D71-458D-9423-839362DC38BE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7EA96B2B-4D71-458D-9423-839362DC38BE}.Release|x64.ActiveCfg = Release|Any CPU
+ {7EA96B2B-4D71-458D-9423-839362DC38BE}.Release|x64.Build.0 = Release|Any CPU
+ {7EA96B2B-4D71-458D-9423-839362DC38BE}.Release|x86.ActiveCfg = Release|Any CPU
+ {7EA96B2B-4D71-458D-9423-839362DC38BE}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -188,6 +204,7 @@ Global
{F2FF84FB-F6AD-47E5-9EE5-18206CAE136E} = {BB59D5B5-E7B0-4BF4-8F82-D14431B2799B}
{4E1F1F40-B1DD-40C9-A4B1-A2046A4C9C76} = {BB59D5B5-E7B0-4BF4-8F82-D14431B2799B}
{9B4C4AFB-3D15-44C6-9E36-12ED625AAA26} = {BB59D5B5-E7B0-4BF4-8F82-D14431B2799B}
+ {7EA96B2B-4D71-458D-9423-839362DC38BE} = {D1F0271E-0EE2-4B66-AC3D-9871B7E1C4CF}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D2404771-EEC8-45F2-9D71-F3373F6C1495}
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
index 8641d377e..74290e595 100644
--- a/docs/CONTRIBUTING.md
+++ b/docs/CONTRIBUTING.md
@@ -1,23 +1,46 @@
# Contributing to Docs
-## General Guidelines
+First of all, thank you for your interest in contributing to our
+documentation work. We really appreciate it! That being said,
+there are several guidelines you should attempt to follow when adding
+to/changing the documentation.
-We do not have any strict conditions for writing documentation,
-but keep these guidelines in mind:
+## General Guidelines
-* Keep code samples in the `guides/samples` folder
+* Keep code samples in each section's `samples` folder
* When referencing an object in the API, link to its page in the
API documentation
* Documentation should be written in an FAQ/Wiki-style format
* Documentation should be written in clear and proper English*
\* If anyone is interested in translating documentation into other
-languages, please open an issue or contact me on
-Discord (`foxbot#0282`).
+languages, please open an issue or contact `foxbot#0282` on
+Discord.
+
+## XML Docstrings Guidelines
-## Style Consistencies
+* When using the `` tag, use concise verbs. For example:
+
+```cs
+/// Gets or sets the guild user in this object.
+public IGuildUser GuildUser { get; set; }
+```
-* Use a ruler set at 70 characters
+* The `` tag should not be more than 3 lines long. Consider
+simplifying the terms or using the `` tag instead.
+* When using the `` tag, put the code sample within the
+`src/Discord.Net.Examples` project under the corresponding path of
+the object and surround the code with a `#region` tag.
+* If the remarks you are looking to write are too long, consider
+writing a shorter version in the XML docs while keeping the longer
+version in the `overwrites` folder using the DocFX overwrites syntax.
+ * You may find an example of this in the samples provided within
+ the folder.
+
+## Docs Guide Guidelines
+
+* Use a ruler set at 70 characters (use the docs workspace provided
+if you are using Visual Studio Code)
* Links should use long syntax
* Pages should be short and concise, not broad and long
@@ -31,5 +54,7 @@ Please consult the [API Documentation] for more information.
## Recommended Reads
-* http://docs.microsoft.com
-* http://flask.pocoo.org/docs/0.12/
\ No newline at end of file
+* [Microsoft Docs](https://docs.microsoft.com)
+* [Flask Docs](https://flask.pocoo.org/docs/1.0/)
+* [DocFX Manual](https://dotnet.github.io/docfx/)
+* [Sandcastle XML Guide](http://ewsoftware.github.io/XMLCommentsGuide)
\ No newline at end of file
diff --git a/docs/_overwrites/Commands/PreconditionAttribute.Remarks.Inclusion.md b/docs/_overwrites/Commands/PreconditionAttribute.Remarks.Inclusion.md
index 499cdb0ad..daa0c33cb 100644
--- a/docs/_overwrites/Commands/PreconditionAttribute.Remarks.Inclusion.md
+++ b/docs/_overwrites/Commands/PreconditionAttribute.Remarks.Inclusion.md
@@ -1,6 +1,6 @@
-A "precondidtion" in the command system is used to determine if a
+A "precondition" in the command system is used to determine if a
condition is met before entering the command task. Using a
-precondidtion may aid in keeping a well-organized command logic.
+precondition may aid in keeping a well-organized command logic.
The most common use case being whether a user has sufficient
permission to execute the command.
\ 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 283f45297..dd790e55e 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 b8e7f4a7b..82f214846 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 d59fd738f..ddc821b29 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-8e0b172.so b/docs/_template/last-modified/plugins/lib/alpine-x64/libgit2-8e0b172.so
deleted file mode 100644
index efd678335..000000000
Binary files a/docs/_template/last-modified/plugins/lib/alpine-x64/libgit2-8e0b172.so and /dev/null differ
diff --git a/docs/_template/last-modified/plugins/lib/alpine-x64/libgit2-a904fc6.so b/docs/_template/last-modified/plugins/lib/alpine-x64/libgit2-a904fc6.so
new file mode 100644
index 000000000..f1f45e7d0
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/alpine-x64/libgit2-a904fc6.so differ
diff --git a/docs/_template/last-modified/plugins/lib/debian.9-x64/libgit2-8e0b172.so b/docs/_template/last-modified/plugins/lib/debian.9-x64/libgit2-8e0b172.so
deleted file mode 100644
index b51cf2342..000000000
Binary files a/docs/_template/last-modified/plugins/lib/debian.9-x64/libgit2-8e0b172.so and /dev/null differ
diff --git a/docs/_template/last-modified/plugins/lib/debian.9-x64/libgit2-a904fc6.so b/docs/_template/last-modified/plugins/lib/debian.9-x64/libgit2-a904fc6.so
new file mode 100644
index 000000000..dd0f7ffcd
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/debian.9-x64/libgit2-a904fc6.so differ
diff --git a/docs/_template/last-modified/plugins/lib/fedora-x64/libgit2-8e0b172.so b/docs/_template/last-modified/plugins/lib/fedora-x64/libgit2-8e0b172.so
deleted file mode 100644
index 7071c172f..000000000
Binary files a/docs/_template/last-modified/plugins/lib/fedora-x64/libgit2-8e0b172.so and /dev/null differ
diff --git a/docs/_template/last-modified/plugins/lib/fedora-x64/libgit2-a904fc6.so b/docs/_template/last-modified/plugins/lib/fedora-x64/libgit2-a904fc6.so
new file mode 100644
index 000000000..7d1aafbed
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/fedora-x64/libgit2-a904fc6.so differ
diff --git a/docs/_template/last-modified/plugins/lib/linux-x64/libgit2-8e0b172.so b/docs/_template/last-modified/plugins/lib/linux-x64/libgit2-8e0b172.so
deleted file mode 100644
index 3b678b6ec..000000000
Binary files a/docs/_template/last-modified/plugins/lib/linux-x64/libgit2-8e0b172.so and /dev/null differ
diff --git a/docs/_template/last-modified/plugins/lib/linux-x64/libgit2-a904fc6.so b/docs/_template/last-modified/plugins/lib/linux-x64/libgit2-a904fc6.so
new file mode 100644
index 000000000..6eb5c8b0c
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/linux-x64/libgit2-a904fc6.so differ
diff --git a/docs/_template/last-modified/plugins/lib/osx/libgit2-8e0b172.dylib b/docs/_template/last-modified/plugins/lib/osx/libgit2-8e0b172.dylib
deleted file mode 100644
index 517c8135a..000000000
Binary files a/docs/_template/last-modified/plugins/lib/osx/libgit2-8e0b172.dylib and /dev/null differ
diff --git a/docs/_template/last-modified/plugins/lib/osx/libgit2-a904fc6.dylib b/docs/_template/last-modified/plugins/lib/osx/libgit2-a904fc6.dylib
new file mode 100644
index 000000000..041256cc3
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/osx/libgit2-a904fc6.dylib differ
diff --git a/docs/_template/last-modified/plugins/lib/rhel-x64/libgit2-8e0b172.so b/docs/_template/last-modified/plugins/lib/rhel-x64/libgit2-8e0b172.so
deleted file mode 100644
index aac190aaf..000000000
Binary files a/docs/_template/last-modified/plugins/lib/rhel-x64/libgit2-8e0b172.so and /dev/null differ
diff --git a/docs/_template/last-modified/plugins/lib/rhel-x64/libgit2-a904fc6.so b/docs/_template/last-modified/plugins/lib/rhel-x64/libgit2-a904fc6.so
new file mode 100644
index 000000000..6166cb4c8
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/rhel-x64/libgit2-a904fc6.so differ
diff --git a/docs/_template/last-modified/plugins/lib/ubuntu.18.04-x64/libgit2-a904fc6.so b/docs/_template/last-modified/plugins/lib/ubuntu.18.04-x64/libgit2-a904fc6.so
new file mode 100644
index 000000000..b3528eee1
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/ubuntu.18.04-x64/libgit2-a904fc6.so differ
diff --git a/docs/_template/last-modified/plugins/lib/win32/x64/git2-8e0b172.dll b/docs/_template/last-modified/plugins/lib/win32/x64/git2-8e0b172.dll
deleted file mode 100644
index 117a78b49..000000000
Binary files a/docs/_template/last-modified/plugins/lib/win32/x64/git2-8e0b172.dll and /dev/null differ
diff --git a/docs/_template/last-modified/plugins/lib/win32/x64/git2-a904fc6.dll b/docs/_template/last-modified/plugins/lib/win32/x64/git2-a904fc6.dll
new file mode 100644
index 000000000..dd0ca9cd3
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/win32/x64/git2-a904fc6.dll differ
diff --git a/docs/_template/last-modified/plugins/lib/win32/x86/git2-8e0b172.dll b/docs/_template/last-modified/plugins/lib/win32/x86/git2-8e0b172.dll
deleted file mode 100644
index 7b709023c..000000000
Binary files a/docs/_template/last-modified/plugins/lib/win32/x86/git2-8e0b172.dll and /dev/null differ
diff --git a/docs/_template/last-modified/plugins/lib/win32/x86/git2-a904fc6.dll b/docs/_template/last-modified/plugins/lib/win32/x86/git2-a904fc6.dll
new file mode 100644
index 000000000..627d344cc
Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/win32/x86/git2-a904fc6.dll differ
diff --git a/docs/_template/light-dark-theme/docfx-material-license.md b/docs/_template/light-dark-theme/docfx-material-license.md
new file mode 100644
index 000000000..4576c42b1
--- /dev/null
+++ b/docs/_template/light-dark-theme/docfx-material-license.md
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 Oscar Vásquez
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/docs/_template/light-dark-theme/partials/head.tmpl.partial b/docs/_template/light-dark-theme/partials/head.tmpl.partial
index 94670f432..a25da58fc 100644
--- a/docs/_template/light-dark-theme/partials/head.tmpl.partial
+++ b/docs/_template/light-dark-theme/partials/head.tmpl.partial
@@ -15,6 +15,7 @@
+
diff --git a/docs/_template/light-dark-theme/styles/dark.css b/docs/_template/light-dark-theme/styles/dark.css
index 54ad76c79..dd55ae949 100644
--- a/docs/_template/light-dark-theme/styles/dark.css
+++ b/docs/_template/light-dark-theme/styles/dark.css
@@ -7,6 +7,15 @@ body {
color: #C0C0C0;
}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ color: #E0E0E0;
+}
+
button,
a {
color: #64B5F6;
@@ -258,6 +267,11 @@ tbody>tr {
border-top: 2px solid rgb(173, 173, 173)
}
+/* top navbar */
+.navbar-inverse[role="navigation"] {
+ background-color: #2C2F33;
+}
+
/* select */
select {
diff --git a/docs/_template/light-dark-theme/styles/docfx.vendor.minify.css b/docs/_template/light-dark-theme/styles/docfx.vendor.minify.css
index f9eb380d6..771cb0b7e 100644
--- a/docs/_template/light-dark-theme/styles/docfx.vendor.minify.css
+++ b/docs/_template/light-dark-theme/styles/docfx.vendor.minify.css
@@ -361,7 +361,6 @@ pre {
word-break: break-all;
word-wrap: break-word;
color: #333;
- border: 1px solid #ccc;
border-radius: 4px;
}
diff --git a/docs/_template/light-dark-theme/styles/gray.css b/docs/_template/light-dark-theme/styles/gray.css
index 4cb658788..463561be5 100644
--- a/docs/_template/light-dark-theme/styles/gray.css
+++ b/docs/_template/light-dark-theme/styles/gray.css
@@ -7,6 +7,15 @@ body {
color: #dddddd;
}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ color: #EEEEEE;
+}
+
button,
a {
color: #64B5F6;
@@ -39,13 +48,13 @@ hr {
}
/* top navbar */
-.navbar-inverse[role="navigation"] {
+/*.navbar-inverse[role="navigation"] {
background-color: #2C2F33;
-}
+}*/
/* sub navbar (below top) */
.subnav {
- background: #282B2F
+ background: rgb(69, 75, 82)
}
diff --git a/docs/_template/light-dark-theme/styles/master.css b/docs/_template/light-dark-theme/styles/master.css
index e0f7befb5..aa4b71ac6 100644
--- a/docs/_template/light-dark-theme/styles/master.css
+++ b/docs/_template/light-dark-theme/styles/master.css
@@ -1,14 +1,37 @@
-@import url('https://fonts.googleapis.com/css?family=Titillium+Web|Noto+Sans');
+@import url('https://fonts.googleapis.com/css?family=Roboto|Muli|Fira+Mono');
html,
body {
- font-family: 'Titillium Web', 'Segoe UI', Tahoma, Helvetica, sans-serif;
+ font-family: 'Roboto', 'Segoe UI', Tahoma, Helvetica, sans-serif;
font-display: optional;
height: 100%;
font-size: 15px;
scroll-behavior: smooth;
}
+code{
+ font-family: 'Fira Mono', 'Courier New', Courier, monospace
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-family: 'Muli', Verdana, Geneva, Tahoma, sans-serif;
+ line-height: 130%;
+}
+
+h1,
+.h1,
+h2,
+.h2,
+h3,
+.h3 {
+ font-weight: 600;
+}
+
#logo
{
max-width: 100px;
@@ -25,6 +48,14 @@ li,
line-height: 160%;
}
+.toc-filter{
+ background: inherit !important;
+}
+
+.affix ul>li.active>ul, .affix ul>li.active>a:before, .affix ul>li>a:hover:before{
+ white-space: normal;
+}
+
img {
box-shadow: 0px 0px 3px 0px rgb(66, 66, 66);
max-width: 95% !important;
@@ -57,16 +88,6 @@ article.content h6{
transition: all .25s ease-in-out;
}
-h1,
-h2,
-h3,
-h4,
-h5,
-h6 {
- font-family: 'Noto Sans', Verdana, Geneva, Tahoma, sans-serif;
- line-height: 130%;
-}
-
.sideaffix {
line-height: 140%;
}
@@ -173,3 +194,38 @@ span.arrow-d{
span.arrow-r{
top: 6px; position: relative;
}
+
+/* widen viewport */
+
+@media (min-width: 1085px) {
+ .container {
+ width: calc(100% - 15vw);
+ max-width: calc(100% - 15vw);
+ }
+}
+
+/* fix level indentation */
+
+.level2 {
+ padding: 0 5px;
+}
+
+.level3 {
+ padding: 0 5px;
+ font-size: 90%;
+}
+
+.level4 {
+ padding: 0 5px;
+ font-size: 85%;
+}
+
+.level5 {
+ padding: 0 5px;
+ font-size: 80%;
+}
+
+.level6 {
+ padding: 0 5px;
+ font-size: 75%;
+}
\ No newline at end of file
diff --git a/docs/_template/light-dark-theme/styles/material.css b/docs/_template/light-dark-theme/styles/material.css
new file mode 100644
index 000000000..06a064337
--- /dev/null
+++ b/docs/_template/light-dark-theme/styles/material.css
@@ -0,0 +1,199 @@
+body {
+ color: #34393e;
+ line-height: 1.5;
+ /*font-size: 16px;*/
+ -ms-text-size-adjust: 100%;
+ -webkit-text-size-adjust: 100%;
+ word-wrap: break-word
+}
+
+/* HEADINGS */
+
+h1 {
+ font-weight: 600;
+ font-size: 32px;
+}
+
+h2 {
+ font-weight: 600;
+ font-size: 24px;
+ line-height: 1.8;
+}
+
+h3 {
+ font-weight: 600;
+ font-size: 20px;
+ line-height: 1.8;
+}
+
+h5 {
+ font-size: 14px;
+ padding: 10px 0px;
+}
+
+article h1,
+article h2,
+article h3,
+article h4 {
+ margin-top: 35px;
+ margin-bottom: 15px;
+}
+
+article h4 {
+ padding-bottom: 8px;
+ border-bottom: 2px solid #ddd;
+}
+
+/* NAVBAR */
+
+.navbar-brand>img {
+ color: #fff;
+}
+
+.navbar {
+ border: none;
+ /* Both navbars use box-shadow */
+ -webkit-box-shadow: 0px 1px 3px 0px rgba(100, 100, 100, 0.5);
+ -moz-box-shadow: 0px 1px 3px 0px rgba(100, 100, 100, 0.5);
+ box-shadow: 0px 1px 3px 0px rgba(100, 100, 100, 0.5);
+}
+
+.subnav {
+ border-top: 1px solid #ddd;
+ background-color: #fff;
+}
+
+.navbar-inverse {
+ background-color: #0d47a1;
+ z-index: 100;
+}
+
+.navbar-inverse .navbar-nav>li>a,
+.navbar-inverse .navbar-text {
+ color: #fff;
+ /*background-color: #0d47a1;*/
+ border-bottom: 3px solid transparent;
+ padding-bottom: 12px;
+}
+
+.navbar-inverse .navbar-nav>li>a:focus,
+.navbar-inverse .navbar-nav>li>a:hover {
+ color: #fff;
+ background-color: #1157c0;
+ border-bottom: 3px solid white;
+}
+
+.navbar-inverse .navbar-nav>.active>a,
+.navbar-inverse .navbar-nav>.active>a:focus,
+.navbar-inverse .navbar-nav>.active>a:hover {
+ color: #fff;
+ background-color: #1157c0;
+ border-bottom: 3px solid white;
+}
+
+.navbar-form .form-control {
+ border: none;
+ border-radius: 20px;
+}
+
+/* SIDEBAR */
+
+/*.toc .level1>li {
+ font-weight: 400;
+}*/
+
+.toc .nav>li>a {
+ color: #34393e;
+}
+
+.sidefilter {
+ background-color: #fff;
+ border-left: none;
+ border-right: none;
+}
+
+.sidefilter {
+ background-color: #fff;
+ border-left: none;
+ border-right: none;
+}
+
+.toc-filter {
+ padding: 10px;
+ margin: 0;
+}
+
+.toc-filter>input {
+ border: 2px solid #ddd;
+ border-radius: 20px;
+}
+
+.toc-filter>.filter-icon {
+ display: none;
+}
+
+.sidetoc>.toc {
+ overflow-x: hidden;
+}
+
+.sidetoc {
+ border: none;
+}
+
+/* ALERTS */
+
+.alert {
+ padding: 0px 0px 5px 0px;
+ color: inherit;
+ background-color: inherit;
+ border: none;
+ box-shadow: 0px 2px 2px 0px rgba(100, 100, 100, 0.4);
+}
+
+.alert>p {
+ margin-bottom: 0;
+ padding: 5px 10px;
+}
+
+.alert>ul {
+ margin-bottom: 0;
+ padding: 5px 40px;
+}
+
+.alert>h5 {
+ padding: 10px 15px;
+ margin-top: 0;
+ text-transform: uppercase;
+ font-weight: bold;
+ border-radius: 4px 4px 0 0;
+}
+
+.alert-info>h5 {
+ color: #1976d2;
+ border-bottom: 4px solid #1976d2;
+ background-color: #e3f2fd;
+}
+
+.alert-warning>h5 {
+ color: #f57f17;
+ border-bottom: 4px solid #f57f17;
+ background-color: #fff3e0;
+}
+
+.alert-danger>h5 {
+ color: #d32f2f;
+ border-bottom: 4px solid #d32f2f;
+ background-color: #ffebee;
+}
+
+/* CODE HIGHLIGHT */
+pre {
+ padding: 9.5px;
+ margin: 10px 10px 10px;
+ font-size: 13px;
+ word-break: break-all;
+ word-wrap: break-word;
+ /*background-color: #fffaef;*/
+ border-radius: 4px;
+ box-shadow: 0px 1px 4px 1px rgba(100, 100, 100, 0.4);
+}
diff --git a/docs/filterConfig.yml b/docs/filterConfig.yml
index 410a80d25..598fd3442 100644
--- a/docs/filterConfig.yml
+++ b/docs/filterConfig.yml
@@ -4,4 +4,7 @@ apiRules:
type: Namespace
- exclude:
uidRegex: ^Discord\.Analyzers$
+ type: Namespace
+- exclude:
+ uidRegex: ^Discord\.API$
type: Namespace
\ No newline at end of file
diff --git a/docs/guides/commands/samples/dependency-injection/dependency_map_setup.cs b/docs/guides/commands/samples/dependency-injection/dependency_map_setup.cs
index 118c032ed..16ca479db 100644
--- a/docs/guides/commands/samples/dependency-injection/dependency_map_setup.cs
+++ b/docs/guides/commands/samples/dependency-injection/dependency_map_setup.cs
@@ -3,6 +3,9 @@ public class Initialize
private readonly CommandService _commands;
private readonly DiscordSocketClient _client;
+ // Ask if there are existing CommandService and DiscordSocketClient
+ // instance. If there are, we retrieve them and add them to the
+ // DI container; if not, we create our own.
public Initialize(CommandService commands = null, DiscordSocketClient client = null)
{
_commands = commands ?? new CommandService();
diff --git a/docs/guides/commands/samples/dependency-injection/dependency_module.cs b/docs/guides/commands/samples/dependency-injection/dependency_module.cs
index f0625e503..3e42074ca 100644
--- a/docs/guides/commands/samples/dependency-injection/dependency_module.cs
+++ b/docs/guides/commands/samples/dependency-injection/dependency_module.cs
@@ -32,6 +32,6 @@ public class DatabaseModule : ModuleBase
[Command("read")]
public async Task ReadFromDbAsync()
{
- await ReplyAsync(_database.GetData());
+ await ReplyAsync(DbService.GetData());
}
-}
\ No newline at end of file
+}
diff --git a/docs/guides/commands/samples/intro/command_handler.cs b/docs/guides/commands/samples/intro/command_handler.cs
index b962cdd6c..480e43c7f 100644
--- a/docs/guides/commands/samples/intro/command_handler.cs
+++ b/docs/guides/commands/samples/intro/command_handler.cs
@@ -3,6 +3,7 @@ public class CommandHandler
private readonly DiscordSocketClient _client;
private readonly CommandService _commands;
+ // Retrieve client and CommandService instance via ctor
public CommandHandler(DiscordSocketClient client, CommandService commands)
{
_commands = commands;
@@ -46,19 +47,9 @@ public class CommandHandler
// Execute the command with the command context we just
// created, along with the service provider for precondition checks.
-
- // Keep in mind that result does not indicate a return value
- // rather an object stating if the command executed successfully.
- var result = await _commands.ExecuteAsync(
+ await _commands.ExecuteAsync(
context: context,
argPos: argPos,
services: null);
-
- // Optionally, we may inform the user if the command fails
- // to be executed; however, this may not always be desired,
- // as it may clog up the request queue should a user spam a
- // command.
- // if (!result.IsSuccess)
- // await context.Channel.SendMessageAsync(result.ErrorReason);
}
}
diff --git a/docs/guides/commands/samples/post-execution/command_exception_log.cs b/docs/guides/commands/samples/post-execution/command_exception_log.cs
index 2956a0203..fa3673e82 100644
--- a/docs/guides/commands/samples/post-execution/command_exception_log.cs
+++ b/docs/guides/commands/samples/post-execution/command_exception_log.cs
@@ -1,6 +1,5 @@
public async Task LogAsync(LogMessage logMessage)
{
- // This casting type requries C#7
if (logMessage.Exception is CommandException cmdException)
{
// We can tell the user that something unexpected has happened
diff --git a/docs/guides/commands/samples/typereaders/typereader.cs b/docs/guides/commands/samples/typereaders/typereader.cs
index a2a4627d2..28611872c 100644
--- a/docs/guides/commands/samples/typereaders/typereader.cs
+++ b/docs/guides/commands/samples/typereaders/typereader.cs
@@ -1,5 +1,6 @@
-// Note: This example is obsolete, a boolean type reader is bundled
-// with Discord.Commands
+// Please note that the library already supports type reading
+// primitive types such as bool. This example is merely used
+// to demonstrate how one could write a simple TypeReader.
using Discord;
using Discord.Commands;
diff --git a/docs/guides/concepts/connections.md b/docs/guides/concepts/connections.md
index 3d7b77cb8..d9951a8cc 100644
--- a/docs/guides/concepts/connections.md
+++ b/docs/guides/concepts/connections.md
@@ -35,10 +35,6 @@ sync and has a completed guild cache.
[DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient
-### Samples
-
-[!code-csharp[Connection Sample](samples/events.cs)]
-
## Reconnection
> [!TIP]
diff --git a/docs/guides/concepts/entities.md b/docs/guides/concepts/entities.md
index 7415f043a..5ad5b01f2 100644
--- a/docs/guides/concepts/entities.md
+++ b/docs/guides/concepts/entities.md
@@ -7,7 +7,7 @@ title: Entities
> [!NOTE]
> This article is written with the Socket variants of entities in mind,
-> not the general interfaces or Rest/Rpc entities.
+> not the general interfaces or Rest entities.
Discord.Net provides a versatile entity system for navigating the
Discord API.
diff --git a/docs/guides/concepts/samples/connections.cs b/docs/guides/concepts/samples/connections.cs
deleted file mode 100644
index b610da524..000000000
--- a/docs/guides/concepts/samples/connections.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using Discord;
-using Discord.WebSocket;
-
-public class Program
-{
- private DiscordSocketClient _client;
- static void Main(string[] args) => new Program().MainAsync().GetAwaiter().GetResult();
-
- public async Task MainAsync()
- {
- _client = new DiscordSocketClient();
-
- await _client.LoginAsync(TokenType.Bot, Environment.GetEnvironmentVariable("DiscordToken"));
- await _client.StartAsync();
-
- Console.WriteLine("Press any key to exit...");
- Console.ReadKey();
-
- await _client.StopAsync();
- // Wait a little for the client to finish disconnecting before allowing the program to return
- await Task.Delay(500);
- }
-}
\ No newline at end of file
diff --git a/docs/guides/concepts/samples/logging.cs b/docs/guides/concepts/samples/logging.cs
index 2419452f0..982fb11b6 100644
--- a/docs/guides/concepts/samples/logging.cs
+++ b/docs/guides/concepts/samples/logging.cs
@@ -1,29 +1,24 @@
using Discord;
using Discord.WebSocket;
-public class Program
+public class LoggingService
{
- private DiscordSocketClient _client;
- static void Main(string[] args) => new Program().MainAsync().GetAwaiter().GetResult();
-
- public async Task MainAsync()
+ public LoggingService(DiscordSocketClient client, CommandService command)
{
- _client = new DiscordSocketClient(new DiscordSocketConfig
- {
- LogLevel = LogSeverity.Info
- });
-
- _client.Log += Log;
-
- await _client.LoginAsync(TokenType.Bot, Environment.GetEnvironmentVariable("DiscordToken"));
- await _client.StartAsync();
-
- await Task.Delay(-1);
+ client.Log += LogAsync;
+ command.Log += LogAsync;
}
-
- private Task Log(LogMessage message)
+ private Task LogAsync(LogMessage message)
{
- Console.WriteLine(message.ToString());
+ if (message.Exception is CommandException cmdException)
+ {
+ Console.WriteLine($"[Command/{message.Severity}] {cmdException.Command.Aliases.First()}"
+ + $" failed to execute in {cmdException.Context.Channel}.");
+ Console.WriteLine(cmdException);
+ }
+ else
+ Console.WriteLine($"[General/{message.Severity}] {message}");
+
return Task.CompletedTask;
}
}
\ No newline at end of file
diff --git a/docs/guides/getting_started/first-bot.md b/docs/guides/getting_started/first-bot.md
index a33e72aff..eb140cd75 100644
--- a/docs/guides/getting_started/first-bot.md
+++ b/docs/guides/getting_started/first-bot.md
@@ -128,12 +128,10 @@ Finally, we can create a new connection to Discord.
Since we are writing a bot, we will be using a [DiscordSocketClient]
along with socket entities. See @Guides.GettingStarted.Terminology
-if you are unsure of the differences.
-
-To establish a new connection, we will create an instance of
-[DiscordSocketClient] in the new async main. You may pass in an
-optional @Discord.WebSocket.DiscordSocketConfig if necessary. For most
-users, the default will work fine.
+if you are unsure of the differences. To establish a new connection,
+we will create an instance of [DiscordSocketClient] in the new async
+main. You may pass in an optional @Discord.WebSocket.DiscordSocketConfig
+if necessary. For most users, the default will work fine.
Before connecting, we should hook the client's `Log` event to the
log handler that we had just created. Events in Discord.Net work
@@ -142,22 +140,33 @@ similarly to any other events in C#.
Next, you will need to "log in to Discord" with the [LoginAsync]
method with the application's "token."
+
+
> [!NOTE]
> Pay attention to what you are copying from the developer portal!
> A token is not the same as the application's "client secret."
-
-
> [!IMPORTANT]
> Your bot's token can be used to gain total access to your bot, so
-> **do __NOT__ share this token with anyone else!** It may behoove you
-> to store this token in an external source if you plan on distributing
+> **do not** share this token with anyone else! You should store this
+> token in an external source if you plan on distributing
> the source code for your bot.
+>
+> In the following example, we retrieve the token from the environment
+> variable `DiscordToken`. Please note that this is *not* designed to
+> be used in a production environment, as the secrets are stored in
+> plain-text.
+>
+> For information on how to set an environment variable, please see
+> instructions below,
+>
+> * Windows: [How to Create Environment Variables Shortcut in Windows](https://www.tenforums.com/tutorials/121742-create-environment-variables-shortcut-windows.html)
+> * Linux: [How To Read and Set Environmental and Shell Variables on a Linux VPS](https://www.digitalocean.com/community/tutorials/how-to-read-and-set-environmental-and-shell-variables-on-a-linux-vps)
+> * macOS: [How do I set environment variables on OS X?](https://apple.stackexchange.com/questions/106778/how-do-i-set-environment-variables-on-os-x)
We may now invoke the client's [StartAsync] method, which will
start connection/reconnection logic. It is important to note that
**this method will return as soon as connection logic has been started!**
-
Any methods that rely on the client's state should go in an event
handler. This means that you should **not** directly be interacting with
the client before it is fully ready.
@@ -173,81 +182,34 @@ The following lines can now be added:
At this point, feel free to start your program and see your bot come
online in Discord.
-> [!TIP]
+> [!WARNING]
> Getting a warning about `A supplied token was invalid.` and/or
> having trouble logging in? Double-check whether you have put in
> the correct credentials and make sure that it is _not_ a client
> secret, which is different from a token.
-> [!TIP]
+> [!WARNING]
> Encountering a `PlatformNotSupportedException` when starting your bot?
> This means that you are targeting a platform where .NET's default
> WebSocket client is not supported. Refer to the [installation guide]
> for how to fix this.
+> [!NOTE]
+> For your reference, you may view the [completed program].
+
[DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient
[LoginAsync]: xref:Discord.Rest.BaseDiscordClient.LoginAsync*
[StartAsync]: xref:Discord.WebSocket.DiscordSocketClient.StartAsync*
[installation guide]: xref:Guides.GettingStarted.Installation
-
-### Handling a 'ping'
-
-> [!WARNING]
-> Please note that this is *not* a proper way to create a command.
-> Use the `CommandService` provided by the library instead, as explained
-> in the [Command Guide](xref:Guides.Commands.Intro) section.
-
-Now that we have learned to open a connection to Discord, we can
-begin handling messages that the users are sending. To start out, our
-bot will listen for any message whose content is equal to `!ping` and
-will respond back with "Pong!".
-
-Since we want to listen for new messages, the event to hook into
-is [MessageReceived].
-
-In your program, add a method that matches the signature of the
-`MessageReceived` event - it must be a method (`Func`) that returns
-the type `Task` and takes a single parameter, a [SocketMessage]. Also,
-since we will be sending data to Discord in this method, we will flag
-it as `async`.
-
-In this method, we will add an `if` block to determine if the message
-content fits the rules of our scenario - recall that it must be equal
-to `!ping`.
-
-Inside the branch of this condition, we will want to send a message,
-`Pong!`, back to the channel from which the message comes from. To
-find the channel, look for the `Channel` property on the message
-parameter.
-
-Next, we will want to send a message to this channel. Since the
-channel object is of type [ISocketMessageChannel], we can invoke the
-[SendMessageAsync] instance method. For the message content, send back
-a string, "Pong!".
-
-You should have now added the following lines,
-
-[!code-csharp[Message](samples/first-bot/message.cs)]
-
-Now that your first bot is complete. You may continue to add on to this
-if you desire, but for any bots that will be carrying out multiple
-commands, it is strongly recommended to use the command framework as
-shown below.
-
-> [!NOTE]
-> For your reference, you may view the [completed program].
-
-[MessageReceived]: xref:Discord.WebSocket.BaseSocketClient.MessageReceived
-[SocketMessage]: xref:Discord.WebSocket.SocketMessage
-[ISocketMessageChannel]: xref:Discord.WebSocket.ISocketMessageChannel
-[SendMessageAsync]: xref:Discord.WebSocket.ISocketMessageChannel.SendMessageAsync*
[completed program]: samples/first-bot/complete.cs
# Building a bot with commands
-@Guides.Commands.Intro will guide you through how to setup a program
-that is ready for [CommandService], a service that is ready for
-advanced command usage.
+To create commands for your bot, you may choose from a variety of
+command processors available. Throughout the guides, we will be using
+the one that Discord.Net ships with. @Guides.Commands.Intro will
+guide you through how to setup a program that is ready for
+[CommandService].
For reference, view an [annotated example] of this structure.
diff --git a/docs/guides/getting_started/installing.md b/docs/guides/getting_started/installing.md
index bb2592c02..f34032cb2 100644
--- a/docs/guides/getting_started/installing.md
+++ b/docs/guides/getting_started/installing.md
@@ -42,37 +42,45 @@ published to our [MyGet feed]. See
### [Using Visual Studio](#tab/vs-install)
-1. Create a new solution for your bot.
+1. Create a new solution for your bot
2. In the Solution Explorer, find the "Dependencies" element under your
- bot's project.
-3. Right click on "Dependencies", and select "Manage NuGet packages."
- 
-4. In the "Browse" tab, search for `Discord.Net`.
-5. Install the `Discord.Net` package.
- 
+ 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.
-
+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)
-1. Create a new project for your bot.
-2. Add `Discord.Net` to your .csproj.
+1. Create a new project for your bot
+2. Add `Discord.Net` to your `*.csproj`
[!code[Sample .csproj](samples/project.xml)]
### [Using dotnet CLI](#tab/dotnet-cli)
-1. Open command-line and navigate to where your .csproj is located.
-2. Enter `dotnet add package Discord.Net`.
+1. Launch your terminal
+2. Navigate to where your `*.csproj` is located
+3. Enter `dotnet add package Discord.Net`
***
@@ -115,16 +123,16 @@ by installing one or more custom packages as listed below.
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.
- * 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)]
diff --git a/docs/guides/getting_started/nightlies.md b/docs/guides/getting_started/nightlies.md
index 39bcc1c9f..d3844be1c 100644
--- a/docs/guides/getting_started/nightlies.md
+++ b/docs/guides/getting_started/nightlies.md
@@ -31,22 +31,33 @@ The following is the feed link of Discord.Net,
Depending on which IDE you use, there are many different ways of
adding the feed to your package source.
-### [Visual Studio](#tab/vs)
+### [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 prerelease` checkbox to see the
+> Remember to tick the `Include pre-release` checkbox to see the
> nightly builds!
> 
-### [Local NuGet.Config](#tab/local-nuget-config)
+### [Using dotnet CLI](#tab/cli)
+
+1. Launch your terminal
+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
diff --git a/docs/guides/getting_started/samples/first-bot/complete.cs b/docs/guides/getting_started/samples/first-bot/complete.cs
index 3a7cb061d..871641e23 100644
--- a/docs/guides/getting_started/samples/first-bot/complete.cs
+++ b/docs/guides/getting_started/samples/first-bot/complete.cs
@@ -9,7 +9,6 @@ public class Program
{
_client = new DiscordSocketClient();
_client.Log += Log;
- _client.MessageReceived += MessageReceivedAsync;
await _client.LoginAsync(TokenType.Bot,
Environment.GetEnvironmentVariable("DiscordToken"));
await _client.StartAsync();
@@ -17,15 +16,6 @@ public class Program
// Block this task until the program is closed.
await Task.Delay(-1);
}
-
- private async Task MessageReceivedAsync(SocketMessage message)
- {
- if (message.Content == "!ping")
- {
- await message.Channel.SendMessageAsync("Pong!");
- }
- }
-
private Task Log(LogMessage msg)
{
Console.WriteLine(msg.ToString());
diff --git a/docs/guides/introduction/intro.md b/docs/guides/introduction/intro.md
index 14d4aa49e..0a4ca26e9 100644
--- a/docs/guides/introduction/intro.md
+++ b/docs/guides/introduction/intro.md
@@ -16,17 +16,14 @@ understand these topics to some extent before proceeding. With all
that being said, feel free to visit us on Discord at the link below
if you have any questions!
-Here are some examples:
-
-1. [Official samples]
-2. [Official template]
+An official collection of samples can be found
+in [our GitHub repository].
> [!NOTE]
> Please note that you should *not* try to blindly copy paste
> the code. The examples are meant to be a template or a guide.
-[Official template]: https://github.com/foxbot/DiscordBotBase/tree/csharp/src/DiscordBot
-[Official samples]: https://github.com/RogueException/Discord.Net/tree/dev/samples
+[our GitHub repository]: https://github.com/RogueException/Discord.Net/tree/dev/samples
[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/
diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs
index 94f2636ee..3a7da3862 100644
--- a/src/Discord.Net.Commands/CommandService.cs
+++ b/src/Discord.Net.Commands/CommandService.cs
@@ -39,15 +39,8 @@ namespace Discord.Commands
/// Occurs when a command is successfully executed without any error.
///
///
- ///
- /// This event is fired when a command has been successfully executed without any of the following errors:
- ///
- /// * Parsing error
- /// * Precondition error
- /// * Runtime exception
- ///
- /// Should the command encounter any of the aforementioned error, this event will not be raised.
- ///
+ /// This event is fired when a command has been executed, successfully or not. When a command fails to
+ /// execute during parsing or precondition stage, the CommandInfo may not be returned.
///
public event Func, ICommandContext, IResult, Task> CommandExecuted { add { _commandExecutedEvent.Add(value); } remove { _commandExecutedEvent.Remove(value); } }
internal readonly AsyncEvent, ICommandContext, IResult, Task>> _commandExecutedEvent = new AsyncEvent, ICommandContext, IResult, Task>>();
diff --git a/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs
index dab7f436d..efa5fb1e8 100644
--- a/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs
+++ b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs
@@ -97,26 +97,12 @@ namespace Discord
/// Adds or updates the permission overwrite for the given role.
///
///
- /// The following example fetches a role via and a channel via
+ /// The following example fetches a role via and a channel via
/// . Next, it checks if an overwrite had already been set via
/// ; if not, it denies the role from sending any
- /// messages to the channel.
- ///
- /// // Fetches the role and channels
- /// var role = guild.GetRole(339805618376540160);
- /// var channel = await guild.GetChannelAsync(233937283911385098);
- ///
- /// // If either the of the object does not exist, bail
- /// if (role == null || channel == null) return;
- ///
- /// // Fetches the previous overwrite and bail if one is found
- /// var previousOverwrite = channel.GetPermissionOverwrite(role);
- /// if (previousOverwrite.HasValue) return;
- ///
- /// // Creates a new OverwritePermissions with send message set to deny and pass it into the method
- /// await channel.AddPermissionOverwriteAsync(role,
- /// new OverwritePermissions(sendMessage: PermValue.Deny));
- ///
+ /// messages to the channel.
+ ///
///
/// The role to add the overwrite to.
/// The overwrite to add to the role.
@@ -130,26 +116,12 @@ namespace Discord
/// Adds or updates the permission overwrite for the given user.
///
///
- /// The following example fetches a user via and a channel via
+ /// The following example fetches a user via and a channel via
/// . Next, it checks if an overwrite had already been set via
/// ; if not, it denies the user from sending any
- /// messages to the channel.
- ///
- /// // Fetches the role and channels
- /// var user = await guild.GetUserAsync(168693960628371456);
- /// var channel = await guild.GetChannelAsync(233937283911385098);
- ///
- /// // If either the of the object does not exist, bail
- /// if (user == null || channel == null) return;
- ///
- /// // Fetches the previous overwrite and bail if one is found
- /// var previousOverwrite = channel.GetPermissionOverwrite(user);
- /// if (previousOverwrite.HasValue) return;
- ///
- /// // Creates a new OverwritePermissions with send message set to deny and pass it into the method
- /// await channel.AddPermissionOverwriteAsync(role,
- /// new OverwritePermissions(sendMessage: PermValue.Deny));
- ///
+ /// messages to the channel.
+ ///
///
/// The user to add the overwrite to.
/// The overwrite to add to the user.
diff --git a/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs
index 919b3f60b..0e7df3f7c 100644
--- a/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs
+++ b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs
@@ -14,13 +14,10 @@ namespace Discord
/// Sends a message to this message channel.
///
///
- /// The following example sends a message with the current system time in RFC 1123 format to the channel and
- /// deletes itself after 5 seconds.
- ///
- /// var message = await channel.SendMessageAsync(DateTimeOffset.UtcNow.ToString("R"));
- /// await Task.Delay(TimeSpan.FromSeconds(5))
- /// .ContinueWith(x => message.DeleteAsync());
- ///
+ /// The following example sends a message with the current system time in RFC 1123 format to the channel and
+ /// deletes itself after 5 seconds.
+ ///
///
/// The message to be sent.
/// Determines whether the message should be read aloud by Discord or not.
@@ -35,18 +32,14 @@ namespace Discord
/// Sends a file to this message channel with an optional caption.
///
///
- /// The following example uploads a local file called wumpus.txt along with the text
- /// good discord boi to the channel.
- ///
- /// await channel.SendFileAsync("wumpus.txt", "good discord boi");
- ///
- ///
- /// The following example uploads a local image called b1nzy.jpg embedded inside a rich embed to the
- /// channel.
- ///
- /// await channel.SendFileAsync("b1nzy.jpg",
- /// embed: new EmbedBuilder {ImageUrl = "attachment://b1nzy.jpg"}.Build());
- ///
+ /// The following example uploads a local file called wumpus.txt along with the text
+ /// good discord boi to the channel.
+ ///
+ /// The following example uploads a local image called b1nzy.jpg embedded inside a rich embed to the
+ /// channel.
+ ///
///
///
/// This method sends a file as if you are uploading an attachment directly from your Discord client.
@@ -70,12 +63,10 @@ namespace Discord
/// Sends a file to this message channel with an optional caption.
///
///
- /// The following example uploads a streamed image that will be called b1nzy.jpg embedded inside a
- /// rich embed to the channel.
- ///
- /// await channel.SendFileAsync(b1nzyStream, "b1nzy.jpg",
- /// embed: new EmbedBuilder {ImageUrl = "attachment://b1nzy.jpg"}.Build());
- ///
+ /// The following example uploads a streamed image that will be called b1nzy.jpg embedded inside a
+ /// rich embed to the channel.
+ ///
///
///
/// This method sends a file as if you are uploading an attachment directly from your Discord client.
@@ -130,12 +121,10 @@ namespace Discord
/// of flattening.
///
///
- /// The following example downloads 300 messages and gets messages that belong to the user
- /// 53905483156684800.
- ///
- /// var messages = await messageChannel.GetMessagesAsync(300).FlattenAsync();
- /// var userMessages = messages.Where(x => x.Author.Id == 53905483156684800);
- ///
+ /// The following example downloads 300 messages and gets messages that belong to the user
+ /// 53905483156684800.
+ ///
///
/// The numbers of message to be gotten from.
/// The that determines whether the object should be fetched from
@@ -168,10 +157,13 @@ namespace Discord
/// of flattening.
///
///
- /// The following example gets 5 message prior to the message identifier 442012544660537354.
- ///
- /// var messages = await channel.GetMessagesAsync(442012544660537354, Direction.Before, 5).FlattenAsync();
- ///
+ /// The following example gets 5 message prior to the message identifier 442012544660537354.
+ ///
+ /// The following example attempts to retrieve messageCount number of messages from the
+ /// beginning of the channel and prints them to the console.
+ ///
///
/// The ID of the starting message to get the messages from.
/// The direction of the messages to be gotten from.
@@ -206,10 +198,9 @@ namespace Discord
/// of flattening.
///
///
- /// The following example gets 5 message prior to a specific message, oldMessage.
- ///
- /// var messages = await channel.GetMessagesAsync(oldMessage, Direction.Before, 5).FlattenAsync();
- ///
+ /// The following example gets 5 message prior to a specific message, oldMessage.
+ ///
///
/// The starting message to get the messages from.
/// The direction of the messages to be gotten from.
@@ -262,13 +253,9 @@ namespace Discord
/// object is disposed.
///
///
- /// The following example keeps the client in the typing state until LongRunningAsync has finished.
- ///
- /// using (messageChannel.EnterTypingState())
- /// {
- /// await LongRunningAsync();
- /// }
- ///
+ /// The following example keeps the client in the typing state until LongRunningAsync has finished.
+ ///
///
/// The options to be used when sending the request.
///
diff --git a/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs b/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs
index 440349eca..29c764e3f 100644
--- a/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs
+++ b/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs
@@ -40,7 +40,7 @@ namespace Discord
///
///
/// The following example gets 250 messages from the channel and deletes them.
- ///
+ ///
/// var messages = await textChannel.GetMessagesAsync(250).FlattenAsync();
/// await textChannel.DeleteMessagesAsync(messages);
///
diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs
index 3b35796b9..571afef15 100644
--- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs
+++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs
@@ -440,16 +440,8 @@ namespace Discord
///
///
/// The following example creates a new text channel under an existing category named Wumpus with a set topic.
- ///
- /// var categories = await guild.GetCategoriesAsync();
- /// var targetCategory = categories.FirstOrDefault(x => x.Name == "wumpus");
- /// if (targetCategory == null) return;
- /// await Context.Guild.CreateTextChannelAsync(name, x =>
- /// {
- /// x.CategoryId = targetCategory.Id;
- /// x.Topic = $"This channel was created at {DateTimeOffset.UtcNow} by {user}.";
- /// });
- ///
+ ///
///
/// The new name for the text channel.
/// The delegate containing the properties to be applied to the channel upon its creation.
diff --git a/src/Discord.Net.Core/Entities/Users/IUser.cs b/src/Discord.Net.Core/Entities/Users/IUser.cs
index 5d7d0a0b0..c59a75de1 100644
--- a/src/Discord.Net.Core/Entities/Users/IUser.cs
+++ b/src/Discord.Net.Core/Entities/Users/IUser.cs
@@ -23,10 +23,8 @@ namespace Discord
///
/// The following example attempts to retrieve the user's current avatar and send it to a channel; if one is
/// not set, a default avatar for this user will be returned instead.
- ///
- /// var userAvatarUrl = user.GetAvatarUrl() ?? user.GetDefaultAvatarUrl();
- /// await textChannel.SendMessageAsync(userAvatarUrl);
- ///
+ ///
///
/// The format to return.
/// The size of the image to return in. This can be any power of two between 16 and 2048.
@@ -84,27 +82,18 @@ namespace Discord
///
/// This method is used to obtain or create a channel used to send a direct message.
///
- /// In event that the current user cannot send a message to the target user, a channel can and will still be
- /// created by Discord. However, attempting to send a message will yield a
- /// with a 403 as its
- /// . There are currently no official workarounds by
- /// Discord.
+ /// In event that the current user cannot send a message to the target user, a channel can and will
+ /// still be created by Discord. However, attempting to send a message will yield a
+ /// with a 403 as its
+ /// . There are currently no official workarounds by
+ /// Discord.
///
///
///
/// The following example attempts to send a direct message to the target user and logs the incident should
/// it fail.
- ///
- /// var channel = await user.GetOrCreateDMChannelAsync();
- /// try
- /// {
- /// await channel.SendMessageAsync("Awesome stuff!");
- /// }
- /// catch (Discord.Net.HttpException ex) when (ex.HttpCode == 403)
- /// {
- /// Console.WriteLine($"Boo, I cannot message {user}");
- /// }
- ///
+ ///
///
/// The options to be used when sending the request.
///
diff --git a/src/Discord.Net.Core/IDiscordClient.cs b/src/Discord.Net.Core/IDiscordClient.cs
index d6a863556..e1c900680 100644
--- a/src/Discord.Net.Core/IDiscordClient.cs
+++ b/src/Discord.Net.Core/IDiscordClient.cs
@@ -63,7 +63,7 @@ namespace Discord
/// Gets a generic channel.
///
///
- ///
+ ///
/// var channel = await _client.GetChannelAsync(381889909113225237);
/// if (channel != null && channel is IMessageChannel msgChannel)
/// {
@@ -194,7 +194,7 @@ namespace Discord
/// Gets a user.
///
///
- ///
+ ///
/// var user = await _client.GetUserAsync(168693960628371456);
/// if (user != null)
/// Console.WriteLine($"{user} is created at {user.CreatedAt}.";
@@ -212,7 +212,7 @@ namespace Discord
/// Gets a user.
///
///
- ///
+ ///
/// var user = await _client.GetUserAsync("Still", "2876");
/// if (user != null)
/// Console.WriteLine($"{user} is created at {user.CreatedAt}.";
@@ -232,7 +232,7 @@ namespace Discord
///
///
/// The following example gets the most optimal voice region from the collection.
- ///
+ ///
/// var regions = await client.GetVoiceRegionsAsync();
/// var optimalRegion = regions.FirstOrDefault(x => x.IsOptimal);
///
diff --git a/src/Discord.Net.Examples/Core/Entities/Channels/IGuildChannel.Examples.cs b/src/Discord.Net.Examples/Core/Entities/Channels/IGuildChannel.Examples.cs
new file mode 100644
index 000000000..d382ddbf3
--- /dev/null
+++ b/src/Discord.Net.Examples/Core/Entities/Channels/IGuildChannel.Examples.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+
+namespace Discord.Net.Examples.Core.Entities.Channels
+{
+ [PublicAPI]
+ internal class GuildChannelExamples
+ {
+ #region AddPermissionOverwriteAsyncRole
+
+ public async Task MuteRoleAsync(IRole role, IGuildChannel channel)
+ {
+ if (role == null) throw new ArgumentNullException(nameof(role));
+ if (channel == null) throw new ArgumentNullException(nameof(channel));
+
+ // Fetches the previous overwrite and bail if one is found
+ var previousOverwrite = channel.GetPermissionOverwrite(role);
+ if (previousOverwrite.HasValue && previousOverwrite.Value.SendMessages == PermValue.Deny)
+ throw new InvalidOperationException($"Role {role.Name} had already been muted in this channel.");
+
+ // Creates a new OverwritePermissions with send message set to deny and pass it into the method
+ await channel.AddPermissionOverwriteAsync(role, new OverwritePermissions(sendMessages: PermValue.Deny));
+ }
+
+ #endregion
+
+ #region AddPermissionOverwriteAsyncUser
+
+ public async Task MuteUserAsync(IGuildUser user, IGuildChannel channel)
+ {
+ if (user == null) throw new ArgumentNullException(nameof(user));
+ if (channel == null) throw new ArgumentNullException(nameof(channel));
+
+ // Fetches the previous overwrite and bail if one is found
+ var previousOverwrite = channel.GetPermissionOverwrite(user);
+ if (previousOverwrite.HasValue && previousOverwrite.Value.SendMessages == PermValue.Deny)
+ throw new InvalidOperationException($"User {user.Username} had already been muted in this channel.");
+
+ // Creates a new OverwritePermissions with send message set to deny and pass it into the method
+ await channel.AddPermissionOverwriteAsync(user, new OverwritePermissions(sendMessages: PermValue.Deny));
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Discord.Net.Examples/Core/Entities/Channels/IMessageChannel.Examples.cs b/src/Discord.Net.Examples/Core/Entities/Channels/IMessageChannel.Examples.cs
new file mode 100644
index 000000000..5f9d4b5d6
--- /dev/null
+++ b/src/Discord.Net.Examples/Core/Entities/Channels/IMessageChannel.Examples.cs
@@ -0,0 +1,114 @@
+using System;
+using System.Linq;
+using System.Net.Http;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+
+namespace Discord.Net.Examples.Core.Entities.Channels
+{
+ [PublicAPI]
+ internal class MessageChannelExamples
+ {
+ #region GetMessagesAsync.FromId.BeginningMessages
+
+ public async Task PrintFirstMessages(IMessageChannel channel, int messageCount)
+ {
+ // Although the library does attempt to divide the messageCount by 100
+ // to comply to Discord's maximum message limit per request, sending
+ // too many could still cause the queue to clog up.
+ // The purpose of this exception is to discourage users from sending
+ // too many requests at once.
+ if (messageCount > 1000)
+ throw new InvalidOperationException("Too many messages requested.");
+
+ // Setting fromMessageId to 0 will make Discord
+ // default to the first message in channel.
+ var messages = await channel.GetMessagesAsync(
+ 0, Direction.After, messageCount)
+ .FlattenAsync();
+
+ // Print message content
+ foreach (var message in messages)
+ Console.WriteLine($"{message.Author} posted '{message.Content}' at {message.CreatedAt}.");
+ }
+
+ #endregion
+
+ public async Task GetMessagesExampleBody(IMessageChannel channel)
+ {
+#pragma warning disable IDISP001
+#pragma warning disable IDISP014
+ // We're just declaring this for the sample below.
+ // Ideally, you want to get or create your HttpClient
+ // from IHttpClientFactory.
+ // You get a bonus for reading the example source though!
+ var httpClient = new HttpClient();
+#pragma warning restore IDISP014
+#pragma warning restore IDISP001
+
+ // Another dummy method
+ Task LongRunningAsync()
+ {
+ return Task.Delay(0);
+ }
+
+ #region GetMessagesAsync.FromLimit.Standard
+
+ var messages = await channel.GetMessagesAsync(300).FlattenAsync();
+ var userMessages = messages.Where(x => x.Author.Id == 53905483156684800);
+
+ #endregion
+
+ #region GetMessagesAsync.FromMessage
+
+ var oldMessage = await channel.SendMessageAsync("boi");
+ var messagesFromMsg = await channel.GetMessagesAsync(oldMessage, Direction.Before, 5).FlattenAsync();
+
+ #endregion
+
+
+ #region GetMessagesAsync.FromId.FromMessage
+
+ await channel.GetMessagesAsync(442012544660537354, Direction.Before, 5).FlattenAsync();
+
+ #endregion
+
+ #region SendMessageAsync
+
+ var message = await channel.SendMessageAsync(DateTimeOffset.UtcNow.ToString("R"));
+ await Task.Delay(TimeSpan.FromSeconds(5))
+ .ContinueWith(x => message.DeleteAsync());
+
+ #endregion
+
+ #region SendFileAsync.FilePath
+
+ await channel.SendFileAsync("wumpus.txt", "good discord boi");
+
+ #endregion
+
+ #region SendFileAsync.FilePath.EmbeddedImage
+
+ await channel.SendFileAsync("b1nzy.jpg",
+ embed: new EmbedBuilder {ImageUrl = "attachment://b1nzy.jpg"}.Build());
+
+ #endregion
+
+
+ #region SendFileAsync.FileStream.EmbeddedImage
+
+ using (var b1nzyStream = await httpClient.GetStreamAsync("https://example.com/b1nzy"))
+ await channel.SendFileAsync(b1nzyStream, "b1nzy.jpg",
+ embed: new EmbedBuilder {ImageUrl = "attachment://b1nzy.jpg"}.Build());
+
+ #endregion
+
+ #region EnterTypingState
+
+ using (channel.EnterTypingState()) await LongRunningAsync();
+
+ #endregion
+
+ }
+ }
+}
diff --git a/src/Discord.Net.Examples/Core/Entities/Guilds/IGuild.Examples.cs b/src/Discord.Net.Examples/Core/Entities/Guilds/IGuild.Examples.cs
new file mode 100644
index 000000000..03144b232
--- /dev/null
+++ b/src/Discord.Net.Examples/Core/Entities/Guilds/IGuild.Examples.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+
+namespace Discord.Net.Examples.Core.Entities.Guilds
+{
+ [PublicAPI]
+ internal class GuildExamples
+ {
+ #region CreateTextChannelAsync
+ public async Task CreateTextChannelUnderWumpus(IGuild guild, string name)
+ {
+ var categories = await guild.GetCategoriesAsync();
+ var targetCategory = categories.FirstOrDefault(x => x.Name == "wumpus");
+ if (targetCategory == null) return;
+ await guild.CreateTextChannelAsync(name, x =>
+ {
+ x.CategoryId = targetCategory.Id;
+ x.Topic = $"This channel was created at {DateTimeOffset.UtcNow}.";
+ });
+ }
+ #endregion
+ }
+}
diff --git a/src/Discord.Net.Examples/Core/Entities/Users/IUser.Examples.cs b/src/Discord.Net.Examples/Core/Entities/Users/IUser.Examples.cs
new file mode 100644
index 000000000..79a90b46d
--- /dev/null
+++ b/src/Discord.Net.Examples/Core/Entities/Users/IUser.Examples.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Net;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+
+namespace Discord.Net.Examples.Core.Entities.Users
+{
+ [PublicAPI]
+ internal class UserExamples
+ {
+ #region GetAvatarUrl
+
+ public async Task GetAvatarAsync(IUser user, ITextChannel textChannel)
+ {
+ var userAvatarUrl = user.GetAvatarUrl() ?? user.GetDefaultAvatarUrl();
+ await textChannel.SendMessageAsync(userAvatarUrl);
+ }
+
+ #endregion
+
+ #region GetOrCreateDMChannelAsync
+
+ public async Task MessageUserAsync(IUser user)
+ {
+ var channel = await user.GetOrCreateDMChannelAsync();
+ try
+ {
+ await channel.SendMessageAsync("Awesome stuff!");
+ }
+ catch (Discord.Net.HttpException ex) when (ex.HttpCode == HttpStatusCode.Forbidden)
+ {
+ Console.WriteLine($"Boo, I cannot message {user}.");
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Discord.Net.Examples/Discord.Net.Examples.csproj b/src/Discord.Net.Examples/Discord.Net.Examples.csproj
new file mode 100644
index 000000000..b02d2e6d8
--- /dev/null
+++ b/src/Discord.Net.Examples/Discord.Net.Examples.csproj
@@ -0,0 +1,21 @@
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Discord.Net.Examples/WebSocket/BaseSocketClient.Events.Examples.cs b/src/Discord.Net.Examples/WebSocket/BaseSocketClient.Events.Examples.cs
new file mode 100644
index 000000000..387584877
--- /dev/null
+++ b/src/Discord.Net.Examples/WebSocket/BaseSocketClient.Events.Examples.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using Discord.WebSocket;
+using JetBrains.Annotations;
+
+namespace Discord.Net.Examples.WebSocket
+{
+ [PublicAPI]
+ internal class BaseSocketClientExamples
+ {
+ #region ReactionAdded
+
+ public void HookReactionAdded(BaseSocketClient client)
+ => client.ReactionAdded += HandleReactionAddedAsync;
+
+ public async Task HandleReactionAddedAsync(Cacheable cachedMessage,
+ ISocketMessageChannel originChannel, SocketReaction reaction)
+ {
+ var message = await cachedMessage.GetOrDownloadAsync();
+ if (message != null && reaction.User.IsSpecified)
+ Console.WriteLine($"{reaction.User.Value} just added a reaction '{reaction.Emote}' " +
+ $"to {message.Author}'s message ({message.Id}).");
+ }
+
+ #endregion
+
+ #region ChannelCreated
+
+ public void HookChannelCreated(BaseSocketClient client)
+ => client.ChannelCreated += HandleChannelCreated;
+
+ public Task HandleChannelCreated(SocketChannel channel)
+ {
+ if (channel is SocketGuildChannel guildChannel)
+ Console.WriteLine($"A new channel '{guildChannel.Name}'({guildChannel.Id}, {guildChannel.GetType()})"
+ + $"has been created at {guildChannel.CreatedAt}.");
+ return Task.CompletedTask;
+ }
+
+ #endregion
+
+ #region ChannelDestroyed
+
+ public void HookChannelDestroyed(BaseSocketClient client)
+ => client.ChannelDestroyed += HandleChannelDestroyed;
+
+ public Task HandleChannelDestroyed(SocketChannel channel)
+ {
+ if (channel is SocketGuildChannel guildChannel)
+ Console.WriteLine(
+ $"A new channel '{guildChannel.Name}'({guildChannel.Id}, {guildChannel.GetType()}) has been deleted.");
+ return Task.CompletedTask;
+ }
+
+ #endregion
+
+ #region ChannelUpdated
+
+ public void HookChannelUpdated(BaseSocketClient client)
+ => client.ChannelUpdated += HandleChannelRename;
+
+ public Task HandleChannelRename(SocketChannel beforeChannel, SocketChannel afterChannel)
+ {
+ if (beforeChannel is SocketGuildChannel beforeGuildChannel &&
+ afterChannel is SocketGuildChannel afterGuildChannel)
+ if (beforeGuildChannel.Name != afterGuildChannel.Name)
+ Console.WriteLine(
+ $"A channel ({beforeChannel.Id}) is renamed from {beforeGuildChannel.Name} to {afterGuildChannel.Name}.");
+ return Task.CompletedTask;
+ }
+
+ #endregion
+
+ #region MessageReceived
+
+ private readonly ulong[] _targetUserIds = {168693960628371456, 53905483156684800};
+
+ public void HookMessageReceived(BaseSocketClient client)
+ => client.MessageReceived += HandleMessageReceived;
+
+ public Task HandleMessageReceived(SocketMessage message)
+ {
+ // check if the message is a user message as opposed to a system message (e.g. Clyde, pins, etc.)
+ if (!(message is SocketUserMessage userMessage)) return Task.CompletedTask;
+ // check if the message origin is a guild message channel
+ if (!(userMessage.Channel is SocketTextChannel textChannel)) return Task.CompletedTask;
+ // check if the target user was mentioned
+ var targetUsers = userMessage.MentionedUsers.Where(x => _targetUserIds.Contains(x.Id));
+ foreach (var targetUser in targetUsers)
+ Console.WriteLine(
+ $"{targetUser} was mentioned in the message '{message.Content}' by {message.Author} in {textChannel.Name}.");
+ return Task.CompletedTask;
+ }
+
+ #endregion
+
+ #region MessageDeleted
+
+ public void HookMessageDeleted(BaseSocketClient client)
+ => client.MessageDeleted += HandleMessageDelete;
+
+ public Task HandleMessageDelete(Cacheable cachedMessage, ISocketMessageChannel channel)
+ {
+ // check if the message exists in cache; if not, we cannot report what was removed
+ if (!cachedMessage.HasValue) return Task.CompletedTask;
+ var message = cachedMessage.Value;
+ Console.WriteLine(
+ $"A message ({message.Id}) from {message.Author} was removed from the channel {channel.Name} ({channel.Id}):"
+ + Environment.NewLine
+ + message.Content);
+ return Task.CompletedTask;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
index a847998b5..902d2c9a8 100644
--- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
+++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
@@ -405,7 +405,7 @@ namespace Discord.Rest
///
///
/// The following example creates a new text channel under an existing category named Wumpus with a set topic.
- ///
+ ///
/// var categories = await guild.GetCategoriesAsync();
/// var targetCategory = categories.FirstOrDefault(x => x.Name == "wumpus");
/// if (targetCategory == null) return;
diff --git a/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs b/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs
index b1b7d17b5..4f94f702d 100644
--- a/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs
+++ b/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs
@@ -18,6 +18,10 @@ namespace Discord.WebSocket
/// see the derived classes of for more details.
///
///
+ ///
+ ///
+ ///
public event Func ChannelCreated
{
add { _channelCreatedEvent.Add(value); }
@@ -36,6 +40,10 @@ namespace Discord.WebSocket
/// see the derived classes of for more details.
///
///
+ ///
+ ///
+ ///
public event Func ChannelDestroyed {
add { _channelDestroyedEvent.Add(value); }
remove { _channelDestroyedEvent.Remove(value); }
@@ -54,6 +62,10 @@ namespace Discord.WebSocket
/// for more details.
///
///
+ ///
+ ///
+ ///
public event Func ChannelUpdated {
add { _channelUpdatedEvent.Add(value); }
remove { _channelUpdatedEvent.Remove(value); }
@@ -74,6 +86,11 @@ namespace Discord.WebSocket
/// derived classes of for more details.
///
///
+ ///
+ /// The example below checks if the newly received message contains the target user.
+ ///
+ ///
public event Func MessageReceived {
add { _messageReceivedEvent.Add(value); }
remove { _messageReceivedEvent.Remove(value); }
@@ -102,6 +119,10 @@ namespace Discord.WebSocket
/// parameter.
///
///
+ ///
+ ///
+ ///
public event Func, ISocketMessageChannel, Task> MessageDeleted {
add { _messageDeletedEvent.Add(value); }
remove { _messageDeletedEvent.Remove(value); }
@@ -134,6 +155,35 @@ namespace Discord.WebSocket
}
internal readonly AsyncEvent, SocketMessage, ISocketMessageChannel, Task>> _messageUpdatedEvent = new AsyncEvent, SocketMessage, ISocketMessageChannel, Task>>();
/// Fired when a reaction is added to a message.
+ ///
+ ///
+ /// This event is fired when a reaction is added to a user message. The event handler must return a
+ /// and accept a , an
+ /// , and a as its parameter.
+ ///
+ ///
+ /// If caching is enabled via , the
+ /// entity will contain the original message; otherwise, in event
+ /// that the message cannot be retrieved, the snowflake ID of the message is preserved in the
+ /// .
+ ///
+ ///
+ /// The source channel of the reaction addition will be passed into the
+ /// parameter.
+ ///
+ ///
+ /// The reaction that was added will be passed into the parameter.
+ ///
+ ///
+ /// When fetching the reaction from this event, a user may not be provided under
+ /// . Please see the documentation of the property for more
+ /// information.
+ ///
+ ///
+ ///
+ ///
+ ///
public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionAdded {
add { _reactionAddedEvent.Add(value); }
remove { _reactionAddedEvent.Remove(value); }
diff --git a/src/Discord.Net.WebSocket/BaseSocketClient.cs b/src/Discord.Net.WebSocket/BaseSocketClient.cs
index abaf5989e..4ab149832 100644
--- a/src/Discord.Net.WebSocket/BaseSocketClient.cs
+++ b/src/Discord.Net.WebSocket/BaseSocketClient.cs
@@ -97,13 +97,15 @@ namespace Discord.WebSocket
///
/// This method gets the user present in the WebSocket cache with the given condition.
///
- /// Sometimes a user may return null due to Discord not sending offline users in large
- /// guilds (i.e. guild with 100+ members) actively. To download users on startup, consider enabling
- /// .
+ /// Sometimes a user may return null due to Discord not sending offline users in large guilds
+ /// (i.e. guild with 100+ members) actively. To download users on startup and to see more information
+ /// about this subject, see .
///
///
/// This method does not attempt to fetch users that the logged-in user does not have access to (i.e.
- /// users who don't share mutual guild(s) with the current user).
+ /// users who don't share mutual guild(s) with the current user). If you wish to get a user that you do
+ /// not have access to, consider using the REST implementation of
+ /// .
///
///
///
@@ -114,20 +116,22 @@ namespace Discord.WebSocket
///
/// Gets a user.
///
- /// The name of the user.
- /// The discriminator value of the user.
///
/// This method gets the user present in the WebSocket cache with the given condition.
///
- /// Sometimes a user may return null due to Discord not sending offline users in large
- /// guilds (i.e. guild with 100+ members) actively. To download users on startup, consider enabling
- /// .
+ /// Sometimes a user may return null due to Discord not sending offline users in large guilds
+ /// (i.e. guild with 100+ members) actively. To download users on startup and to see more information
+ /// about this subject, see .
///
///
/// This method does not attempt to fetch users that the logged-in user does not have access to (i.e.
- /// users who don't share mutual guild(s) with the current user).
+ /// users who don't share mutual guild(s) with the current user). If you wish to get a user that you do
+ /// not have access to, consider using the REST implementation of
+ /// .
///
///
+ /// The name of the user.
+ /// The discriminator value of the user.
///
/// A generic WebSocket-based user; null when the user cannot be found.
///
diff --git a/src/Discord.Net.WebSocket/DiscordSocketConfig.cs b/src/Discord.Net.WebSocket/DiscordSocketConfig.cs
index 9674cff52..8571cd5c9 100644
--- a/src/Discord.Net.WebSocket/DiscordSocketConfig.cs
+++ b/src/Discord.Net.WebSocket/DiscordSocketConfig.cs
@@ -94,7 +94,9 @@ namespace Discord.WebSocket
/// Please note that it can be difficult to fill the cache completely on large guilds depending on the
/// traffic. If you are using the command system, the default user TypeReader may fail to find the user
/// due to this issue. This may be resolved at v3 of the library. Until then, you may want to consider
- /// overriding the TypeReader and use as a backup.
+ /// overriding the TypeReader and use
+ ///
+ /// or as a backup.
///
///
public bool AlwaysDownloadUsers { get; set; } = false;
diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
index 955e219af..d952b3d92 100644
--- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
+++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
@@ -535,7 +535,7 @@ namespace Discord.WebSocket
///
///
/// The following example creates a new text channel under an existing category named Wumpus with a set topic.
- ///
+ ///
/// var categories = await guild.GetCategoriesAsync();
/// var targetCategory = categories.FirstOrDefault(x => x.Name == "wumpus");
/// if (targetCategory == null) return;
diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs
index 9c3a5f32b..32cac7d8b 100644
--- a/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs
+++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs
@@ -10,6 +10,11 @@ namespace Discord.WebSocket
///
/// Gets the ID of the user who added the reaction.
///
+ ///
+ /// This property retrieves the snowflake identifier of the user responsible for this reaction. This
+ /// property will always contain the user identifier in event that
+ /// cannot be retrieved.
+ ///
///
/// A user snowflake identifier associated with the user.
///
@@ -17,6 +22,18 @@ namespace Discord.WebSocket
///
/// Gets the user who added the reaction if possible.
///
+ ///
+ ///
+ /// This property attempts to retrieve a WebSocket-cached user that is responsible for this reaction from
+ /// the client. In other words, when the user is not in the WebSocket cache, this property may not
+ /// contain a value, leaving the only identifiable information to be
+ /// .
+ ///
+ ///
+ /// If you wish to obtain an identifiable user object, consider utilizing
+ /// which will attempt to retrieve the user from REST.
+ ///
+ ///
///
/// A user object where possible; a value is not always returned.
///