Browse Source

docs: Main docs update (#1304)

* Remove template in favor of official samples

* Fixed a variable name copy pasta mistake

line 35 was _database.GetData() instead of DBService.GetData()

* Experimental theme change

* Change paragraph, code, heading fonts
* Widen viewport

* Update DocFX.Plugins.LastModified v1.2.3

* Exclude Discord.API in docs

* Add remarks for SocketReaction properties

* Add examples for BaseSocketClient.Events

* Add additional clarification for some methods

* Move IUser and IGuildChannel examples

* Clarify several guides samples with notes

- Reword TypeReader comment to avoid giving the idea that the sample itself is "obsolete"
- Remove CommandException logging comment regarding C#7.0 as the version is now the standard across VS2017 and up
- Remove suggestion about handling result in command handler since it is now advised to use CommandExecuted instead
+ Add additional comment to clarify ctor for DI setup

* Add/migrate code examples

* Incorporate material design theme

License @ https://github.com/ovasquez

* Update installation and nightly guide

* Fix improper indentations made obvious by the widen viewport
* Fix minor grammar issues
+ Add installation for nightly build using dotnet CLI

* Fix nav level indentation

* Revise "Your First Bot" article

* Merge some paragraphs to avoid clutter while keeping readability
* Reword the use of command framework
+ Add additional warning/note about environment variable

* Add additional indent level

* Fix indentation text warping

* Remove connections sample

* Update logging sample

Remove redundant part of the sample

* Remove mention of RPC

* Remove misleading section about commands

- Remove command sample from complete snippet
* Revise "Your First Bot" command paragraphs
* Change wording to hint devs that additional command parser packages may be available, as more and more begin to crop up

* Update themes

* Add XML docs contribution guidelines


Update guidelines

* Update CommandExecuted remarks

* Fix precondition remarks typo
no one saw that ok

* Fix permission sample in docfx

* Fix IMessageChannel samples

* Update docs/_template/light-dark-theme/styles/docfx.vendor.minify.css

Co-Authored-By: Still34 <341464@gmail.com>

* Update docs/_template/light-dark-theme/styles/material.css

Co-Authored-By: Still34 <341464@gmail.com>

* Update docs/_template/light-dark-theme/styles/material.css

Co-Authored-By: Still34 <341464@gmail.com>
tags/2.1.0
Still Hsu Christopher F 6 years ago
parent
commit
4309550ca0
64 changed files with 986 additions and 345 deletions
  1. +18
    -1
      Discord.Net.sln
  2. +35
    -10
      docs/CONTRIBUTING.md
  3. +2
    -2
      docs/_overwrites/Commands/PreconditionAttribute.Remarks.Inclusion.md
  4. BIN
      docs/_template/last-modified/plugins/LastModifiedPostProcessor.dll
  5. BIN
      docs/_template/last-modified/plugins/LibGit2Sharp.dll
  6. +2
    -2
      docs/_template/last-modified/plugins/LibGit2Sharp.dll.config
  7. BIN
      docs/_template/last-modified/plugins/lib/alpine-x64/libgit2-8e0b172.so
  8. BIN
      docs/_template/last-modified/plugins/lib/alpine-x64/libgit2-a904fc6.so
  9. BIN
      docs/_template/last-modified/plugins/lib/debian.9-x64/libgit2-8e0b172.so
  10. BIN
      docs/_template/last-modified/plugins/lib/debian.9-x64/libgit2-a904fc6.so
  11. BIN
      docs/_template/last-modified/plugins/lib/fedora-x64/libgit2-8e0b172.so
  12. BIN
      docs/_template/last-modified/plugins/lib/fedora-x64/libgit2-a904fc6.so
  13. BIN
      docs/_template/last-modified/plugins/lib/linux-x64/libgit2-8e0b172.so
  14. BIN
      docs/_template/last-modified/plugins/lib/linux-x64/libgit2-a904fc6.so
  15. BIN
      docs/_template/last-modified/plugins/lib/osx/libgit2-8e0b172.dylib
  16. BIN
      docs/_template/last-modified/plugins/lib/osx/libgit2-a904fc6.dylib
  17. BIN
      docs/_template/last-modified/plugins/lib/rhel-x64/libgit2-8e0b172.so
  18. BIN
      docs/_template/last-modified/plugins/lib/rhel-x64/libgit2-a904fc6.so
  19. BIN
      docs/_template/last-modified/plugins/lib/ubuntu.18.04-x64/libgit2-a904fc6.so
  20. BIN
      docs/_template/last-modified/plugins/lib/win32/x64/git2-8e0b172.dll
  21. BIN
      docs/_template/last-modified/plugins/lib/win32/x64/git2-a904fc6.dll
  22. BIN
      docs/_template/last-modified/plugins/lib/win32/x86/git2-8e0b172.dll
  23. BIN
      docs/_template/last-modified/plugins/lib/win32/x86/git2-a904fc6.dll
  24. +21
    -0
      docs/_template/light-dark-theme/docfx-material-license.md
  25. +1
    -0
      docs/_template/light-dark-theme/partials/head.tmpl.partial
  26. +14
    -0
      docs/_template/light-dark-theme/styles/dark.css
  27. +0
    -1
      docs/_template/light-dark-theme/styles/docfx.vendor.minify.css
  28. +12
    -3
      docs/_template/light-dark-theme/styles/gray.css
  29. +68
    -12
      docs/_template/light-dark-theme/styles/master.css
  30. +199
    -0
      docs/_template/light-dark-theme/styles/material.css
  31. +3
    -0
      docs/filterConfig.yml
  32. +3
    -0
      docs/guides/commands/samples/dependency-injection/dependency_map_setup.cs
  33. +2
    -2
      docs/guides/commands/samples/dependency-injection/dependency_module.cs
  34. +2
    -11
      docs/guides/commands/samples/intro/command_handler.cs
  35. +0
    -1
      docs/guides/commands/samples/post-execution/command_exception_log.cs
  36. +3
    -2
      docs/guides/commands/samples/typereaders/typereader.cs
  37. +0
    -4
      docs/guides/concepts/connections.md
  38. +1
    -1
      docs/guides/concepts/entities.md
  39. +0
    -23
      docs/guides/concepts/samples/connections.cs
  40. +14
    -19
      docs/guides/concepts/samples/logging.cs
  41. +30
    -68
      docs/guides/getting_started/first-bot.md
  42. +33
    -25
      docs/guides/getting_started/installing.md
  43. +14
    -3
      docs/guides/getting_started/nightlies.md
  44. +0
    -10
      docs/guides/getting_started/samples/first-bot/complete.cs
  45. +3
    -6
      docs/guides/introduction/intro.md
  46. +2
    -9
      src/Discord.Net.Commands/CommandService.cs
  47. +8
    -36
      src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs
  48. +33
    -46
      src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs
  49. +1
    -1
      src/Discord.Net.Core/Entities/Channels/ITextChannel.cs
  50. +2
    -10
      src/Discord.Net.Core/Entities/Guilds/IGuild.cs
  51. +9
    -20
      src/Discord.Net.Core/Entities/Users/IUser.cs
  52. +4
    -4
      src/Discord.Net.Core/IDiscordClient.cs
  53. +46
    -0
      src/Discord.Net.Examples/Core/Entities/Channels/IGuildChannel.Examples.cs
  54. +114
    -0
      src/Discord.Net.Examples/Core/Entities/Channels/IMessageChannel.Examples.cs
  55. +25
    -0
      src/Discord.Net.Examples/Core/Entities/Guilds/IGuild.Examples.cs
  56. +38
    -0
      src/Discord.Net.Examples/Core/Entities/Users/IUser.Examples.cs
  57. +21
    -0
      src/Discord.Net.Examples/Discord.Net.Examples.csproj
  58. +117
    -0
      src/Discord.Net.Examples/WebSocket/BaseSocketClient.Events.Examples.cs
  59. +1
    -1
      src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
  60. +50
    -0
      src/Discord.Net.WebSocket/BaseSocketClient.Events.cs
  61. +14
    -10
      src/Discord.Net.WebSocket/BaseSocketClient.cs
  62. +3
    -1
      src/Discord.Net.WebSocket/DiscordSocketConfig.cs
  63. +1
    -1
      src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
  64. +17
    -0
      src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs

+ 18
- 1
Discord.Net.sln View File

@@ -30,7 +30,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01_basic_ping_bot", "sample
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02_commands_framework", "samples\02_commands_framework\02_commands_framework.csproj", "{4E1F1F40-B1DD-40C9-A4B1-A2046A4C9C76}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02_commands_framework", "samples\02_commands_framework\02_commands_framework.csproj", "{4E1F1F40-B1DD-40C9-A4B1-A2046A4C9C76}"
EndProject 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 EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution 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|x64.Build.0 = Release|Any CPU
{9B4C4AFB-3D15-44C6-9E36-12ED625AAA26}.Release|x86.ActiveCfg = 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 {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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -188,6 +204,7 @@ Global
{F2FF84FB-F6AD-47E5-9EE5-18206CAE136E} = {BB59D5B5-E7B0-4BF4-8F82-D14431B2799B} {F2FF84FB-F6AD-47E5-9EE5-18206CAE136E} = {BB59D5B5-E7B0-4BF4-8F82-D14431B2799B}
{4E1F1F40-B1DD-40C9-A4B1-A2046A4C9C76} = {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} {9B4C4AFB-3D15-44C6-9E36-12ED625AAA26} = {BB59D5B5-E7B0-4BF4-8F82-D14431B2799B}
{7EA96B2B-4D71-458D-9423-839362DC38BE} = {D1F0271E-0EE2-4B66-AC3D-9871B7E1C4CF}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D2404771-EEC8-45F2-9D71-F3373F6C1495} SolutionGuid = {D2404771-EEC8-45F2-9D71-F3373F6C1495}


+ 35
- 10
docs/CONTRIBUTING.md View File

@@ -1,23 +1,46 @@
# Contributing to Docs # 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 * When referencing an object in the API, link to its page in the
API documentation API documentation
* Documentation should be written in an FAQ/Wiki-style format * Documentation should be written in an FAQ/Wiki-style format
* Documentation should be written in clear and proper English* * Documentation should be written in clear and proper English*


\* If anyone is interested in translating documentation into other \* 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 `<summary>` tag, use concise verbs. For example:

```cs
/// <summary> Gets or sets the guild user in this object. </summary>
public IGuildUser GuildUser { get; set; }
```


* Use a ruler set at 70 characters
* The `<summary>` tag should not be more than 3 lines long. Consider
simplifying the terms or using the `<remarks>` tag instead.
* When using the `<code>` tag, put the code sample within the
`src/Discord.Net.Examples` project under the corresponding path of
the object and surround the code with a `#region` tag.
* If the remarks you are looking to write are too long, consider
writing a shorter version in the XML docs while keeping the longer
version in the `overwrites` folder using the DocFX overwrites syntax.
* You may find an example of this in the samples provided within
the folder.

## Docs Guide Guidelines

* Use a ruler set at 70 characters (use the docs workspace provided
if you are using Visual Studio Code)
* Links should use long syntax * Links should use long syntax
* Pages should be short and concise, not broad and long * Pages should be short and concise, not broad and long


@@ -31,5 +54,7 @@ Please consult the [API Documentation] for more information.


## Recommended Reads ## Recommended Reads


* http://docs.microsoft.com
* http://flask.pocoo.org/docs/0.12/
* [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)

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

@@ -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 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 The most common use case being whether a user has sufficient
permission to execute the command. permission to execute the command.

BIN
docs/_template/last-modified/plugins/LastModifiedPostProcessor.dll View File


BIN
docs/_template/last-modified/plugins/LibGit2Sharp.dll View File


+ 2
- 2
docs/_template/last-modified/plugins/LibGit2Sharp.dll.config View File

@@ -1,4 +1,4 @@
<configuration> <configuration>
<dllmap os="linux" cpu="x86-64" wordsize="64" dll="git2-8e0b172" target="lib/linux-x64/libgit2-8e0b172.so" />
<dllmap os="osx" cpu="x86,x86-64" dll="git2-8e0b172" target="lib/osx/libgit2-8e0b172.dylib" />
<dllmap os="linux" cpu="x86-64" wordsize="64" dll="git2-a904fc6" target="lib/linux-x64/libgit2-a904fc6.so" />
<dllmap os="osx" cpu="x86,x86-64" dll="git2-a904fc6" target="lib/osx/libgit2-a904fc6.dylib" />
</configuration> </configuration>

BIN
docs/_template/last-modified/plugins/lib/alpine-x64/libgit2-8e0b172.so View File


BIN
docs/_template/last-modified/plugins/lib/alpine-x64/libgit2-a904fc6.so View File


BIN
docs/_template/last-modified/plugins/lib/debian.9-x64/libgit2-8e0b172.so View File


BIN
docs/_template/last-modified/plugins/lib/debian.9-x64/libgit2-a904fc6.so View File


BIN
docs/_template/last-modified/plugins/lib/fedora-x64/libgit2-8e0b172.so View File


BIN
docs/_template/last-modified/plugins/lib/fedora-x64/libgit2-a904fc6.so View File


BIN
docs/_template/last-modified/plugins/lib/linux-x64/libgit2-8e0b172.so View File


BIN
docs/_template/last-modified/plugins/lib/linux-x64/libgit2-a904fc6.so View File


BIN
docs/_template/last-modified/plugins/lib/osx/libgit2-8e0b172.dylib View File


BIN
docs/_template/last-modified/plugins/lib/osx/libgit2-a904fc6.dylib View File


BIN
docs/_template/last-modified/plugins/lib/rhel-x64/libgit2-8e0b172.so View File


BIN
docs/_template/last-modified/plugins/lib/rhel-x64/libgit2-a904fc6.so View File


BIN
docs/_template/last-modified/plugins/lib/ubuntu.18.04-x64/libgit2-a904fc6.so View File


BIN
docs/_template/last-modified/plugins/lib/win32/x64/git2-8e0b172.dll View File


BIN
docs/_template/last-modified/plugins/lib/win32/x64/git2-a904fc6.dll View File


BIN
docs/_template/last-modified/plugins/lib/win32/x86/git2-8e0b172.dll View File


BIN
docs/_template/last-modified/plugins/lib/win32/x86/git2-a904fc6.dll View File


+ 21
- 0
docs/_template/light-dark-theme/docfx-material-license.md View File

@@ -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.

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

@@ -15,6 +15,7 @@
<link rel="stylesheet" href="{{_rel}}styles/docfx.css"> <link rel="stylesheet" href="{{_rel}}styles/docfx.css">
<link rel="stylesheet" href="{{_rel}}styles/master.css"> <link rel="stylesheet" href="{{_rel}}styles/master.css">
<link rel="stylesheet" href="{{_rel}}styles/main.css"> <link rel="stylesheet" href="{{_rel}}styles/main.css">
<link rel="stylesheet" href="{{_rel}}styles/material.css">
<link rel="stylesheet" href="{{_rel}}styles/theme-switcher.css"> <link rel="stylesheet" href="{{_rel}}styles/theme-switcher.css">
<link href="https://cdn.rawgit.com/noelboss/featherlight/1.7.6/release/featherlight.min.css" type="text/css" rel="stylesheet" /> <link href="https://cdn.rawgit.com/noelboss/featherlight/1.7.6/release/featherlight.min.css" type="text/css" rel="stylesheet" />
<meta name="theme-color" content="#99AAB5"/> <meta name="theme-color" content="#99AAB5"/>


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

@@ -7,6 +7,15 @@ body {
color: #C0C0C0; color: #C0C0C0;
} }


h1,
h2,
h3,
h4,
h5,
h6 {
color: #E0E0E0;
}

button, button,
a { a {
color: #64B5F6; color: #64B5F6;
@@ -258,6 +267,11 @@ tbody>tr {
border-top: 2px solid rgb(173, 173, 173) border-top: 2px solid rgb(173, 173, 173)
} }


/* top navbar */
.navbar-inverse[role="navigation"] {
background-color: #2C2F33;
}

/* select */ /* select */


select { select {


+ 0
- 1
docs/_template/light-dark-theme/styles/docfx.vendor.minify.css View File

@@ -361,7 +361,6 @@ pre {
word-break: break-all; word-break: break-all;
word-wrap: break-word; word-wrap: break-word;
color: #333; color: #333;
border: 1px solid #ccc;
border-radius: 4px; border-radius: 4px;
} }




+ 12
- 3
docs/_template/light-dark-theme/styles/gray.css View File

@@ -7,6 +7,15 @@ body {
color: #dddddd; color: #dddddd;
} }


h1,
h2,
h3,
h4,
h5,
h6 {
color: #EEEEEE;
}

button, button,
a { a {
color: #64B5F6; color: #64B5F6;
@@ -39,13 +48,13 @@ hr {
} }


/* top navbar */ /* top navbar */
.navbar-inverse[role="navigation"] {
/*.navbar-inverse[role="navigation"] {
background-color: #2C2F33; background-color: #2C2F33;
}
}*/


/* sub navbar (below top) */ /* sub navbar (below top) */
.subnav { .subnav {
background: #282B2F
background: rgb(69, 75, 82)
} }






+ 68
- 12
docs/_template/light-dark-theme/styles/master.css View File

@@ -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, html,
body { body {
font-family: 'Titillium Web', 'Segoe UI', Tahoma, Helvetica, sans-serif;
font-family: 'Roboto', 'Segoe UI', Tahoma, Helvetica, sans-serif;
font-display: optional; font-display: optional;
height: 100%; height: 100%;
font-size: 15px; font-size: 15px;
scroll-behavior: smooth; 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 #logo
{ {
max-width: 100px; max-width: 100px;
@@ -25,6 +48,14 @@ li,
line-height: 160%; 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 { img {
box-shadow: 0px 0px 3px 0px rgb(66, 66, 66); box-shadow: 0px 0px 3px 0px rgb(66, 66, 66);
max-width: 95% !important; max-width: 95% !important;
@@ -57,16 +88,6 @@ article.content h6{
transition: all .25s ease-in-out; 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 { .sideaffix {
line-height: 140%; line-height: 140%;
} }
@@ -173,3 +194,38 @@ span.arrow-d{
span.arrow-r{ span.arrow-r{
top: 6px; position: relative; 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%;
}

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

@@ -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);
}

+ 3
- 0
docs/filterConfig.yml View File

@@ -4,4 +4,7 @@ apiRules:
type: Namespace type: Namespace
- exclude: - exclude:
uidRegex: ^Discord\.Analyzers$ uidRegex: ^Discord\.Analyzers$
type: Namespace
- exclude:
uidRegex: ^Discord\.API$
type: Namespace type: Namespace

+ 3
- 0
docs/guides/commands/samples/dependency-injection/dependency_map_setup.cs View File

@@ -3,6 +3,9 @@ public class Initialize
private readonly CommandService _commands; private readonly CommandService _commands;
private readonly DiscordSocketClient _client; 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) public Initialize(CommandService commands = null, DiscordSocketClient client = null)
{ {
_commands = commands ?? new CommandService(); _commands = commands ?? new CommandService();


+ 2
- 2
docs/guides/commands/samples/dependency-injection/dependency_module.cs View File

@@ -32,6 +32,6 @@ public class DatabaseModule : ModuleBase<SocketCommandContext>
[Command("read")] [Command("read")]
public async Task ReadFromDbAsync() public async Task ReadFromDbAsync()
{ {
await ReplyAsync(_database.GetData());
await ReplyAsync(DbService.GetData());
} }
}
}

+ 2
- 11
docs/guides/commands/samples/intro/command_handler.cs View File

@@ -3,6 +3,7 @@ public class CommandHandler
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly CommandService _commands; private readonly CommandService _commands;


// Retrieve client and CommandService instance via ctor
public CommandHandler(DiscordSocketClient client, CommandService commands) public CommandHandler(DiscordSocketClient client, CommandService commands)
{ {
_commands = commands; _commands = commands;
@@ -46,19 +47,9 @@ public class CommandHandler


// Execute the command with the command context we just // Execute the command with the command context we just
// created, along with the service provider for precondition checks. // 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, context: context,
argPos: argPos, argPos: argPos,
services: null); 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);
} }
} }

+ 0
- 1
docs/guides/commands/samples/post-execution/command_exception_log.cs View File

@@ -1,6 +1,5 @@
public async Task LogAsync(LogMessage logMessage) public async Task LogAsync(LogMessage logMessage)
{ {
// This casting type requries C#7
if (logMessage.Exception is CommandException cmdException) if (logMessage.Exception is CommandException cmdException)
{ {
// We can tell the user that something unexpected has happened // We can tell the user that something unexpected has happened


+ 3
- 2
docs/guides/commands/samples/typereaders/typereader.cs View File

@@ -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;
using Discord.Commands; using Discord.Commands;




+ 0
- 4
docs/guides/concepts/connections.md View File

@@ -35,10 +35,6 @@ sync and has a completed guild cache.


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


### Samples

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

## Reconnection ## Reconnection


> [!TIP] > [!TIP]


+ 1
- 1
docs/guides/concepts/entities.md View File

@@ -7,7 +7,7 @@ title: Entities


> [!NOTE] > [!NOTE]
> This article is written with the Socket variants of entities in mind, > 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.Net provides a versatile entity system for navigating the
Discord API. Discord API.


+ 0
- 23
docs/guides/concepts/samples/connections.cs View File

@@ -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);
}
}

+ 14
- 19
docs/guides/concepts/samples/logging.cs View File

@@ -1,29 +1,24 @@
using Discord; using Discord;
using Discord.WebSocket; 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; return Task.CompletedTask;
} }
} }

+ 30
- 68
docs/guides/getting_started/first-bot.md View File

@@ -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] Since we are writing a bot, we will be using a [DiscordSocketClient]
along with socket entities. See @Guides.GettingStarted.Terminology 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 Before connecting, we should hook the client's `Log` event to the
log handler that we had just created. Events in Discord.Net work 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] Next, you will need to "log in to Discord" with the [LoginAsync]
method with the application's "token." method with the application's "token."


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

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


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

> [!IMPORTANT] > [!IMPORTANT]
> Your bot's token can be used to gain total access to your bot, so > 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. > 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 We may now invoke the client's [StartAsync] method, which will
start connection/reconnection logic. It is important to note that start connection/reconnection logic. It is important to note that
**this method will return as soon as connection logic has been started!** **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 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 handler. This means that you should **not** directly be interacting with
the client before it is fully ready. 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 At this point, feel free to start your program and see your bot come
online in Discord. online in Discord.


> [!TIP]
> [!WARNING]
> Getting a warning about `A supplied token was invalid.` and/or > Getting a warning about `A supplied token was invalid.` and/or
> having trouble logging in? Double-check whether you have put in > having trouble logging in? Double-check whether you have put in
> the correct credentials and make sure that it is _not_ a client > the correct credentials and make sure that it is _not_ a client
> secret, which is different from a token. > secret, which is different from a token.


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


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

[DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient [DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient
[LoginAsync]: xref:Discord.Rest.BaseDiscordClient.LoginAsync* [LoginAsync]: xref:Discord.Rest.BaseDiscordClient.LoginAsync*
[StartAsync]: xref:Discord.WebSocket.DiscordSocketClient.StartAsync* [StartAsync]: xref:Discord.WebSocket.DiscordSocketClient.StartAsync*
[installation guide]: xref:Guides.GettingStarted.Installation [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 [completed program]: samples/first-bot/complete.cs


# Building a bot with commands # 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. For reference, view an [annotated example] of this structure.




+ 33
- 25
docs/guides/getting_started/installing.md View File

@@ -42,37 +42,45 @@ published to our [MyGet feed]. See


### [Using Visual Studio](#tab/vs-install) ### [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 2. In the Solution Explorer, find the "Dependencies" element under your
bot's project.
3. Right click on "Dependencies", and select "Manage NuGet packages."
![Step 3](images/install-vs-deps.png)
4. In the "Browse" tab, search for `Discord.Net`.
5. Install the `Discord.Net` package.
![Step 5](images/install-vs-nuget.png)
bot's project
3. Right click on "Dependencies", and select "Manage NuGet packages"

![Step 3](images/install-vs-deps.png)

4. In the "Browse" tab, search for `Discord.Net`
5. Install the `Discord.Net` package

![Step 5](images/install-vs-nuget.png)


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

![Step 2](images/install-rider-nuget-manager.png)

3. In the "Packages" tab, search for `Discord.Net`

![Step 3](images/install-rider-search.png)

4. Install by adding the package to your project

![Step 4](images/install-rider-add.png)


### [Using Visual Studio Code](#tab/vs-code) ### [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)] [!code[Sample .csproj](samples/project.xml)]


### [Using dotnet CLI](#tab/dotnet-cli) ### [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: 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 2. Configure your [DiscordSocketClient] to use these custom providers
over the default ones. 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)] [!code-csharp[Example](samples/netstd11.cs)]




+ 14
- 3
docs/guides/getting_started/nightlies.md View File

@@ -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 Depending on which IDE you use, there are many different ways of
adding the feed to your package source. 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` 1. Go to `Tools` > `NuGet Package Manager` > `Package Manager Settings`

![VS](images/nightlies-vs-step1.png) ![VS](images/nightlies-vs-step1.png)

2. Go to `Package Sources` 2. Go to `Package Sources`

![Package Sources](images/nightlies-vs-step2.png) ![Package Sources](images/nightlies-vs-step2.png)

3. Click on the add icon 3. Click on the add icon
4. Fill in the desired name and source as shown below and hit `Update` 4. Fill in the desired name and source as shown below and hit `Update`

![Add Source](images/nightlies-vs-step4.png) ![Add Source](images/nightlies-vs-step4.png)


> [!NOTE] > [!NOTE]
> Remember to tick the `Include prerelease` checkbox to see the
> Remember to tick the `Include pre-release` checkbox to see the
> nightly builds! > nightly builds!
> ![Checkbox](images/nightlies-vs-note.png) > ![Checkbox](images/nightlies-vs-note.png)


### [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 If you plan on deploying your bot or developing outside of Visual
Studio, you will need to create a local NuGet configuration file for Studio, you will need to create a local NuGet configuration file for


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

@@ -9,7 +9,6 @@ public class Program
{ {
_client = new DiscordSocketClient(); _client = new DiscordSocketClient();
_client.Log += Log; _client.Log += Log;
_client.MessageReceived += MessageReceivedAsync;
await _client.LoginAsync(TokenType.Bot, await _client.LoginAsync(TokenType.Bot,
Environment.GetEnvironmentVariable("DiscordToken")); Environment.GetEnvironmentVariable("DiscordToken"));
await _client.StartAsync(); await _client.StartAsync();
@@ -17,15 +16,6 @@ public class Program
// Block this task until the program is closed. // Block this task until the program is closed.
await Task.Delay(-1); await Task.Delay(-1);
} }

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

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


+ 3
- 6
docs/guides/introduction/intro.md View File

@@ -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 that being said, feel free to visit us on Discord at the link below
if you have any questions! 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] > [!NOTE]
> Please note that you should *not* try to blindly copy paste > Please note that you should *not* try to blindly copy paste
> the code. The examples are meant to be a template or a guide. > 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 [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 [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/ [interface]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/interfaces/


+ 2
- 9
src/Discord.Net.Commands/CommandService.cs View File

@@ -39,15 +39,8 @@ namespace Discord.Commands
/// Occurs when a command is successfully executed without any error. /// Occurs when a command is successfully executed without any error.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// <para>
/// This event is fired when a command has been successfully executed without any of the following errors:
/// </para>
/// <para>* Parsing error</para>
/// <para>* Precondition error</para>
/// <para>* Runtime exception</para>
/// <para>
/// Should the command encounter any of the aforementioned error, this event will not be raised.
/// </para>
/// 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.
/// </remarks> /// </remarks>
public event Func<Optional<CommandInfo>, ICommandContext, IResult, Task> CommandExecuted { add { _commandExecutedEvent.Add(value); } remove { _commandExecutedEvent.Remove(value); } } public event Func<Optional<CommandInfo>, ICommandContext, IResult, Task> CommandExecuted { add { _commandExecutedEvent.Add(value); } remove { _commandExecutedEvent.Remove(value); } }
internal readonly AsyncEvent<Func<Optional<CommandInfo>, ICommandContext, IResult, Task>> _commandExecutedEvent = new AsyncEvent<Func<Optional<CommandInfo>, ICommandContext, IResult, Task>>(); internal readonly AsyncEvent<Func<Optional<CommandInfo>, ICommandContext, IResult, Task>> _commandExecutedEvent = new AsyncEvent<Func<Optional<CommandInfo>, ICommandContext, IResult, Task>>();


+ 8
- 36
src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs View File

@@ -97,26 +97,12 @@ namespace Discord
/// Adds or updates the permission overwrite for the given role. /// Adds or updates the permission overwrite for the given role.
/// </summary> /// </summary>
/// <example> /// <example>
/// The following example fetches a role via <see cref="IGuild.GetRole"/> and a channel via
/// <para>The following example fetches a role via <see cref="IGuild.GetRole"/> and a channel via
/// <see cref="IGuild.GetChannelAsync"/>. Next, it checks if an overwrite had already been set via /// <see cref="IGuild.GetChannelAsync"/>. Next, it checks if an overwrite had already been set via
/// <see cref="GetPermissionOverwrite(Discord.IRole)"/>; if not, it denies the role from sending any /// <see cref="GetPermissionOverwrite(Discord.IRole)"/>; if not, it denies the role from sending any
/// messages to the channel.
/// <code lang="cs">
/// // 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));
/// </code>
/// messages to the channel.</para>
/// <code language="cs" region="AddPermissionOverwriteAsyncRole"
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IGuildChannel.Examples.cs"/>
/// </example> /// </example>
/// <param name="role">The role to add the overwrite to.</param> /// <param name="role">The role to add the overwrite to.</param>
/// <param name="permissions">The overwrite to add to the role.</param> /// <param name="permissions">The overwrite to add to the role.</param>
@@ -130,26 +116,12 @@ namespace Discord
/// Adds or updates the permission overwrite for the given user. /// Adds or updates the permission overwrite for the given user.
/// </summary> /// </summary>
/// <example> /// <example>
/// The following example fetches a user via <see cref="IGuild.GetUserAsync"/> and a channel via
/// <para>The following example fetches a user via <see cref="IGuild.GetUserAsync"/> and a channel via
/// <see cref="IGuild.GetChannelAsync"/>. Next, it checks if an overwrite had already been set via /// <see cref="IGuild.GetChannelAsync"/>. Next, it checks if an overwrite had already been set via
/// <see cref="GetPermissionOverwrite(Discord.IUser)"/>; if not, it denies the user from sending any /// <see cref="GetPermissionOverwrite(Discord.IUser)"/>; if not, it denies the user from sending any
/// messages to the channel.
/// <code lang="cs">
/// // 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));
/// </code>
/// messages to the channel.</para>
/// <code language="cs" region="AddPermissionOverwriteAsyncUser"
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IGuildChannel.Examples.cs"/>
/// </example> /// </example>
/// <param name="user">The user to add the overwrite to.</param> /// <param name="user">The user to add the overwrite to.</param>
/// <param name="permissions">The overwrite to add to the user.</param> /// <param name="permissions">The overwrite to add to the user.</param>


+ 33
- 46
src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs View File

@@ -14,13 +14,10 @@ namespace Discord
/// Sends a message to this message channel. /// Sends a message to this message channel.
/// </summary> /// </summary>
/// <example> /// <example>
/// The following example sends a message with the current system time in RFC 1123 format to the channel and
/// deletes itself after 5 seconds.
/// <code language="cs">
/// var message = await channel.SendMessageAsync(DateTimeOffset.UtcNow.ToString("R"));
/// await Task.Delay(TimeSpan.FromSeconds(5))
/// .ContinueWith(x => message.DeleteAsync());
/// </code>
/// <para>The following example sends a message with the current system time in RFC 1123 format to the channel and
/// deletes itself after 5 seconds.</para>
/// <code language="cs" region="SendMessageAsync"
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
/// </example> /// </example>
/// <param name="text">The message to be sent.</param> /// <param name="text">The message to be sent.</param>
/// <param name="isTTS">Determines whether the message should be read aloud by Discord or not.</param> /// <param name="isTTS">Determines whether the message should be read aloud by Discord or not.</param>
@@ -35,18 +32,14 @@ namespace Discord
/// Sends a file to this message channel with an optional caption. /// Sends a file to this message channel with an optional caption.
/// </summary> /// </summary>
/// <example> /// <example>
/// The following example uploads a local file called <c>wumpus.txt</c> along with the text
/// <c>good discord boi</c> to the channel.
/// <code language="cs">
/// await channel.SendFileAsync("wumpus.txt", "good discord boi");
/// </code>
///
/// The following example uploads a local image called <c>b1nzy.jpg</c> embedded inside a rich embed to the
/// channel.
/// <code language="cs">
/// await channel.SendFileAsync("b1nzy.jpg",
/// embed: new EmbedBuilder {ImageUrl = "attachment://b1nzy.jpg"}.Build());
/// </code>
/// <para>The following example uploads a local file called <c>wumpus.txt</c> along with the text
/// <c>good discord boi</c> to the channel.</para>
/// <code language="cs" region="SendFileAsync.FilePath"
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
/// <para>The following example uploads a local image called <c>b1nzy.jpg</c> embedded inside a rich embed to the
/// channel.</para>
/// <code language="cs" region="SendFileAsync.FilePath.EmbeddedImage"
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
/// </example> /// </example>
/// <remarks> /// <remarks>
/// This method sends a file as if you are uploading an attachment directly from your Discord client. /// 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. /// Sends a file to this message channel with an optional caption.
/// </summary> /// </summary>
/// <example> /// <example>
/// The following example uploads a streamed image that will be called <c>b1nzy.jpg</c> embedded inside a
/// rich embed to the channel.
/// <code language="cs">
/// await channel.SendFileAsync(b1nzyStream, "b1nzy.jpg",
/// embed: new EmbedBuilder {ImageUrl = "attachment://b1nzy.jpg"}.Build());
/// </code>
/// <para>The following example uploads a streamed image that will be called <c>b1nzy.jpg</c> embedded inside a
/// rich embed to the channel.</para>
/// <code language="cs" region="SendFileAsync.FileStream.EmbeddedImage"
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
/// </example> /// </example>
/// <remarks> /// <remarks>
/// This method sends a file as if you are uploading an attachment directly from your Discord client. /// 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. /// of flattening.
/// </remarks> /// </remarks>
/// <example> /// <example>
/// The following example downloads 300 messages and gets messages that belong to the user
/// <c>53905483156684800</c>.
/// <code lang="cs">
/// var messages = await messageChannel.GetMessagesAsync(300).FlattenAsync();
/// var userMessages = messages.Where(x =&gt; x.Author.Id == 53905483156684800);
/// </code>
/// <para>The following example downloads 300 messages and gets messages that belong to the user
/// <c>53905483156684800</c>.</para>
/// <code language="cs" region="GetMessagesAsync.FromLimit.Standard"
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
/// </example> /// </example>
/// <param name="limit">The numbers of message to be gotten from.</param> /// <param name="limit">The numbers of message to be gotten from.</param>
/// <param name="mode">The <see cref="CacheMode" /> that determines whether the object should be fetched from /// <param name="mode">The <see cref="CacheMode" /> that determines whether the object should be fetched from
@@ -168,10 +157,13 @@ namespace Discord
/// of flattening. /// of flattening.
/// </remarks> /// </remarks>
/// <example> /// <example>
/// The following example gets 5 message prior to the message identifier <c>442012544660537354</c>.
/// <code lang="cs">
/// var messages = await channel.GetMessagesAsync(442012544660537354, Direction.Before, 5).FlattenAsync();
/// </code>
/// <para>The following example gets 5 message prior to the message identifier <c>442012544660537354</c>.</para>
/// <code language="cs" region="GetMessagesAsync.FromId.FromMessage"
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
/// <para>The following example attempts to retrieve <c>messageCount</c> number of messages from the
/// beginning of the channel and prints them to the console.</para>
/// <code language="cs" region="GetMessagesAsync.FromId.BeginningMessages"
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
/// </example> /// </example>
/// <param name="fromMessageId">The ID of the starting message to get the messages from.</param> /// <param name="fromMessageId">The ID of the starting message to get the messages from.</param>
/// <param name="dir">The direction of the messages to be gotten from.</param> /// <param name="dir">The direction of the messages to be gotten from.</param>
@@ -206,10 +198,9 @@ namespace Discord
/// of flattening. /// of flattening.
/// </remarks> /// </remarks>
/// <example> /// <example>
/// The following example gets 5 message prior to a specific message, <c>oldMessage</c>.
/// <code lang="cs">
/// var messages = await channel.GetMessagesAsync(oldMessage, Direction.Before, 5).FlattenAsync();
/// </code>
/// <para>The following example gets 5 message prior to a specific message, <c>oldMessage</c>.</para>
/// <code language="cs" region="GetMessagesAsync.FromMessage"
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
/// </example> /// </example>
/// <param name="fromMessage">The starting message to get the messages from.</param> /// <param name="fromMessage">The starting message to get the messages from.</param>
/// <param name="dir">The direction of the messages to be gotten from.</param> /// <param name="dir">The direction of the messages to be gotten from.</param>
@@ -262,13 +253,9 @@ namespace Discord
/// object is disposed. /// object is disposed.
/// </summary> /// </summary>
/// <example> /// <example>
/// The following example keeps the client in the typing state until <c>LongRunningAsync</c> has finished.
/// <code lang="cs">
/// using (messageChannel.EnterTypingState())
/// {
/// await LongRunningAsync();
/// }
/// </code>
/// <para>The following example keeps the client in the typing state until <c>LongRunningAsync</c> has finished.</para>
/// <code language="cs" region="EnterTypingState"
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
/// </example> /// </example>
/// <param name="options">The options to be used when sending the request.</param> /// <param name="options">The options to be used when sending the request.</param>
/// <returns> /// <returns>


+ 1
- 1
src/Discord.Net.Core/Entities/Channels/ITextChannel.cs View File

@@ -40,7 +40,7 @@ namespace Discord
/// </summary> /// </summary>
/// <example> /// <example>
/// The following example gets 250 messages from the channel and deletes them. /// The following example gets 250 messages from the channel and deletes them.
/// <code lang="cs">
/// <code language="cs">
/// var messages = await textChannel.GetMessagesAsync(250).FlattenAsync(); /// var messages = await textChannel.GetMessagesAsync(250).FlattenAsync();
/// await textChannel.DeleteMessagesAsync(messages); /// await textChannel.DeleteMessagesAsync(messages);
/// </code> /// </code>


+ 2
- 10
src/Discord.Net.Core/Entities/Guilds/IGuild.cs View File

@@ -440,16 +440,8 @@ namespace Discord
/// </summary> /// </summary>
/// <example> /// <example>
/// The following example creates a new text channel under an existing category named <c>Wumpus</c> with a set topic. /// The following example creates a new text channel under an existing category named <c>Wumpus</c> with a set topic.
/// <code lang="cs">
/// 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}.";
/// });
/// </code>
/// <code language="cs" region="CreateTextChannelAsync"
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Guilds\IGuild.Examples.cs"/>
/// </example> /// </example>
/// <param name="name">The new name for the text channel.</param> /// <param name="name">The new name for the text channel.</param>
/// <param name="func">The delegate containing the properties to be applied to the channel upon its creation.</param> /// <param name="func">The delegate containing the properties to be applied to the channel upon its creation.</param>


+ 9
- 20
src/Discord.Net.Core/Entities/Users/IUser.cs View File

@@ -23,10 +23,8 @@ namespace Discord
/// <example> /// <example>
/// The following example attempts to retrieve the user's current avatar and send it to a channel; if one is /// 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. /// not set, a default avatar for this user will be returned instead.
/// <code language="cs">
/// var userAvatarUrl = user.GetAvatarUrl() ?? user.GetDefaultAvatarUrl();
/// await textChannel.SendMessageAsync(userAvatarUrl);
/// </code>
/// <code language="cs" region="GetAvatarUrl"
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Users\IUser.Examples.cs"/>
/// </example> /// </example>
/// <param name="format">The format to return.</param> /// <param name="format">The format to return.</param>
/// <param name="size">The size of the image to return in. This can be any power of two between 16 and 2048. /// <param name="size">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
/// <remarks> /// <remarks>
/// This method is used to obtain or create a channel used to send a direct message. /// This method is used to obtain or create a channel used to send a direct message.
/// <note type="warning"> /// <note type="warning">
/// 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
/// <see cref="Discord.Net.HttpException"/> with a 403 as its
/// <see cref="Discord.Net.HttpException.HttpCode"/>. 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
/// <see cref="Discord.Net.HttpException"/> with a 403 as its
/// <see cref="Discord.Net.HttpException.HttpCode"/>. There are currently no official workarounds by
/// Discord.
/// </note> /// </note>
/// </remarks> /// </remarks>
/// <example> /// <example>
/// The following example attempts to send a direct message to the target user and logs the incident should /// The following example attempts to send a direct message to the target user and logs the incident should
/// it fail. /// it fail.
/// <code language="cs">
/// 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}");
/// }
/// </code>
/// <code region="GetOrCreateDMChannelAsync" language="cs"
/// source="../../../Discord.Net.Examples/Core/Entities/Users/IUser.Examples.cs"/>
/// </example> /// </example>
/// <param name="options">The options to be used when sending the request.</param> /// <param name="options">The options to be used when sending the request.</param>
/// <returns> /// <returns>


+ 4
- 4
src/Discord.Net.Core/IDiscordClient.cs View File

@@ -63,7 +63,7 @@ namespace Discord
/// Gets a generic channel. /// Gets a generic channel.
/// </summary> /// </summary>
/// <example> /// <example>
/// <code lang="cs" title="Example method">
/// <code language="cs" title="Example method">
/// var channel = await _client.GetChannelAsync(381889909113225237); /// var channel = await _client.GetChannelAsync(381889909113225237);
/// if (channel != null &amp;&amp; channel is IMessageChannel msgChannel) /// if (channel != null &amp;&amp; channel is IMessageChannel msgChannel)
/// { /// {
@@ -194,7 +194,7 @@ namespace Discord
/// Gets a user. /// Gets a user.
/// </summary> /// </summary>
/// <example> /// <example>
/// <code lang="cs" title="Example method">
/// <code language="cs" title="Example method">
/// var user = await _client.GetUserAsync(168693960628371456); /// var user = await _client.GetUserAsync(168693960628371456);
/// if (user != null) /// if (user != null)
/// Console.WriteLine($"{user} is created at {user.CreatedAt}."; /// Console.WriteLine($"{user} is created at {user.CreatedAt}.";
@@ -212,7 +212,7 @@ namespace Discord
/// Gets a user. /// Gets a user.
/// </summary> /// </summary>
/// <example> /// <example>
/// <code lang="cs" title="Example method">
/// <code language="cs" title="Example method">
/// var user = await _client.GetUserAsync("Still", "2876"); /// var user = await _client.GetUserAsync("Still", "2876");
/// if (user != null) /// if (user != null)
/// Console.WriteLine($"{user} is created at {user.CreatedAt}."; /// Console.WriteLine($"{user} is created at {user.CreatedAt}.";
@@ -232,7 +232,7 @@ namespace Discord
/// </summary> /// </summary>
/// <example> /// <example>
/// The following example gets the most optimal voice region from the collection. /// The following example gets the most optimal voice region from the collection.
/// <code lang="cs">
/// <code language="cs">
/// var regions = await client.GetVoiceRegionsAsync(); /// var regions = await client.GetVoiceRegionsAsync();
/// var optimalRegion = regions.FirstOrDefault(x =&gt; x.IsOptimal); /// var optimalRegion = regions.FirstOrDefault(x =&gt; x.IsOptimal);
/// </code> /// </code>


+ 46
- 0
src/Discord.Net.Examples/Core/Entities/Channels/IGuildChannel.Examples.cs View File

@@ -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
}
}

+ 114
- 0
src/Discord.Net.Examples/Core/Entities/Channels/IMessageChannel.Examples.cs View File

@@ -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

}
}
}

+ 25
- 0
src/Discord.Net.Examples/Core/Entities/Guilds/IGuild.Examples.cs View File

@@ -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
}
}

+ 38
- 0
src/Discord.Net.Examples/Core/Entities/Users/IUser.Examples.cs View File

@@ -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
}
}

+ 21
- 0
src/Discord.Net.Examples/Discord.Net.Examples.csproj View File

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

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<Compile Remove="Core\Entities\Guilds\IGuild.Examples.cs" />
</ItemGroup>

<ItemGroup>
<None Include="Core\Entities\Guilds\IGuild.Examples.cs" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" />
<ProjectReference Include="..\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" />
<PackageReference Include="JetBrains.Annotations" Version="2018.3.0" />
</ItemGroup>

</Project>

+ 117
- 0
src/Discord.Net.Examples/WebSocket/BaseSocketClient.Events.Examples.cs View File

@@ -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<IUserMessage, ulong> 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<IMessage, ulong> 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
}
}

+ 1
- 1
src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs View File

@@ -405,7 +405,7 @@ namespace Discord.Rest
/// </summary> /// </summary>
/// <example> /// <example>
/// The following example creates a new text channel under an existing category named <c>Wumpus</c> with a set topic. /// The following example creates a new text channel under an existing category named <c>Wumpus</c> with a set topic.
/// <code lang="cs">
/// <code language="cs">
/// var categories = await guild.GetCategoriesAsync(); /// var categories = await guild.GetCategoriesAsync();
/// var targetCategory = categories.FirstOrDefault(x => x.Name == "wumpus"); /// var targetCategory = categories.FirstOrDefault(x => x.Name == "wumpus");
/// if (targetCategory == null) return; /// if (targetCategory == null) return;


+ 50
- 0
src/Discord.Net.WebSocket/BaseSocketClient.Events.cs View File

@@ -18,6 +18,10 @@ namespace Discord.WebSocket
/// see the derived classes of <see cref="SocketChannel"/> for more details. /// see the derived classes of <see cref="SocketChannel"/> for more details.
/// </para> /// </para>
/// </remarks> /// </remarks>
/// <example>
/// <code language="cs" region="ChannelCreated"
/// source="..\Discord.Net.Examples\WebSocket\BaseSocketClient.Events.Examples.cs"/>
/// </example>
public event Func<SocketChannel, Task> ChannelCreated public event Func<SocketChannel, Task> ChannelCreated
{ {
add { _channelCreatedEvent.Add(value); } add { _channelCreatedEvent.Add(value); }
@@ -36,6 +40,10 @@ namespace Discord.WebSocket
/// see the derived classes of <see cref="SocketChannel"/> for more details. /// see the derived classes of <see cref="SocketChannel"/> for more details.
/// </para> /// </para>
/// </remarks> /// </remarks>
/// <example>
/// <code language="cs" region="ChannelDestroyed"
/// source="..\Discord.Net.Examples\WebSocket\BaseSocketClient.Events.Examples.cs"/>
/// </example>
public event Func<SocketChannel, Task> ChannelDestroyed { public event Func<SocketChannel, Task> ChannelDestroyed {
add { _channelDestroyedEvent.Add(value); } add { _channelDestroyedEvent.Add(value); }
remove { _channelDestroyedEvent.Remove(value); } remove { _channelDestroyedEvent.Remove(value); }
@@ -54,6 +62,10 @@ namespace Discord.WebSocket
/// <see cref="SocketChannel"/> for more details. /// <see cref="SocketChannel"/> for more details.
/// </para> /// </para>
/// </remarks> /// </remarks>
/// <example>
/// <code language="cs" region="ChannelUpdated"
/// source="..\Discord.Net.Examples\WebSocket\BaseSocketClient.Events.Examples.cs"/>
/// </example>
public event Func<SocketChannel, SocketChannel, Task> ChannelUpdated { public event Func<SocketChannel, SocketChannel, Task> ChannelUpdated {
add { _channelUpdatedEvent.Add(value); } add { _channelUpdatedEvent.Add(value); }
remove { _channelUpdatedEvent.Remove(value); } remove { _channelUpdatedEvent.Remove(value); }
@@ -74,6 +86,11 @@ namespace Discord.WebSocket
/// derived classes of <see cref="SocketMessage"/> for more details. /// derived classes of <see cref="SocketMessage"/> for more details.
/// </para> /// </para>
/// </remarks> /// </remarks>
/// <example>
/// <para>The example below checks if the newly received message contains the target user.</para>
/// <code language="cs" region="MessageReceived"
/// source="..\Discord.Net.Examples\WebSocket\BaseSocketClient.Events.Examples.cs"/>
/// </example>
public event Func<SocketMessage, Task> MessageReceived { public event Func<SocketMessage, Task> MessageReceived {
add { _messageReceivedEvent.Add(value); } add { _messageReceivedEvent.Add(value); }
remove { _messageReceivedEvent.Remove(value); } remove { _messageReceivedEvent.Remove(value); }
@@ -102,6 +119,10 @@ namespace Discord.WebSocket
/// <see cref="ISocketMessageChannel"/> parameter. /// <see cref="ISocketMessageChannel"/> parameter.
/// </para> /// </para>
/// </remarks> /// </remarks>
/// <example>
/// <code language="cs" region="MessageDeleted"
/// source="..\Discord.Net.Examples\WebSocket\BaseSocketClient.Events.Examples.cs" />
/// </example>
public event Func<Cacheable<IMessage, ulong>, ISocketMessageChannel, Task> MessageDeleted { public event Func<Cacheable<IMessage, ulong>, ISocketMessageChannel, Task> MessageDeleted {
add { _messageDeletedEvent.Add(value); } add { _messageDeletedEvent.Add(value); }
remove { _messageDeletedEvent.Remove(value); } remove { _messageDeletedEvent.Remove(value); }
@@ -134,6 +155,35 @@ namespace Discord.WebSocket
} }
internal readonly AsyncEvent<Func<Cacheable<IMessage, ulong>, SocketMessage, ISocketMessageChannel, Task>> _messageUpdatedEvent = new AsyncEvent<Func<Cacheable<IMessage, ulong>, SocketMessage, ISocketMessageChannel, Task>>(); internal readonly AsyncEvent<Func<Cacheable<IMessage, ulong>, SocketMessage, ISocketMessageChannel, Task>> _messageUpdatedEvent = new AsyncEvent<Func<Cacheable<IMessage, ulong>, SocketMessage, ISocketMessageChannel, Task>>();
/// <summary> Fired when a reaction is added to a message. </summary> /// <summary> Fired when a reaction is added to a message. </summary>
/// <remarks>
/// <para>
/// This event is fired when a reaction is added to a user message. The event handler must return a
/// <see cref="Task"/> and accept a <see cref="Cacheable{TEntity,TId}"/>, an
/// <see cref="ISocketMessageChannel"/>, and a <see cref="SocketReaction"/> as its parameter.
/// </para>
/// <para>
/// If caching is enabled via <see cref="DiscordSocketConfig"/>, the
/// <see cref="Cacheable{TEntity,TId}"/> 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
/// <see cref="ulong"/>.
/// </para>
/// <para>
/// The source channel of the reaction addition will be passed into the
/// <see cref="ISocketMessageChannel"/> parameter.
/// </para>
/// <para>
/// The reaction that was added will be passed into the <see cref="SocketReaction"/> parameter.
/// </para>
/// <note>
/// When fetching the reaction from this event, a user may not be provided under
/// <see cref="SocketReaction.User"/>. Please see the documentation of the property for more
/// information.
/// </note>
/// </remarks>
/// <example>
/// <code language="cs" region="ReactionAdded"
/// source="..\Discord.Net.Examples\WebSocket\BaseSocketClient.Events.Examples.cs"/>
/// </example>
public event Func<Cacheable<IUserMessage, ulong>, ISocketMessageChannel, SocketReaction, Task> ReactionAdded { public event Func<Cacheable<IUserMessage, ulong>, ISocketMessageChannel, SocketReaction, Task> ReactionAdded {
add { _reactionAddedEvent.Add(value); } add { _reactionAddedEvent.Add(value); }
remove { _reactionAddedEvent.Remove(value); } remove { _reactionAddedEvent.Remove(value); }


+ 14
- 10
src/Discord.Net.WebSocket/BaseSocketClient.cs View File

@@ -97,13 +97,15 @@ namespace Discord.WebSocket
/// <remarks> /// <remarks>
/// This method gets the user present in the WebSocket cache with the given condition. /// This method gets the user present in the WebSocket cache with the given condition.
/// <note type="warning"> /// <note type="warning">
/// Sometimes a user may return <c>null</c> due to Discord not sending offline users in large
/// guilds (i.e. guild with 100+ members) actively. To download users on startup, consider enabling
/// <see cref="DiscordSocketConfig.AlwaysDownloadUsers"/>.
/// Sometimes a user may return <c>null</c> 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 <see cref="Discord.WebSocket.DiscordSocketConfig.AlwaysDownloadUsers" />.
/// </note> /// </note>
/// <note> /// <note>
/// This method does not attempt to fetch users that the logged-in user does not have access to (i.e. /// 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
/// <see cref="DiscordRestClient.GetUserAsync(System.UInt64,Discord.RequestOptions)" />.
/// </note> /// </note>
/// </remarks> /// </remarks>
/// <returns> /// <returns>
@@ -114,20 +116,22 @@ namespace Discord.WebSocket
/// <summary> /// <summary>
/// Gets a user. /// Gets a user.
/// </summary> /// </summary>
/// <param name="username">The name of the user.</param>
/// <param name="discriminator">The discriminator value of the user.</param>
/// <remarks> /// <remarks>
/// This method gets the user present in the WebSocket cache with the given condition. /// This method gets the user present in the WebSocket cache with the given condition.
/// <note type="warning"> /// <note type="warning">
/// Sometimes a user may return <c>null</c> due to Discord not sending offline users in large
/// guilds (i.e. guild with 100+ members) actively. To download users on startup, consider enabling
/// <see cref="DiscordSocketConfig.AlwaysDownloadUsers"/>.
/// Sometimes a user may return <c>null</c> 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 <see cref="Discord.WebSocket.DiscordSocketConfig.AlwaysDownloadUsers" />.
/// </note> /// </note>
/// <note> /// <note>
/// This method does not attempt to fetch users that the logged-in user does not have access to (i.e. /// 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
/// <see cref="DiscordRestClient.GetUserAsync(System.UInt64,Discord.RequestOptions)" />.
/// </note> /// </note>
/// </remarks> /// </remarks>
/// <param name="username">The name of the user.</param>
/// <param name="discriminator">The discriminator value of the user.</param>
/// <returns> /// <returns>
/// A generic WebSocket-based user; <c>null</c> when the user cannot be found. /// A generic WebSocket-based user; <c>null</c> when the user cannot be found.
/// </returns> /// </returns>


+ 3
- 1
src/Discord.Net.WebSocket/DiscordSocketConfig.cs View File

@@ -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 /// 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 /// 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 /// 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 <see cref="DiscordRestClient.GetGuildUserAsync"/> as a backup.
/// overriding the TypeReader and use
/// <see cref="DiscordRestClient.GetUserAsync(System.UInt64,Discord.RequestOptions)"/>
/// or <see cref="DiscordSocketRestClient.GetGuildUserAsync(ulong, ulong, RequestOptions)"/> as a backup.
/// </note> /// </note>
/// </remarks> /// </remarks>
public bool AlwaysDownloadUsers { get; set; } = false; public bool AlwaysDownloadUsers { get; set; } = false;


+ 1
- 1
src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs View File

@@ -535,7 +535,7 @@ namespace Discord.WebSocket
/// </summary> /// </summary>
/// <example> /// <example>
/// The following example creates a new text channel under an existing category named <c>Wumpus</c> with a set topic. /// The following example creates a new text channel under an existing category named <c>Wumpus</c> with a set topic.
/// <code lang="cs">
/// <code language="cs">
/// var categories = await guild.GetCategoriesAsync(); /// var categories = await guild.GetCategoriesAsync();
/// var targetCategory = categories.FirstOrDefault(x => x.Name == "wumpus"); /// var targetCategory = categories.FirstOrDefault(x => x.Name == "wumpus");
/// if (targetCategory == null) return; /// if (targetCategory == null) return;


+ 17
- 0
src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs View File

@@ -10,6 +10,11 @@ namespace Discord.WebSocket
/// <summary> /// <summary>
/// Gets the ID of the user who added the reaction. /// Gets the ID of the user who added the reaction.
/// </summary> /// </summary>
/// <remarks>
/// This property retrieves the snowflake identifier of the user responsible for this reaction. This
/// property will always contain the user identifier in event that
/// <see cref="Discord.WebSocket.SocketReaction.User" /> cannot be retrieved.
/// </remarks>
/// <returns> /// <returns>
/// A user snowflake identifier associated with the user. /// A user snowflake identifier associated with the user.
/// </returns> /// </returns>
@@ -17,6 +22,18 @@ namespace Discord.WebSocket
/// <summary> /// <summary>
/// Gets the user who added the reaction if possible. /// Gets the user who added the reaction if possible.
/// </summary> /// </summary>
/// <remarks>
/// <para>
/// 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
/// <see cref="Discord.WebSocket.SocketReaction.UserId" />.
/// </para>
/// <para>
/// If you wish to obtain an identifiable user object, consider utilizing
/// <see cref="Discord.Rest.DiscordRestClient" /> which will attempt to retrieve the user from REST.
/// </para>
/// </remarks>
/// <returns> /// <returns>
/// A user object where possible; a value is not always returned. /// A user object where possible; a value is not always returned.
/// </returns> /// </returns>


Loading…
Cancel
Save