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
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02_commands_framework", "samples\02_commands_framework\02_commands_framework.csproj", "{4E1F1F40-B1DD-40C9-A4B1-A2046A4C9C76}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "03_sharded_client", "samples\03_sharded_client\03_sharded_client.csproj", "{9B4C4AFB-3D15-44C6-9E36-12ED625AAA26}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "03_sharded_client", "samples\03_sharded_client\03_sharded_client.csproj", "{9B4C4AFB-3D15-44C6-9E36-12ED625AAA26}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{D1F0271E-0EE2-4B66-AC3D-9871B7E1C4CF}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Examples", "src\Discord.Net.Examples\Discord.Net.Examples.csproj", "{7EA96B2B-4D71-458D-9423-839362DC38BE}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -174,6 +178,18 @@ Global
|
||||
{9B4C4AFB-3D15-44C6-9E36-12ED625AAA26}.Release|x64.Build.0 = Release|Any CPU
|
||||
{9B4C4AFB-3D15-44C6-9E36-12ED625AAA26}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{9B4C4AFB-3D15-44C6-9E36-12ED625AAA26}.Release|x86.Build.0 = Release|Any CPU
|
||||
{7EA96B2B-4D71-458D-9423-839362DC38BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7EA96B2B-4D71-458D-9423-839362DC38BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7EA96B2B-4D71-458D-9423-839362DC38BE}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{7EA96B2B-4D71-458D-9423-839362DC38BE}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{7EA96B2B-4D71-458D-9423-839362DC38BE}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{7EA96B2B-4D71-458D-9423-839362DC38BE}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{7EA96B2B-4D71-458D-9423-839362DC38BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7EA96B2B-4D71-458D-9423-839362DC38BE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7EA96B2B-4D71-458D-9423-839362DC38BE}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{7EA96B2B-4D71-458D-9423-839362DC38BE}.Release|x64.Build.0 = Release|Any CPU
|
||||
{7EA96B2B-4D71-458D-9423-839362DC38BE}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{7EA96B2B-4D71-458D-9423-839362DC38BE}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -188,6 +204,7 @@ Global
|
||||
{F2FF84FB-F6AD-47E5-9EE5-18206CAE136E} = {BB59D5B5-E7B0-4BF4-8F82-D14431B2799B}
|
||||
{4E1F1F40-B1DD-40C9-A4B1-A2046A4C9C76} = {BB59D5B5-E7B0-4BF4-8F82-D14431B2799B}
|
||||
{9B4C4AFB-3D15-44C6-9E36-12ED625AAA26} = {BB59D5B5-E7B0-4BF4-8F82-D14431B2799B}
|
||||
{7EA96B2B-4D71-458D-9423-839362DC38BE} = {D1F0271E-0EE2-4B66-AC3D-9871B7E1C4CF}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {D2404771-EEC8-45F2-9D71-F3373F6C1495}
|
||||
|
||||
@@ -1,23 +1,46 @@
|
||||
# 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
|
||||
|
||||
We do not have any strict conditions for writing documentation,
|
||||
but keep these guidelines in mind:
|
||||
|
||||
* Keep code samples in the `guides/samples` folder
|
||||
* Keep code samples in each section's `samples` folder
|
||||
* When referencing an object in the API, link to its page in the
|
||||
API documentation
|
||||
* Documentation should be written in an FAQ/Wiki-style format
|
||||
* Documentation should be written in clear and proper English*
|
||||
|
||||
\* If anyone is interested in translating documentation into other
|
||||
languages, please open an issue or contact me on
|
||||
Discord (`foxbot#0282`).
|
||||
languages, please open an issue or contact `foxbot#0282` on
|
||||
Discord.
|
||||
|
||||
## 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
|
||||
* Pages should be short and concise, not broad and long
|
||||
|
||||
@@ -31,5 +54,7 @@ Please consult the [API Documentation] for more information.
|
||||
|
||||
## Recommended Reads
|
||||
|
||||
* http://docs.microsoft.com
|
||||
* http://flask.pocoo.org/docs/0.12/
|
||||
* [Microsoft Docs](https://docs.microsoft.com)
|
||||
* [Flask Docs](https://flask.pocoo.org/docs/1.0/)
|
||||
* [DocFX Manual](https://dotnet.github.io/docfx/)
|
||||
* [Sandcastle XML Guide](http://ewsoftware.github.io/XMLCommentsGuide)
|
||||
@@ -1,6 +1,6 @@
|
||||
A "precondidtion" in the command system is used to determine if a
|
||||
A "precondition" in the command system is used to determine if a
|
||||
condition is met before entering the command task. Using a
|
||||
precondidtion may aid in keeping a well-organized command logic.
|
||||
precondition may aid in keeping a well-organized command logic.
|
||||
|
||||
The most common use case being whether a user has sufficient
|
||||
permission to execute the command.
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
<configuration>
|
||||
<dllmap os="linux" cpu="x86-64" wordsize="64" dll="git2-8e0b172" target="lib/linux-x64/libgit2-8e0b172.so" />
|
||||
<dllmap os="osx" cpu="x86,x86-64" dll="git2-8e0b172" target="lib/osx/libgit2-8e0b172.dylib" />
|
||||
<dllmap os="linux" cpu="x86-64" wordsize="64" dll="git2-a904fc6" target="lib/linux-x64/libgit2-a904fc6.so" />
|
||||
<dllmap os="osx" cpu="x86,x86-64" dll="git2-a904fc6" target="lib/osx/libgit2-a904fc6.dylib" />
|
||||
</configuration>
|
||||
|
||||
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/master.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 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"/>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
color: #E0E0E0;
|
||||
}
|
||||
|
||||
button,
|
||||
a {
|
||||
color: #64B5F6;
|
||||
@@ -258,6 +267,11 @@ tbody>tr {
|
||||
border-top: 2px solid rgb(173, 173, 173)
|
||||
}
|
||||
|
||||
/* top navbar */
|
||||
.navbar-inverse[role="navigation"] {
|
||||
background-color: #2C2F33;
|
||||
}
|
||||
|
||||
/* select */
|
||||
|
||||
select {
|
||||
|
||||
@@ -361,7 +361,6 @@ pre {
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
color: #333;
|
||||
border: 1px solid #ccc;
|
||||
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;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
color: #EEEEEE;
|
||||
}
|
||||
|
||||
button,
|
||||
a {
|
||||
color: #64B5F6;
|
||||
@@ -39,13 +48,13 @@ hr {
|
||||
}
|
||||
|
||||
/* top navbar */
|
||||
.navbar-inverse[role="navigation"] {
|
||||
/*.navbar-inverse[role="navigation"] {
|
||||
background-color: #2C2F33;
|
||||
}
|
||||
}*/
|
||||
|
||||
/* sub navbar (below top) */
|
||||
.subnav {
|
||||
background: #282B2F
|
||||
background: rgb(69, 75, 82)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,14 +1,37 @@
|
||||
@import url('https://fonts.googleapis.com/css?family=Titillium+Web|Noto+Sans');
|
||||
@import url('https://fonts.googleapis.com/css?family=Roboto|Muli|Fira+Mono');
|
||||
|
||||
html,
|
||||
body {
|
||||
font-family: 'Titillium Web', 'Segoe UI', Tahoma, Helvetica, sans-serif;
|
||||
font-family: 'Roboto', 'Segoe UI', Tahoma, Helvetica, sans-serif;
|
||||
font-display: optional;
|
||||
height: 100%;
|
||||
font-size: 15px;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
code{
|
||||
font-family: 'Fira Mono', 'Courier New', Courier, monospace
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: 'Muli', Verdana, Geneva, Tahoma, sans-serif;
|
||||
line-height: 130%;
|
||||
}
|
||||
|
||||
h1,
|
||||
.h1,
|
||||
h2,
|
||||
.h2,
|
||||
h3,
|
||||
.h3 {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#logo
|
||||
{
|
||||
max-width: 100px;
|
||||
@@ -25,6 +48,14 @@ li,
|
||||
line-height: 160%;
|
||||
}
|
||||
|
||||
.toc-filter{
|
||||
background: inherit !important;
|
||||
}
|
||||
|
||||
.affix ul>li.active>ul, .affix ul>li.active>a:before, .affix ul>li>a:hover:before{
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
img {
|
||||
box-shadow: 0px 0px 3px 0px rgb(66, 66, 66);
|
||||
max-width: 95% !important;
|
||||
@@ -57,16 +88,6 @@ article.content h6{
|
||||
transition: all .25s ease-in-out;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: 'Noto Sans', Verdana, Geneva, Tahoma, sans-serif;
|
||||
line-height: 130%;
|
||||
}
|
||||
|
||||
.sideaffix {
|
||||
line-height: 140%;
|
||||
}
|
||||
@@ -173,3 +194,38 @@ span.arrow-d{
|
||||
span.arrow-r{
|
||||
top: 6px; position: relative;
|
||||
}
|
||||
|
||||
/* widen viewport */
|
||||
|
||||
@media (min-width: 1085px) {
|
||||
.container {
|
||||
width: calc(100% - 15vw);
|
||||
max-width: calc(100% - 15vw);
|
||||
}
|
||||
}
|
||||
|
||||
/* fix level indentation */
|
||||
|
||||
.level2 {
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.level3 {
|
||||
padding: 0 5px;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.level4 {
|
||||
padding: 0 5px;
|
||||
font-size: 85%;
|
||||
}
|
||||
|
||||
.level5 {
|
||||
padding: 0 5px;
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
.level6 {
|
||||
padding: 0 5px;
|
||||
font-size: 75%;
|
||||
}
|
||||
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);
|
||||
}
|
||||
@@ -4,4 +4,7 @@ apiRules:
|
||||
type: Namespace
|
||||
- exclude:
|
||||
uidRegex: ^Discord\.Analyzers$
|
||||
type: Namespace
|
||||
- exclude:
|
||||
uidRegex: ^Discord\.API$
|
||||
type: Namespace
|
||||
@@ -3,6 +3,9 @@ public class Initialize
|
||||
private readonly CommandService _commands;
|
||||
private readonly DiscordSocketClient _client;
|
||||
|
||||
// Ask if there are existing CommandService and DiscordSocketClient
|
||||
// instance. If there are, we retrieve them and add them to the
|
||||
// DI container; if not, we create our own.
|
||||
public Initialize(CommandService commands = null, DiscordSocketClient client = null)
|
||||
{
|
||||
_commands = commands ?? new CommandService();
|
||||
|
||||
@@ -32,6 +32,6 @@ public class DatabaseModule : ModuleBase<SocketCommandContext>
|
||||
[Command("read")]
|
||||
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 CommandService _commands;
|
||||
|
||||
// Retrieve client and CommandService instance via ctor
|
||||
public CommandHandler(DiscordSocketClient client, CommandService commands)
|
||||
{
|
||||
_commands = commands;
|
||||
@@ -46,19 +47,9 @@ public class CommandHandler
|
||||
|
||||
// Execute the command with the command context we just
|
||||
// created, along with the service provider for precondition checks.
|
||||
|
||||
// Keep in mind that result does not indicate a return value
|
||||
// rather an object stating if the command executed successfully.
|
||||
var result = await _commands.ExecuteAsync(
|
||||
await _commands.ExecuteAsync(
|
||||
context: context,
|
||||
argPos: argPos,
|
||||
services: null);
|
||||
|
||||
// Optionally, we may inform the user if the command fails
|
||||
// to be executed; however, this may not always be desired,
|
||||
// as it may clog up the request queue should a user spam a
|
||||
// command.
|
||||
// if (!result.IsSuccess)
|
||||
// await context.Channel.SendMessageAsync(result.ErrorReason);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
public async Task LogAsync(LogMessage logMessage)
|
||||
{
|
||||
// This casting type requries C#7
|
||||
if (logMessage.Exception is CommandException cmdException)
|
||||
{
|
||||
// We can tell the user that something unexpected has happened
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Note: This example is obsolete, a boolean type reader is bundled
|
||||
// with Discord.Commands
|
||||
// Please note that the library already supports type reading
|
||||
// primitive types such as bool. This example is merely used
|
||||
// to demonstrate how one could write a simple TypeReader.
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
|
||||
|
||||
@@ -35,10 +35,6 @@ sync and has a completed guild cache.
|
||||
|
||||
[DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient
|
||||
|
||||
### Samples
|
||||
|
||||
[!code-csharp[Connection Sample](samples/events.cs)]
|
||||
|
||||
## Reconnection
|
||||
|
||||
> [!TIP]
|
||||
|
||||
@@ -7,7 +7,7 @@ title: Entities
|
||||
|
||||
> [!NOTE]
|
||||
> This article is written with the Socket variants of entities in mind,
|
||||
> not the general interfaces or Rest/Rpc entities.
|
||||
> not the general interfaces or Rest entities.
|
||||
|
||||
Discord.Net provides a versatile entity system for navigating the
|
||||
Discord API.
|
||||
|
||||
@@ -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.WebSocket;
|
||||
|
||||
public class Program
|
||||
public class LoggingService
|
||||
{
|
||||
private DiscordSocketClient _client;
|
||||
static void Main(string[] args) => new Program().MainAsync().GetAwaiter().GetResult();
|
||||
|
||||
public async Task MainAsync()
|
||||
public LoggingService(DiscordSocketClient client, CommandService command)
|
||||
{
|
||||
_client = new DiscordSocketClient(new DiscordSocketConfig
|
||||
{
|
||||
LogLevel = LogSeverity.Info
|
||||
});
|
||||
|
||||
_client.Log += Log;
|
||||
|
||||
await _client.LoginAsync(TokenType.Bot, Environment.GetEnvironmentVariable("DiscordToken"));
|
||||
await _client.StartAsync();
|
||||
|
||||
await Task.Delay(-1);
|
||||
client.Log += LogAsync;
|
||||
command.Log += LogAsync;
|
||||
}
|
||||
|
||||
private Task Log(LogMessage message)
|
||||
private Task LogAsync(LogMessage message)
|
||||
{
|
||||
Console.WriteLine(message.ToString());
|
||||
if (message.Exception is CommandException cmdException)
|
||||
{
|
||||
Console.WriteLine($"[Command/{message.Severity}] {cmdException.Command.Aliases.First()}"
|
||||
+ $" failed to execute in {cmdException.Context.Channel}.");
|
||||
Console.WriteLine(cmdException);
|
||||
}
|
||||
else
|
||||
Console.WriteLine($"[General/{message.Severity}] {message}");
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -128,12 +128,10 @@ Finally, we can create a new connection to Discord.
|
||||
|
||||
Since we are writing a bot, we will be using a [DiscordSocketClient]
|
||||
along with socket entities. See @Guides.GettingStarted.Terminology
|
||||
if you are unsure of the differences.
|
||||
|
||||
To establish a new connection, we will create an instance of
|
||||
[DiscordSocketClient] in the new async main. You may pass in an
|
||||
optional @Discord.WebSocket.DiscordSocketConfig if necessary. For most
|
||||
users, the default will work fine.
|
||||
if you are unsure of the differences. To establish a new connection,
|
||||
we will create an instance of [DiscordSocketClient] in the new async
|
||||
main. You may pass in an optional @Discord.WebSocket.DiscordSocketConfig
|
||||
if necessary. For most users, the default will work fine.
|
||||
|
||||
Before connecting, we should hook the client's `Log` event to the
|
||||
log handler that we had just created. Events in Discord.Net work
|
||||
@@ -142,22 +140,33 @@ similarly to any other events in C#.
|
||||
Next, you will need to "log in to Discord" with the [LoginAsync]
|
||||
method with the application's "token."
|
||||
|
||||

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

|
||||
|
||||
> [!IMPORTANT]
|
||||
> Your bot's token can be used to gain total access to your bot, so
|
||||
> **do __NOT__ share this token with anyone else!** It may behoove you
|
||||
> to store this token in an external source if you plan on distributing
|
||||
> **do not** share this token with anyone else! You should store this
|
||||
> token in an external source if you plan on distributing
|
||||
> the source code for your bot.
|
||||
>
|
||||
> In the following example, we retrieve the token from the environment
|
||||
> variable `DiscordToken`. Please note that this is *not* designed to
|
||||
> be used in a production environment, as the secrets are stored in
|
||||
> plain-text.
|
||||
>
|
||||
> For information on how to set an environment variable, please see
|
||||
> instructions below,
|
||||
>
|
||||
> * Windows: [How to Create Environment Variables Shortcut in Windows](https://www.tenforums.com/tutorials/121742-create-environment-variables-shortcut-windows.html)
|
||||
> * Linux: [How To Read and Set Environmental and Shell Variables on a Linux VPS](https://www.digitalocean.com/community/tutorials/how-to-read-and-set-environmental-and-shell-variables-on-a-linux-vps)
|
||||
> * macOS: [How do I set environment variables on OS X?](https://apple.stackexchange.com/questions/106778/how-do-i-set-environment-variables-on-os-x)
|
||||
|
||||
We may now invoke the client's [StartAsync] method, which will
|
||||
start connection/reconnection logic. It is important to note that
|
||||
**this method will return as soon as connection logic has been started!**
|
||||
|
||||
Any methods that rely on the client's state should go in an event
|
||||
handler. This means that you should **not** directly be interacting with
|
||||
the client before it is fully ready.
|
||||
@@ -173,81 +182,34 @@ The following lines can now be added:
|
||||
At this point, feel free to start your program and see your bot come
|
||||
online in Discord.
|
||||
|
||||
> [!TIP]
|
||||
> [!WARNING]
|
||||
> Getting a warning about `A supplied token was invalid.` and/or
|
||||
> having trouble logging in? Double-check whether you have put in
|
||||
> the correct credentials and make sure that it is _not_ a client
|
||||
> secret, which is different from a token.
|
||||
|
||||
> [!TIP]
|
||||
> [!WARNING]
|
||||
> Encountering a `PlatformNotSupportedException` when starting your bot?
|
||||
> This means that you are targeting a platform where .NET's default
|
||||
> WebSocket client is not supported. Refer to the [installation guide]
|
||||
> for how to fix this.
|
||||
|
||||
> [!NOTE]
|
||||
> For your reference, you may view the [completed program].
|
||||
|
||||
[DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient
|
||||
[LoginAsync]: xref:Discord.Rest.BaseDiscordClient.LoginAsync*
|
||||
[StartAsync]: xref:Discord.WebSocket.DiscordSocketClient.StartAsync*
|
||||
[installation guide]: xref:Guides.GettingStarted.Installation
|
||||
|
||||
### Handling a 'ping'
|
||||
|
||||
> [!WARNING]
|
||||
> Please note that this is *not* a proper way to create a command.
|
||||
> Use the `CommandService` provided by the library instead, as explained
|
||||
> in the [Command Guide](xref:Guides.Commands.Intro) section.
|
||||
|
||||
Now that we have learned to open a connection to Discord, we can
|
||||
begin handling messages that the users are sending. To start out, our
|
||||
bot will listen for any message whose content is equal to `!ping` and
|
||||
will respond back with "Pong!".
|
||||
|
||||
Since we want to listen for new messages, the event to hook into
|
||||
is [MessageReceived].
|
||||
|
||||
In your program, add a method that matches the signature of the
|
||||
`MessageReceived` event - it must be a method (`Func`) that returns
|
||||
the type `Task` and takes a single parameter, a [SocketMessage]. Also,
|
||||
since we will be sending data to Discord in this method, we will flag
|
||||
it as `async`.
|
||||
|
||||
In this method, we will add an `if` block to determine if the message
|
||||
content fits the rules of our scenario - recall that it must be equal
|
||||
to `!ping`.
|
||||
|
||||
Inside the branch of this condition, we will want to send a message,
|
||||
`Pong!`, back to the channel from which the message comes from. To
|
||||
find the channel, look for the `Channel` property on the message
|
||||
parameter.
|
||||
|
||||
Next, we will want to send a message to this channel. Since the
|
||||
channel object is of type [ISocketMessageChannel], we can invoke the
|
||||
[SendMessageAsync] instance method. For the message content, send back
|
||||
a string, "Pong!".
|
||||
|
||||
You should have now added the following lines,
|
||||
|
||||
[!code-csharp[Message](samples/first-bot/message.cs)]
|
||||
|
||||
Now that your first bot is complete. You may continue to add on to this
|
||||
if you desire, but for any bots that will be carrying out multiple
|
||||
commands, it is strongly recommended to use the command framework as
|
||||
shown below.
|
||||
|
||||
> [!NOTE]
|
||||
> For your reference, you may view the [completed program].
|
||||
|
||||
[MessageReceived]: xref:Discord.WebSocket.BaseSocketClient.MessageReceived
|
||||
[SocketMessage]: xref:Discord.WebSocket.SocketMessage
|
||||
[ISocketMessageChannel]: xref:Discord.WebSocket.ISocketMessageChannel
|
||||
[SendMessageAsync]: xref:Discord.WebSocket.ISocketMessageChannel.SendMessageAsync*
|
||||
[completed program]: samples/first-bot/complete.cs
|
||||
|
||||
# Building a bot with commands
|
||||
|
||||
@Guides.Commands.Intro will guide you through how to setup a program
|
||||
that is ready for [CommandService], a service that is ready for
|
||||
advanced command usage.
|
||||
To create commands for your bot, you may choose from a variety of
|
||||
command processors available. Throughout the guides, we will be using
|
||||
the one that Discord.Net ships with. @Guides.Commands.Intro will
|
||||
guide you through how to setup a program that is ready for
|
||||
[CommandService].
|
||||
|
||||
For reference, view an [annotated example] of this structure.
|
||||
|
||||
|
||||
@@ -42,37 +42,45 @@ published to our [MyGet feed]. See
|
||||
|
||||
### [Using Visual Studio](#tab/vs-install)
|
||||
|
||||
1. Create a new solution for your bot.
|
||||
1. Create a new solution for your bot
|
||||
2. In the Solution Explorer, find the "Dependencies" element under your
|
||||
bot's project.
|
||||
3. Right click on "Dependencies", and select "Manage NuGet packages."
|
||||

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

|
||||
bot's project
|
||||
3. Right click on "Dependencies", and select "Manage NuGet packages"
|
||||
|
||||

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

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

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

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

|
||||
1. Create a new solution for your bot
|
||||
2. Open the NuGet window (Tools > NuGet > Manage NuGet packages for Solution)
|
||||
|
||||

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

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

|
||||
|
||||
### [Using Visual Studio Code](#tab/vs-code)
|
||||
|
||||
1. Create a new project for your bot.
|
||||
2. Add `Discord.Net` to your .csproj.
|
||||
1. Create a new project for your bot
|
||||
2. Add `Discord.Net` to your `*.csproj`
|
||||
|
||||
[!code[Sample .csproj](samples/project.xml)]
|
||||
|
||||
### [Using dotnet CLI](#tab/dotnet-cli)
|
||||
|
||||
1. Open command-line and navigate to where your .csproj is located.
|
||||
2. Enter `dotnet add package Discord.Net`.
|
||||
1. Launch your terminal
|
||||
2. Navigate to where your `*.csproj` is located
|
||||
3. Enter `dotnet add package Discord.Net`
|
||||
|
||||
***
|
||||
|
||||
@@ -115,16 +123,16 @@ by installing one or more custom packages as listed below.
|
||||
|
||||
1. Install or compile the following packages:
|
||||
|
||||
* `Discord.Net.Providers.WS4Net`
|
||||
* `Discord.Net.Providers.UDPClient` (Optional)
|
||||
* This is _only_ required if your bot will be utilizing voice chat.
|
||||
* `Discord.Net.Providers.WS4Net`
|
||||
* `Discord.Net.Providers.UDPClient` (Optional)
|
||||
* This is _only_ required if your bot will be utilizing voice chat.
|
||||
|
||||
2. Configure your [DiscordSocketClient] to use these custom providers
|
||||
over the default ones.
|
||||
|
||||
* To do this, set the `WebSocketProvider` and the optional
|
||||
`UdpSocketProvider` properties on the [DiscordSocketConfig] that you
|
||||
are passing into your client.
|
||||
* To do this, set the `WebSocketProvider` and the optional
|
||||
`UdpSocketProvider` properties on the [DiscordSocketConfig] that you
|
||||
are passing into your client.
|
||||
|
||||
[!code-csharp[Example](samples/netstd11.cs)]
|
||||
|
||||
|
||||
@@ -31,22 +31,33 @@ The following is the feed link of Discord.Net,
|
||||
Depending on which IDE you use, there are many different ways of
|
||||
adding the feed to your package source.
|
||||
|
||||
### [Visual Studio](#tab/vs)
|
||||
### [Using Visual Studio](#tab/vs)
|
||||
|
||||
1. Go to `Tools` > `NuGet Package Manager` > `Package Manager Settings`
|
||||
|
||||

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

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

|
||||
|
||||
> [!NOTE]
|
||||
> Remember to tick the `Include prerelease` checkbox to see the
|
||||
> Remember to tick the `Include pre-release` checkbox to see the
|
||||
> nightly builds!
|
||||
> 
|
||||
|
||||
### [Local NuGet.Config](#tab/local-nuget-config)
|
||||
### [Using dotnet CLI](#tab/cli)
|
||||
|
||||
1. Launch your terminal
|
||||
2. Navigate to where your `*.csproj` is located
|
||||
3. Type `dotnet add package Discord.Net --source https://www.myget.org/F/discord-net/api/v3/index.json`
|
||||
|
||||
### [Using Local NuGet.Config](#tab/local-nuget-config)
|
||||
|
||||
If you plan on deploying your bot or developing outside of Visual
|
||||
Studio, you will need to create a local NuGet configuration file for
|
||||
|
||||
@@ -9,7 +9,6 @@ public class Program
|
||||
{
|
||||
_client = new DiscordSocketClient();
|
||||
_client.Log += Log;
|
||||
_client.MessageReceived += MessageReceivedAsync;
|
||||
await _client.LoginAsync(TokenType.Bot,
|
||||
Environment.GetEnvironmentVariable("DiscordToken"));
|
||||
await _client.StartAsync();
|
||||
@@ -17,15 +16,6 @@ public class Program
|
||||
// Block this task until the program is closed.
|
||||
await Task.Delay(-1);
|
||||
}
|
||||
|
||||
private async Task MessageReceivedAsync(SocketMessage message)
|
||||
{
|
||||
if (message.Content == "!ping")
|
||||
{
|
||||
await message.Channel.SendMessageAsync("Pong!");
|
||||
}
|
||||
}
|
||||
|
||||
private Task Log(LogMessage msg)
|
||||
{
|
||||
Console.WriteLine(msg.ToString());
|
||||
|
||||
@@ -16,17 +16,14 @@ understand these topics to some extent before proceeding. With all
|
||||
that being said, feel free to visit us on Discord at the link below
|
||||
if you have any questions!
|
||||
|
||||
Here are some examples:
|
||||
|
||||
1. [Official samples]
|
||||
2. [Official template]
|
||||
An official collection of samples can be found
|
||||
in [our GitHub repository].
|
||||
|
||||
> [!NOTE]
|
||||
> Please note that you should *not* try to blindly copy paste
|
||||
> the code. The examples are meant to be a template or a guide.
|
||||
|
||||
[Official template]: https://github.com/foxbot/DiscordBotBase/tree/csharp/src/DiscordBot
|
||||
[Official samples]: https://github.com/RogueException/Discord.Net/tree/dev/samples
|
||||
[our GitHub repository]: https://github.com/RogueException/Discord.Net/tree/dev/samples
|
||||
[Task-based Asynchronous Pattern]: https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap
|
||||
[polymorphism]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/polymorphism
|
||||
[interface]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/interfaces/
|
||||
|
||||
@@ -39,15 +39,8 @@ namespace Discord.Commands
|
||||
/// Occurs when a command is successfully executed without any error.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This event is fired when a command has been successfully executed without any of the following errors:
|
||||
/// </para>
|
||||
/// <para>* Parsing error</para>
|
||||
/// <para>* Precondition error</para>
|
||||
/// <para>* Runtime exception</para>
|
||||
/// <para>
|
||||
/// Should the command encounter any of the aforementioned error, this event will not be raised.
|
||||
/// </para>
|
||||
/// This event is fired when a command has been executed, successfully or not. When a command fails to
|
||||
/// execute during parsing or precondition stage, the CommandInfo may not be returned.
|
||||
/// </remarks>
|
||||
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>>();
|
||||
|
||||
@@ -97,26 +97,12 @@ namespace Discord
|
||||
/// Adds or updates the permission overwrite for the given role.
|
||||
/// </summary>
|
||||
/// <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="GetPermissionOverwrite(Discord.IRole)"/>; if not, it denies the role from sending any
|
||||
/// messages to the channel.
|
||||
/// <code lang="cs">
|
||||
/// // Fetches the role and channels
|
||||
/// var role = guild.GetRole(339805618376540160);
|
||||
/// var channel = await guild.GetChannelAsync(233937283911385098);
|
||||
///
|
||||
/// // If either the of the object does not exist, bail
|
||||
/// if (role == null || channel == null) return;
|
||||
///
|
||||
/// // Fetches the previous overwrite and bail if one is found
|
||||
/// var previousOverwrite = channel.GetPermissionOverwrite(role);
|
||||
/// if (previousOverwrite.HasValue) return;
|
||||
///
|
||||
/// // Creates a new OverwritePermissions with send message set to deny and pass it into the method
|
||||
/// await channel.AddPermissionOverwriteAsync(role,
|
||||
/// new OverwritePermissions(sendMessage: PermValue.Deny));
|
||||
/// </code>
|
||||
/// messages to the channel.</para>
|
||||
/// <code language="cs" region="AddPermissionOverwriteAsyncRole"
|
||||
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IGuildChannel.Examples.cs"/>
|
||||
/// </example>
|
||||
/// <param name="role">The role to add the overwrite to.</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.
|
||||
/// </summary>
|
||||
/// <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="GetPermissionOverwrite(Discord.IUser)"/>; if not, it denies the user from sending any
|
||||
/// messages to the channel.
|
||||
/// <code lang="cs">
|
||||
/// // Fetches the role and channels
|
||||
/// var user = await guild.GetUserAsync(168693960628371456);
|
||||
/// var channel = await guild.GetChannelAsync(233937283911385098);
|
||||
///
|
||||
/// // If either the of the object does not exist, bail
|
||||
/// if (user == null || channel == null) return;
|
||||
///
|
||||
/// // Fetches the previous overwrite and bail if one is found
|
||||
/// var previousOverwrite = channel.GetPermissionOverwrite(user);
|
||||
/// if (previousOverwrite.HasValue) return;
|
||||
///
|
||||
/// // Creates a new OverwritePermissions with send message set to deny and pass it into the method
|
||||
/// await channel.AddPermissionOverwriteAsync(role,
|
||||
/// new OverwritePermissions(sendMessage: PermValue.Deny));
|
||||
/// </code>
|
||||
/// messages to the channel.</para>
|
||||
/// <code language="cs" region="AddPermissionOverwriteAsyncUser"
|
||||
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IGuildChannel.Examples.cs"/>
|
||||
/// </example>
|
||||
/// <param name="user">The user to add the overwrite to.</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.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// The following example sends a message with the current system time in RFC 1123 format to the channel and
|
||||
/// deletes itself after 5 seconds.
|
||||
/// <code language="cs">
|
||||
/// var message = await channel.SendMessageAsync(DateTimeOffset.UtcNow.ToString("R"));
|
||||
/// await Task.Delay(TimeSpan.FromSeconds(5))
|
||||
/// .ContinueWith(x => message.DeleteAsync());
|
||||
/// </code>
|
||||
/// <para>The following example sends a message with the current system time in RFC 1123 format to the channel and
|
||||
/// deletes itself after 5 seconds.</para>
|
||||
/// <code language="cs" region="SendMessageAsync"
|
||||
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
|
||||
/// </example>
|
||||
/// <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>
|
||||
@@ -35,18 +32,14 @@ namespace Discord
|
||||
/// Sends a file to this message channel with an optional caption.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// The following example uploads a local file called <c>wumpus.txt</c> along with the text
|
||||
/// <c>good discord boi</c> to the channel.
|
||||
/// <code language="cs">
|
||||
/// await channel.SendFileAsync("wumpus.txt", "good discord boi");
|
||||
/// </code>
|
||||
///
|
||||
/// The following example uploads a local image called <c>b1nzy.jpg</c> embedded inside a rich embed to the
|
||||
/// channel.
|
||||
/// <code language="cs">
|
||||
/// await channel.SendFileAsync("b1nzy.jpg",
|
||||
/// embed: new EmbedBuilder {ImageUrl = "attachment://b1nzy.jpg"}.Build());
|
||||
/// </code>
|
||||
/// <para>The following example uploads a local file called <c>wumpus.txt</c> along with the text
|
||||
/// <c>good discord boi</c> to the channel.</para>
|
||||
/// <code language="cs" region="SendFileAsync.FilePath"
|
||||
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
|
||||
/// <para>The following example uploads a local image called <c>b1nzy.jpg</c> embedded inside a rich embed to the
|
||||
/// channel.</para>
|
||||
/// <code language="cs" region="SendFileAsync.FilePath.EmbeddedImage"
|
||||
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
|
||||
/// </example>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// The following example uploads a streamed image that will be called <c>b1nzy.jpg</c> embedded inside a
|
||||
/// rich embed to the channel.
|
||||
/// <code language="cs">
|
||||
/// await channel.SendFileAsync(b1nzyStream, "b1nzy.jpg",
|
||||
/// embed: new EmbedBuilder {ImageUrl = "attachment://b1nzy.jpg"}.Build());
|
||||
/// </code>
|
||||
/// <para>The following example uploads a streamed image that will be called <c>b1nzy.jpg</c> embedded inside a
|
||||
/// rich embed to the channel.</para>
|
||||
/// <code language="cs" region="SendFileAsync.FileStream.EmbeddedImage"
|
||||
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
|
||||
/// </example>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// The following example downloads 300 messages and gets messages that belong to the user
|
||||
/// <c>53905483156684800</c>.
|
||||
/// <code lang="cs">
|
||||
/// var messages = await messageChannel.GetMessagesAsync(300).FlattenAsync();
|
||||
/// var userMessages = messages.Where(x => x.Author.Id == 53905483156684800);
|
||||
/// </code>
|
||||
/// <para>The following example downloads 300 messages and gets messages that belong to the user
|
||||
/// <c>53905483156684800</c>.</para>
|
||||
/// <code language="cs" region="GetMessagesAsync.FromLimit.Standard"
|
||||
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
|
||||
/// </example>
|
||||
/// <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
|
||||
@@ -168,10 +157,13 @@ namespace Discord
|
||||
/// of flattening.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// The following example gets 5 message prior to the message identifier <c>442012544660537354</c>.
|
||||
/// <code lang="cs">
|
||||
/// var messages = await channel.GetMessagesAsync(442012544660537354, Direction.Before, 5).FlattenAsync();
|
||||
/// </code>
|
||||
/// <para>The following example gets 5 message prior to the message identifier <c>442012544660537354</c>.</para>
|
||||
/// <code language="cs" region="GetMessagesAsync.FromId.FromMessage"
|
||||
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
|
||||
/// <para>The following example attempts to retrieve <c>messageCount</c> number of messages from the
|
||||
/// beginning of the channel and prints them to the console.</para>
|
||||
/// <code language="cs" region="GetMessagesAsync.FromId.BeginningMessages"
|
||||
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
|
||||
/// </example>
|
||||
/// <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>
|
||||
@@ -206,10 +198,9 @@ namespace Discord
|
||||
/// of flattening.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// The following example gets 5 message prior to a specific message, <c>oldMessage</c>.
|
||||
/// <code lang="cs">
|
||||
/// var messages = await channel.GetMessagesAsync(oldMessage, Direction.Before, 5).FlattenAsync();
|
||||
/// </code>
|
||||
/// <para>The following example gets 5 message prior to a specific message, <c>oldMessage</c>.</para>
|
||||
/// <code language="cs" region="GetMessagesAsync.FromMessage"
|
||||
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
|
||||
/// </example>
|
||||
/// <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>
|
||||
@@ -262,13 +253,9 @@ namespace Discord
|
||||
/// object is disposed.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// The following example keeps the client in the typing state until <c>LongRunningAsync</c> has finished.
|
||||
/// <code lang="cs">
|
||||
/// using (messageChannel.EnterTypingState())
|
||||
/// {
|
||||
/// await LongRunningAsync();
|
||||
/// }
|
||||
/// </code>
|
||||
/// <para>The following example keeps the client in the typing state until <c>LongRunningAsync</c> has finished.</para>
|
||||
/// <code language="cs" region="EnterTypingState"
|
||||
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" />
|
||||
/// </example>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Discord
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// 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();
|
||||
/// await textChannel.DeleteMessagesAsync(messages);
|
||||
/// </code>
|
||||
|
||||
@@ -440,16 +440,8 @@ namespace Discord
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// The following example creates a new text channel under an existing category named <c>Wumpus</c> with a set topic.
|
||||
/// <code lang="cs">
|
||||
/// var categories = await guild.GetCategoriesAsync();
|
||||
/// var targetCategory = categories.FirstOrDefault(x => x.Name == "wumpus");
|
||||
/// if (targetCategory == null) return;
|
||||
/// await Context.Guild.CreateTextChannelAsync(name, x =>
|
||||
/// {
|
||||
/// x.CategoryId = targetCategory.Id;
|
||||
/// x.Topic = $"This channel was created at {DateTimeOffset.UtcNow} by {user}.";
|
||||
/// });
|
||||
/// </code>
|
||||
/// <code language="cs" region="CreateTextChannelAsync"
|
||||
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Guilds\IGuild.Examples.cs"/>
|
||||
/// </example>
|
||||
/// <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>
|
||||
|
||||
@@ -23,10 +23,8 @@ namespace Discord
|
||||
/// <example>
|
||||
/// 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.
|
||||
/// <code language="cs">
|
||||
/// var userAvatarUrl = user.GetAvatarUrl() ?? user.GetDefaultAvatarUrl();
|
||||
/// await textChannel.SendMessageAsync(userAvatarUrl);
|
||||
/// </code>
|
||||
/// <code language="cs" region="GetAvatarUrl"
|
||||
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Users\IUser.Examples.cs"/>
|
||||
/// </example>
|
||||
/// <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.
|
||||
@@ -84,27 +82,18 @@ namespace Discord
|
||||
/// <remarks>
|
||||
/// This method is used to obtain or create a channel used to send a direct message.
|
||||
/// <note type="warning">
|
||||
/// In event that the current user cannot send a message to the target user, a channel can and will still be
|
||||
/// created by Discord. However, attempting to send a message will yield a
|
||||
/// <see cref="Discord.Net.HttpException"/> with a 403 as its
|
||||
/// <see cref="Discord.Net.HttpException.HttpCode"/>. There are currently no official workarounds by
|
||||
/// Discord.
|
||||
/// In event that the current user cannot send a message to the target user, a channel can and will
|
||||
/// still be created by Discord. However, attempting to send a message will yield a
|
||||
/// <see cref="Discord.Net.HttpException"/> with a 403 as its
|
||||
/// <see cref="Discord.Net.HttpException.HttpCode"/>. There are currently no official workarounds by
|
||||
/// Discord.
|
||||
/// </note>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// The following example attempts to send a direct message to the target user and logs the incident should
|
||||
/// it fail.
|
||||
/// <code language="cs">
|
||||
/// var channel = await user.GetOrCreateDMChannelAsync();
|
||||
/// try
|
||||
/// {
|
||||
/// await channel.SendMessageAsync("Awesome stuff!");
|
||||
/// }
|
||||
/// catch (Discord.Net.HttpException ex) when (ex.HttpCode == 403)
|
||||
/// {
|
||||
/// Console.WriteLine($"Boo, I cannot message {user}");
|
||||
/// }
|
||||
/// </code>
|
||||
/// <code region="GetOrCreateDMChannelAsync" language="cs"
|
||||
/// source="../../../Discord.Net.Examples/Core/Entities/Users/IUser.Examples.cs"/>
|
||||
/// </example>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace Discord
|
||||
/// Gets a generic channel.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code lang="cs" title="Example method">
|
||||
/// <code language="cs" title="Example method">
|
||||
/// var channel = await _client.GetChannelAsync(381889909113225237);
|
||||
/// if (channel != null && channel is IMessageChannel msgChannel)
|
||||
/// {
|
||||
@@ -194,7 +194,7 @@ namespace Discord
|
||||
/// Gets a user.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code lang="cs" title="Example method">
|
||||
/// <code language="cs" title="Example method">
|
||||
/// var user = await _client.GetUserAsync(168693960628371456);
|
||||
/// if (user != null)
|
||||
/// Console.WriteLine($"{user} is created at {user.CreatedAt}.";
|
||||
@@ -212,7 +212,7 @@ namespace Discord
|
||||
/// Gets a user.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code lang="cs" title="Example method">
|
||||
/// <code language="cs" title="Example method">
|
||||
/// var user = await _client.GetUserAsync("Still", "2876");
|
||||
/// if (user != null)
|
||||
/// Console.WriteLine($"{user} is created at {user.CreatedAt}.";
|
||||
@@ -232,7 +232,7 @@ namespace Discord
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// The following example gets the most optimal voice region from the collection.
|
||||
/// <code lang="cs">
|
||||
/// <code language="cs">
|
||||
/// var regions = await client.GetVoiceRegionsAsync();
|
||||
/// var optimalRegion = regions.FirstOrDefault(x => x.IsOptimal);
|
||||
/// </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>
|
||||
/// <example>
|
||||
/// 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 targetCategory = categories.FirstOrDefault(x => x.Name == "wumpus");
|
||||
/// if (targetCategory == null) return;
|
||||
|
||||
@@ -18,6 +18,10 @@ namespace Discord.WebSocket
|
||||
/// see the derived classes of <see cref="SocketChannel"/> for more details.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code language="cs" region="ChannelCreated"
|
||||
/// source="..\Discord.Net.Examples\WebSocket\BaseSocketClient.Events.Examples.cs"/>
|
||||
/// </example>
|
||||
public event Func<SocketChannel, Task> ChannelCreated
|
||||
{
|
||||
add { _channelCreatedEvent.Add(value); }
|
||||
@@ -36,6 +40,10 @@ namespace Discord.WebSocket
|
||||
/// see the derived classes of <see cref="SocketChannel"/> for more details.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code language="cs" region="ChannelDestroyed"
|
||||
/// source="..\Discord.Net.Examples\WebSocket\BaseSocketClient.Events.Examples.cs"/>
|
||||
/// </example>
|
||||
public event Func<SocketChannel, Task> ChannelDestroyed {
|
||||
add { _channelDestroyedEvent.Add(value); }
|
||||
remove { _channelDestroyedEvent.Remove(value); }
|
||||
@@ -54,6 +62,10 @@ namespace Discord.WebSocket
|
||||
/// <see cref="SocketChannel"/> for more details.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code language="cs" region="ChannelUpdated"
|
||||
/// source="..\Discord.Net.Examples\WebSocket\BaseSocketClient.Events.Examples.cs"/>
|
||||
/// </example>
|
||||
public event Func<SocketChannel, SocketChannel, Task> ChannelUpdated {
|
||||
add { _channelUpdatedEvent.Add(value); }
|
||||
remove { _channelUpdatedEvent.Remove(value); }
|
||||
@@ -74,6 +86,11 @@ namespace Discord.WebSocket
|
||||
/// derived classes of <see cref="SocketMessage"/> for more details.
|
||||
/// </para>
|
||||
/// </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 {
|
||||
add { _messageReceivedEvent.Add(value); }
|
||||
remove { _messageReceivedEvent.Remove(value); }
|
||||
@@ -102,6 +119,10 @@ namespace Discord.WebSocket
|
||||
/// <see cref="ISocketMessageChannel"/> parameter.
|
||||
/// </para>
|
||||
/// </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 {
|
||||
add { _messageDeletedEvent.Add(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>>();
|
||||
/// <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 {
|
||||
add { _reactionAddedEvent.Add(value); }
|
||||
remove { _reactionAddedEvent.Remove(value); }
|
||||
|
||||
@@ -97,13 +97,15 @@ namespace Discord.WebSocket
|
||||
/// <remarks>
|
||||
/// This method gets the user present in the WebSocket cache with the given condition.
|
||||
/// <note type="warning">
|
||||
/// Sometimes a user may return <c>null</c> due to Discord not sending offline users in large
|
||||
/// guilds (i.e. guild with 100+ members) actively. To download users on startup, consider enabling
|
||||
/// <see cref="DiscordSocketConfig.AlwaysDownloadUsers"/>.
|
||||
/// Sometimes a user may return <c>null</c> due to Discord not sending offline users in large guilds
|
||||
/// (i.e. guild with 100+ members) actively. To download users on startup and to see more information
|
||||
/// about this subject, see <see cref="Discord.WebSocket.DiscordSocketConfig.AlwaysDownloadUsers" />.
|
||||
/// </note>
|
||||
/// <note>
|
||||
/// 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>
|
||||
/// </remarks>
|
||||
/// <returns>
|
||||
@@ -114,20 +116,22 @@ namespace Discord.WebSocket
|
||||
/// <summary>
|
||||
/// Gets a user.
|
||||
/// </summary>
|
||||
/// <param name="username">The name of the user.</param>
|
||||
/// <param name="discriminator">The discriminator value of the user.</param>
|
||||
/// <remarks>
|
||||
/// This method gets the user present in the WebSocket cache with the given condition.
|
||||
/// <note type="warning">
|
||||
/// Sometimes a user may return <c>null</c> due to Discord not sending offline users in large
|
||||
/// guilds (i.e. guild with 100+ members) actively. To download users on startup, consider enabling
|
||||
/// <see cref="DiscordSocketConfig.AlwaysDownloadUsers"/>.
|
||||
/// Sometimes a user may return <c>null</c> due to Discord not sending offline users in large guilds
|
||||
/// (i.e. guild with 100+ members) actively. To download users on startup and to see more information
|
||||
/// about this subject, see <see cref="Discord.WebSocket.DiscordSocketConfig.AlwaysDownloadUsers" />.
|
||||
/// </note>
|
||||
/// <note>
|
||||
/// 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>
|
||||
/// </remarks>
|
||||
/// <param name="username">The name of the user.</param>
|
||||
/// <param name="discriminator">The discriminator value of the user.</param>
|
||||
/// <returns>
|
||||
/// A generic WebSocket-based user; <c>null</c> when the user cannot be found.
|
||||
/// </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
|
||||
/// traffic. If you are using the command system, the default user TypeReader may fail to find the user
|
||||
/// due to this issue. This may be resolved at v3 of the library. Until then, you may want to consider
|
||||
/// overriding the TypeReader and use <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>
|
||||
/// </remarks>
|
||||
public bool AlwaysDownloadUsers { get; set; } = false;
|
||||
|
||||
@@ -535,7 +535,7 @@ namespace Discord.WebSocket
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// 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 targetCategory = categories.FirstOrDefault(x => x.Name == "wumpus");
|
||||
/// if (targetCategory == null) return;
|
||||
|
||||
@@ -10,6 +10,11 @@ namespace Discord.WebSocket
|
||||
/// <summary>
|
||||
/// Gets the ID of the user who added the reaction.
|
||||
/// </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>
|
||||
/// A user snowflake identifier associated with the user.
|
||||
/// </returns>
|
||||
@@ -17,6 +22,18 @@ namespace Discord.WebSocket
|
||||
/// <summary>
|
||||
/// Gets the user who added the reaction if possible.
|
||||
/// </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>
|
||||
/// A user object where possible; a value is not always returned.
|
||||
/// </returns>
|
||||
|
||||
Reference in New Issue
Block a user