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>
This commit is contained in:
@@ -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}
|
||||||
|
|||||||
@@ -1,23 +1,46 @@
|
|||||||
# Contributing to Docs
|
# Contributing to Docs
|
||||||
|
|
||||||
|
First of all, thank you for your interest in contributing to our
|
||||||
|
documentation work. We really appreciate it! That being said,
|
||||||
|
there are several guidelines you should attempt to follow when adding
|
||||||
|
to/changing the documentation.
|
||||||
|
|
||||||
## General Guidelines
|
## General Guidelines
|
||||||
|
|
||||||
We do not have any strict conditions for writing documentation,
|
* Keep code samples in each section's `samples` folder
|
||||||
but keep these guidelines in mind:
|
|
||||||
|
|
||||||
* Keep code samples in the `guides/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
|
languages, please open an issue or contact `foxbot#0282` on
|
||||||
Discord (`foxbot#0282`).
|
Discord.
|
||||||
|
|
||||||
## Style Consistencies
|
## XML Docstrings Guidelines
|
||||||
|
|
||||||
* Use a ruler set at 70 characters
|
* When using the `<summary>` tag, use concise verbs. For example:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
/// <summary> Gets or sets the guild user in this object. </summary>
|
||||||
|
public IGuildUser GuildUser { get; set; }
|
||||||
|
```
|
||||||
|
|
||||||
|
* The `<summary>` tag should not be more than 3 lines long. Consider
|
||||||
|
simplifying the terms or using the `<remarks>` tag instead.
|
||||||
|
* When using the `<code>` tag, put the code sample within the
|
||||||
|
`src/Discord.Net.Examples` project under the corresponding path of
|
||||||
|
the object and surround the code with a `#region` tag.
|
||||||
|
* If the remarks you are looking to write are too long, consider
|
||||||
|
writing a shorter version in the XML docs while keeping the longer
|
||||||
|
version in the `overwrites` folder using the DocFX overwrites syntax.
|
||||||
|
* You may find an example of this in the samples provided within
|
||||||
|
the folder.
|
||||||
|
|
||||||
|
## Docs Guide Guidelines
|
||||||
|
|
||||||
|
* Use a ruler set at 70 characters (use the docs workspace provided
|
||||||
|
if you are using Visual Studio Code)
|
||||||
* Links should use long syntax
|
* 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
|
* [Microsoft Docs](https://docs.microsoft.com)
|
||||||
* http://flask.pocoo.org/docs/0.12/
|
* [Flask Docs](https://flask.pocoo.org/docs/1.0/)
|
||||||
|
* [DocFX Manual](https://dotnet.github.io/docfx/)
|
||||||
|
* [Sandcastle XML Guide](http://ewsoftware.github.io/XMLCommentsGuide)
|
||||||
@@ -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.
|
||||||
Binary file not shown.
Binary file not shown.
@@ -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="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-8e0b172" target="lib/osx/libgit2-8e0b172.dylib" />
|
<dllmap os="osx" cpu="x86,x86-64" dll="git2-a904fc6" target="lib/osx/libgit2-a904fc6.dylib" />
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
Binary file not shown.
BIN
docs/_template/last-modified/plugins/lib/alpine-x64/libgit2-a904fc6.so
vendored
Normal file
BIN
docs/_template/last-modified/plugins/lib/alpine-x64/libgit2-a904fc6.so
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
docs/_template/last-modified/plugins/lib/debian.9-x64/libgit2-a904fc6.so
vendored
Normal file
BIN
docs/_template/last-modified/plugins/lib/debian.9-x64/libgit2-a904fc6.so
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
docs/_template/last-modified/plugins/lib/fedora-x64/libgit2-a904fc6.so
vendored
Normal file
BIN
docs/_template/last-modified/plugins/lib/fedora-x64/libgit2-a904fc6.so
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
docs/_template/last-modified/plugins/lib/linux-x64/libgit2-a904fc6.so
vendored
Normal file
BIN
docs/_template/last-modified/plugins/lib/linux-x64/libgit2-a904fc6.so
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
docs/_template/last-modified/plugins/lib/osx/libgit2-a904fc6.dylib
vendored
Normal file
BIN
docs/_template/last-modified/plugins/lib/osx/libgit2-a904fc6.dylib
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
docs/_template/last-modified/plugins/lib/rhel-x64/libgit2-a904fc6.so
vendored
Normal file
BIN
docs/_template/last-modified/plugins/lib/rhel-x64/libgit2-a904fc6.so
vendored
Normal file
Binary file not shown.
BIN
docs/_template/last-modified/plugins/lib/ubuntu.18.04-x64/libgit2-a904fc6.so
vendored
Normal file
BIN
docs/_template/last-modified/plugins/lib/ubuntu.18.04-x64/libgit2-a904fc6.so
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
docs/_template/last-modified/plugins/lib/win32/x64/git2-a904fc6.dll
vendored
Normal file
BIN
docs/_template/last-modified/plugins/lib/win32/x64/git2-a904fc6.dll
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
docs/_template/last-modified/plugins/lib/win32/x86/git2-a904fc6.dll
vendored
Normal file
BIN
docs/_template/last-modified/plugins/lib/win32/x86/git2-a904fc6.dll
vendored
Normal file
Binary file not shown.
21
docs/_template/light-dark-theme/docfx-material-license.md
vendored
Normal file
21
docs/_template/light-dark-theme/docfx-material-license.md
vendored
Normal 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.
|
||||||
@@ -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
docs/_template/light-dark-theme/styles/dark.css
vendored
14
docs/_template/light-dark-theme/styles/dark.css
vendored
@@ -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 {
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
15
docs/_template/light-dark-theme/styles/gray.css
vendored
15
docs/_template/light-dark-theme/styles/gray.css
vendored
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
docs/_template/light-dark-theme/styles/material.css
vendored
Normal file
199
docs/_template/light-dark-theme/styles/material.css
vendored
Normal 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);
|
||||||
|
}
|
||||||
@@ -5,3 +5,6 @@ apiRules:
|
|||||||
- exclude:
|
- exclude:
|
||||||
uidRegex: ^Discord\.Analyzers$
|
uidRegex: ^Discord\.Analyzers$
|
||||||
type: Namespace
|
type: Namespace
|
||||||
|
- exclude:
|
||||||
|
uidRegex: ^Discord\.API$
|
||||||
|
type: Namespace
|
||||||
@@ -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();
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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.
|
||||||
|
await _commands.ExecuteAsync(
|
||||||
// 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(
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// Note: This example is obsolete, a boolean type reader is bundled
|
// Please note that the library already supports type reading
|
||||||
// with Discord.Commands
|
// 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;
|
||||||
|
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +1,24 @@
|
|||||||
using Discord;
|
using Discord;
|
||||||
using Discord.WebSocket;
|
using Discord.WebSocket;
|
||||||
|
|
||||||
public class Program
|
public class LoggingService
|
||||||
{
|
{
|
||||||
private DiscordSocketClient _client;
|
public LoggingService(DiscordSocketClient client, CommandService command)
|
||||||
static void Main(string[] args) => new Program().MainAsync().GetAwaiter().GetResult();
|
|
||||||
|
|
||||||
public async Task MainAsync()
|
|
||||||
{
|
{
|
||||||
_client = new DiscordSocketClient(new DiscordSocketConfig
|
client.Log += LogAsync;
|
||||||
{
|
command.Log += LogAsync;
|
||||||
LogLevel = LogSeverity.Info
|
|
||||||
});
|
|
||||||
|
|
||||||
_client.Log += Log;
|
|
||||||
|
|
||||||
await _client.LoginAsync(TokenType.Bot, Environment.GetEnvironmentVariable("DiscordToken"));
|
|
||||||
await _client.StartAsync();
|
|
||||||
|
|
||||||
await Task.Delay(-1);
|
|
||||||
}
|
}
|
||||||
|
private Task LogAsync(LogMessage message)
|
||||||
private Task Log(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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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.
|
if you are unsure of the differences. To establish a new connection,
|
||||||
|
we will create an instance of [DiscordSocketClient] in the new async
|
||||||
To establish a new connection, we will create an instance of
|
main. You may pass in an optional @Discord.WebSocket.DiscordSocketConfig
|
||||||
[DiscordSocketClient] in the new async main. You may pass in an
|
if necessary. For most users, the default will work fine.
|
||||||
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."
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
> [!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."
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
> [!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
|
> **do not** share this token with anyone else! You should store this
|
||||||
> to store this token in an external source if you plan on distributing
|
> 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
|
To create commands for your bot, you may choose from a variety of
|
||||||
that is ready for [CommandService], a service that is ready for
|
command processors available. Throughout the guides, we will be using
|
||||||
advanced command usage.
|
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.
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
bot's project
|
||||||
3. Right click on "Dependencies", and select "Manage NuGet packages."
|
3. Right click on "Dependencies", and select "Manage NuGet packages"
|
||||||
|
|
||||||

|

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

|

|
||||||
|
|
||||||
### [Using JetBrains Rider](#tab/rider-install)
|
### [Using JetBrains Rider](#tab/rider-install)
|
||||||
|
|
||||||
1. Create a new solution for your bot.
|
1. Create a new solution for your bot
|
||||||
2. Open the NuGet window (Tools > NuGet > Manage NuGet packages for
|
2. Open the NuGet window (Tools > NuGet > Manage NuGet packages for Solution)
|
||||||
Solution).
|
|
||||||

|

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

|

|
||||||
4. Install by adding the package to your project.
|
|
||||||
|
4. Install by adding the package to your project
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### [Using Visual Studio Code](#tab/vs-code)
|
### [Using Visual Studio Code](#tab/vs-code)
|
||||||
|
|
||||||
1. Create a new project for your bot.
|
1. Create a new project for your bot
|
||||||
2. Add `Discord.Net` to your .csproj.
|
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.
|
1. Launch your terminal
|
||||||
2. Enter `dotnet add package Discord.Net`.
|
2. Navigate to where your `*.csproj` is located
|
||||||
|
3. Enter `dotnet add package Discord.Net`
|
||||||
|
|
||||||
***
|
***
|
||||||
|
|
||||||
|
|||||||
@@ -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`
|
||||||
|
|
||||||

|

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

|

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

|

|
||||||
|
|
||||||
> [!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!
|
||||||
> 
|
> 
|
||||||
|
|
||||||
### [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
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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:
|
An official collection of samples can be found
|
||||||
|
in [our GitHub repository].
|
||||||
1. [Official samples]
|
|
||||||
2. [Official template]
|
|
||||||
|
|
||||||
> [!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
|
[our GitHub repository]: https://github.com/RogueException/Discord.Net/tree/dev/samples
|
||||||
[Official samples]: 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/
|
||||||
|
|||||||
@@ -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 executed, successfully or not. When a command fails to
|
||||||
/// This event is fired when a command has been successfully executed without any of the following errors:
|
/// execute during parsing or precondition stage, the CommandInfo may not be returned.
|
||||||
/// </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>
|
|
||||||
/// </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>>();
|
||||||
|
|||||||
@@ -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.
|
/// messages to the channel.</para>
|
||||||
/// <code lang="cs">
|
/// <code language="cs" region="AddPermissionOverwriteAsyncRole"
|
||||||
/// // Fetches the role and channels
|
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IGuildChannel.Examples.cs"/>
|
||||||
/// 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>
|
|
||||||
/// </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.
|
/// messages to the channel.</para>
|
||||||
/// <code lang="cs">
|
/// <code language="cs" region="AddPermissionOverwriteAsyncUser"
|
||||||
/// // Fetches the role and channels
|
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IGuildChannel.Examples.cs"/>
|
||||||
/// 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>
|
|
||||||
/// </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>
|
||||||
|
|||||||
@@ -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
|
/// <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.
|
/// deletes itself after 5 seconds.</para>
|
||||||
/// <code language="cs">
|
/// <code language="cs" region="SendMessageAsync"
|
||||||
/// var message = await channel.SendMessageAsync(DateTimeOffset.UtcNow.ToString("R"));
|
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
|
||||||
/// await Task.Delay(TimeSpan.FromSeconds(5))
|
|
||||||
/// .ContinueWith(x => message.DeleteAsync());
|
|
||||||
/// </code>
|
|
||||||
/// </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
|
/// <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.
|
/// <c>good discord boi</c> to the channel.</para>
|
||||||
/// <code language="cs">
|
/// <code language="cs" region="SendFileAsync.FilePath"
|
||||||
/// await channel.SendFileAsync("wumpus.txt", "good discord boi");
|
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
|
||||||
/// </code>
|
/// <para>The following example uploads a local image called <c>b1nzy.jpg</c> embedded inside a rich embed to the
|
||||||
///
|
/// channel.</para>
|
||||||
/// The following example uploads a local image called <c>b1nzy.jpg</c> embedded inside a rich embed to the
|
/// <code language="cs" region="SendFileAsync.FilePath.EmbeddedImage"
|
||||||
/// channel.
|
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
|
||||||
/// <code language="cs">
|
|
||||||
/// await channel.SendFileAsync("b1nzy.jpg",
|
|
||||||
/// embed: new EmbedBuilder {ImageUrl = "attachment://b1nzy.jpg"}.Build());
|
|
||||||
/// </code>
|
|
||||||
/// </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
|
/// <para>The following example uploads a streamed image that will be called <c>b1nzy.jpg</c> embedded inside a
|
||||||
/// rich embed to the channel.
|
/// rich embed to the channel.</para>
|
||||||
/// <code language="cs">
|
/// <code language="cs" region="SendFileAsync.FileStream.EmbeddedImage"
|
||||||
/// await channel.SendFileAsync(b1nzyStream, "b1nzy.jpg",
|
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
|
||||||
/// embed: new EmbedBuilder {ImageUrl = "attachment://b1nzy.jpg"}.Build());
|
|
||||||
/// </code>
|
|
||||||
/// </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
|
/// <para>The following example downloads 300 messages and gets messages that belong to the user
|
||||||
/// <c>53905483156684800</c>.
|
/// <c>53905483156684800</c>.</para>
|
||||||
/// <code lang="cs">
|
/// <code language="cs" region="GetMessagesAsync.FromLimit.Standard"
|
||||||
/// var messages = await messageChannel.GetMessagesAsync(300).FlattenAsync();
|
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
|
||||||
/// var userMessages = messages.Where(x => x.Author.Id == 53905483156684800);
|
|
||||||
/// </code>
|
|
||||||
/// </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>.
|
/// <para>The following example gets 5 message prior to the message identifier <c>442012544660537354</c>.</para>
|
||||||
/// <code lang="cs">
|
/// <code language="cs" region="GetMessagesAsync.FromId.FromMessage"
|
||||||
/// var messages = await channel.GetMessagesAsync(442012544660537354, Direction.Before, 5).FlattenAsync();
|
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
|
||||||
/// </code>
|
/// <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>.
|
/// <para>The following example gets 5 message prior to a specific message, <c>oldMessage</c>.</para>
|
||||||
/// <code lang="cs">
|
/// <code language="cs" region="GetMessagesAsync.FromMessage"
|
||||||
/// var messages = await channel.GetMessagesAsync(oldMessage, Direction.Before, 5).FlattenAsync();
|
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
|
||||||
/// </code>
|
|
||||||
/// </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.
|
/// <para>The following example keeps the client in the typing state until <c>LongRunningAsync</c> has finished.</para>
|
||||||
/// <code lang="cs">
|
/// <code language="cs" region="EnterTypingState"
|
||||||
/// using (messageChannel.EnterTypingState())
|
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
|
||||||
/// {
|
|
||||||
/// await LongRunningAsync();
|
|
||||||
/// }
|
|
||||||
/// </code>
|
|
||||||
/// </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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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">
|
/// <code language="cs" region="CreateTextChannelAsync"
|
||||||
/// var categories = await guild.GetCategoriesAsync();
|
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Guilds\IGuild.Examples.cs"/>
|
||||||
/// 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>
|
|
||||||
/// </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>
|
||||||
|
|||||||
@@ -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">
|
/// <code language="cs" region="GetAvatarUrl"
|
||||||
/// var userAvatarUrl = user.GetAvatarUrl() ?? user.GetDefaultAvatarUrl();
|
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Users\IUser.Examples.cs"/>
|
||||||
/// await textChannel.SendMessageAsync(userAvatarUrl);
|
|
||||||
/// </code>
|
|
||||||
/// </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,8 +82,8 @@ 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
|
/// In event that the current user cannot send a message to the target user, a channel can and will
|
||||||
/// created by Discord. However, attempting to send a message will yield a
|
/// 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"/> with a 403 as its
|
||||||
/// <see cref="Discord.Net.HttpException.HttpCode"/>. There are currently no official workarounds by
|
/// <see cref="Discord.Net.HttpException.HttpCode"/>. There are currently no official workarounds by
|
||||||
/// Discord.
|
/// Discord.
|
||||||
@@ -94,17 +92,8 @@ namespace Discord
|
|||||||
/// <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">
|
/// <code region="GetOrCreateDMChannelAsync" language="cs"
|
||||||
/// var channel = await user.GetOrCreateDMChannelAsync();
|
/// source="../../../Discord.Net.Examples/Core/Entities/Users/IUser.Examples.cs"/>
|
||||||
/// try
|
|
||||||
/// {
|
|
||||||
/// await channel.SendMessageAsync("Awesome stuff!");
|
|
||||||
/// }
|
|
||||||
/// catch (Discord.Net.HttpException ex) when (ex.HttpCode == 403)
|
|
||||||
/// {
|
|
||||||
/// Console.WriteLine($"Boo, I cannot message {user}");
|
|
||||||
/// }
|
|
||||||
/// </code>
|
|
||||||
/// </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>
|
||||||
|
|||||||
@@ -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 && channel is IMessageChannel msgChannel)
|
/// if (channel != null && 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 => x.IsOptimal);
|
/// var optimalRegion = regions.FirstOrDefault(x => x.IsOptimal);
|
||||||
/// </code>
|
/// </code>
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
src/Discord.Net.Examples/Discord.Net.Examples.csproj
Normal file
21
src/Discord.Net.Examples/Discord.Net.Examples.csproj
Normal 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>
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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); }
|
||||||
|
|||||||
@@ -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
|
/// Sometimes a user may return <c>null</c> due to Discord not sending offline users in large guilds
|
||||||
/// guilds (i.e. guild with 100+ members) actively. To download users on startup, consider enabling
|
/// (i.e. guild with 100+ members) actively. To download users on startup and to see more information
|
||||||
/// <see cref="DiscordSocketConfig.AlwaysDownloadUsers"/>.
|
/// 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
|
/// Sometimes a user may return <c>null</c> due to Discord not sending offline users in large guilds
|
||||||
/// guilds (i.e. guild with 100+ members) actively. To download users on startup, consider enabling
|
/// (i.e. guild with 100+ members) actively. To download users on startup and to see more information
|
||||||
/// <see cref="DiscordSocketConfig.AlwaysDownloadUsers"/>.
|
/// 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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user