* Remove template in favor of official samples * Fixed a variable name copy pasta mistake line 35 was _database.GetData() instead of DBService.GetData() * Experimental theme change * Change paragraph, code, heading fonts * Widen viewport * Update DocFX.Plugins.LastModified v1.2.3 * Exclude Discord.API in docs * Add remarks for SocketReaction properties * Add examples for BaseSocketClient.Events * Add additional clarification for some methods * Move IUser and IGuildChannel examples * Clarify several guides samples with notes - Reword TypeReader comment to avoid giving the idea that the sample itself is "obsolete" - Remove CommandException logging comment regarding C#7.0 as the version is now the standard across VS2017 and up - Remove suggestion about handling result in command handler since it is now advised to use CommandExecuted instead + Add additional comment to clarify ctor for DI setup * Add/migrate code examples * Incorporate material design theme License @ https://github.com/ovasquez * Update installation and nightly guide * Fix improper indentations made obvious by the widen viewport * Fix minor grammar issues + Add installation for nightly build using dotnet CLI * Fix nav level indentation * Revise "Your First Bot" article * Merge some paragraphs to avoid clutter while keeping readability * Reword the use of command framework + Add additional warning/note about environment variable * Add additional indent level * Fix indentation text warping * Remove connections sample * Update logging sample Remove redundant part of the sample * Remove mention of RPC * Remove misleading section about commands - Remove command sample from complete snippet * Revise "Your First Bot" command paragraphs * Change wording to hint devs that additional command parser packages may be available, as more and more begin to crop up * Update themes * Add XML docs contribution guidelines Update guidelines * Update CommandExecuted remarks * Fix precondition remarks typo no one saw that ok * Fix permission sample in docfx * Fix IMessageChannel samples * Update docs/_template/light-dark-theme/styles/docfx.vendor.minify.css Co-Authored-By: Still34 <341464@gmail.com> * Update docs/_template/light-dark-theme/styles/material.css Co-Authored-By: Still34 <341464@gmail.com> * Update docs/_template/light-dark-theme/styles/material.css Co-Authored-By: Still34 <341464@gmail.com>tags/2.1.0
| @@ -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 | ||||
| ## General Guidelines | |||||
| First of all, thank you for your interest in contributing to our | |||||
| documentation work. We really appreciate it! That being said, | |||||
| there are several guidelines you should attempt to follow when adding | |||||
| to/changing the documentation. | |||||
| We do not have any strict conditions for writing documentation, | |||||
| but keep these guidelines in mind: | |||||
| ## General Guidelines | |||||
| * Keep code samples in the `guides/samples` folder | |||||
| * Keep code samples in each section's `samples` folder | |||||
| * When referencing an object in the API, link to its page in the | * When referencing an object in the API, link to its page in the | ||||
| API documentation | API documentation | ||||
| * Documentation should be written in an FAQ/Wiki-style format | * Documentation should be written in an FAQ/Wiki-style format | ||||
| * Documentation should be written in clear and proper English* | * Documentation should be written in clear and proper English* | ||||
| \* If anyone is interested in translating documentation into other | \* If anyone is interested in translating documentation into other | ||||
| languages, please open an issue or contact me on | |||||
| Discord (`foxbot#0282`). | |||||
| languages, please open an issue or contact `foxbot#0282` on | |||||
| Discord. | |||||
| ## XML Docstrings Guidelines | |||||
| ## Style Consistencies | |||||
| * When using the `<summary>` tag, use concise verbs. For example: | |||||
| ```cs | |||||
| /// <summary> Gets or sets the guild user in this object. </summary> | |||||
| public IGuildUser GuildUser { get; set; } | |||||
| ``` | |||||
| * Use a ruler set at 70 characters | |||||
| * The `<summary>` tag should not be more than 3 lines long. Consider | |||||
| simplifying the terms or using the `<remarks>` tag instead. | |||||
| * When using the `<code>` tag, put the code sample within the | |||||
| `src/Discord.Net.Examples` project under the corresponding path of | |||||
| the object and surround the code with a `#region` tag. | |||||
| * If the remarks you are looking to write are too long, consider | |||||
| writing a shorter version in the XML docs while keeping the longer | |||||
| version in the `overwrites` folder using the DocFX overwrites syntax. | |||||
| * You may find an example of this in the samples provided within | |||||
| the folder. | |||||
| ## Docs Guide Guidelines | |||||
| * Use a ruler set at 70 characters (use the docs workspace provided | |||||
| if you are using Visual Studio Code) | |||||
| * Links should use long syntax | * Links should use long syntax | ||||
| * Pages should be short and concise, not broad and long | * Pages should be short and concise, not broad and long | ||||
| @@ -31,5 +54,7 @@ Please consult the [API Documentation] for more information. | |||||
| ## Recommended Reads | ## Recommended Reads | ||||
| * http://docs.microsoft.com | |||||
| * http://flask.pocoo.org/docs/0.12/ | |||||
| * [Microsoft Docs](https://docs.microsoft.com) | |||||
| * [Flask Docs](https://flask.pocoo.org/docs/1.0/) | |||||
| * [DocFX Manual](https://dotnet.github.io/docfx/) | |||||
| * [Sandcastle XML Guide](http://ewsoftware.github.io/XMLCommentsGuide) | |||||
| @@ -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. | ||||
| @@ -1,4 +1,4 @@ | |||||
| <configuration> | <configuration> | ||||
| <dllmap os="linux" cpu="x86-64" wordsize="64" dll="git2-8e0b172" target="lib/linux-x64/libgit2-8e0b172.so" /> | |||||
| <dllmap os="osx" cpu="x86,x86-64" dll="git2-8e0b172" target="lib/osx/libgit2-8e0b172.dylib" /> | |||||
| <dllmap os="linux" cpu="x86-64" wordsize="64" dll="git2-a904fc6" target="lib/linux-x64/libgit2-a904fc6.so" /> | |||||
| <dllmap os="osx" cpu="x86,x86-64" dll="git2-a904fc6" target="lib/osx/libgit2-a904fc6.dylib" /> | |||||
| </configuration> | </configuration> | ||||
| @@ -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"/> | ||||
| @@ -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; | ||||
| } | } | ||||
| @@ -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%; | |||||
| } | |||||
| @@ -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 | type: Namespace | ||||
| - exclude: | - exclude: | ||||
| uidRegex: ^Discord\.Analyzers$ | uidRegex: ^Discord\.Analyzers$ | ||||
| type: Namespace | |||||
| - exclude: | |||||
| uidRegex: ^Discord\.API$ | |||||
| type: Namespace | 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. | ||||
| // Keep in mind that result does not indicate a return value | |||||
| // rather an object stating if the command executed successfully. | |||||
| var result = await _commands.ExecuteAsync( | |||||
| await _commands.ExecuteAsync( | |||||
| context: context, | context: context, | ||||
| argPos: argPos, | argPos: argPos, | ||||
| services: null); | services: null); | ||||
| // Optionally, we may inform the user if the command fails | |||||
| // to be executed; however, this may not always be desired, | |||||
| // as it may clog up the request queue should a user spam a | |||||
| // command. | |||||
| // if (!result.IsSuccess) | |||||
| // await context.Channel.SendMessageAsync(result.ErrorReason); | |||||
| } | } | ||||
| } | } | ||||
| @@ -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 | |||||
| // with Discord.Commands | |||||
| // Please note that the library already supports type reading | |||||
| // primitive types such as bool. This example is merely used | |||||
| // to demonstrate how one could write a simple TypeReader. | |||||
| using Discord; | using Discord; | ||||
| using Discord.Commands; | using Discord.Commands; | ||||
| @@ -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; | |||||
| static void Main(string[] args) => new Program().MainAsync().GetAwaiter().GetResult(); | |||||
| public async Task MainAsync() | |||||
| public LoggingService(DiscordSocketClient client, CommandService command) | |||||
| { | { | ||||
| _client = new DiscordSocketClient(new DiscordSocketConfig | |||||
| { | |||||
| LogLevel = LogSeverity.Info | |||||
| }); | |||||
| _client.Log += Log; | |||||
| await _client.LoginAsync(TokenType.Bot, Environment.GetEnvironmentVariable("DiscordToken")); | |||||
| await _client.StartAsync(); | |||||
| await Task.Delay(-1); | |||||
| client.Log += LogAsync; | |||||
| command.Log += LogAsync; | |||||
| } | } | ||||
| private Task Log(LogMessage message) | |||||
| private Task LogAsync(LogMessage message) | |||||
| { | { | ||||
| Console.WriteLine(message.ToString()); | |||||
| if (message.Exception is CommandException cmdException) | |||||
| { | |||||
| Console.WriteLine($"[Command/{message.Severity}] {cmdException.Command.Aliases.First()}" | |||||
| + $" failed to execute in {cmdException.Context.Channel}."); | |||||
| Console.WriteLine(cmdException); | |||||
| } | |||||
| else | |||||
| Console.WriteLine($"[General/{message.Severity}] {message}"); | |||||
| return Task.CompletedTask; | return Task.CompletedTask; | ||||
| } | } | ||||
| } | } | ||||
| @@ -128,12 +128,10 @@ Finally, we can create a new connection to Discord. | |||||
| Since we are writing a bot, we will be using a [DiscordSocketClient] | Since we are writing a bot, we will be using a [DiscordSocketClient] | ||||
| along with socket entities. See @Guides.GettingStarted.Terminology | along with socket entities. See @Guides.GettingStarted.Terminology | ||||
| if you are unsure of the differences. | |||||
| To establish a new connection, we will create an instance of | |||||
| [DiscordSocketClient] in the new async main. You may pass in an | |||||
| optional @Discord.WebSocket.DiscordSocketConfig if necessary. For most | |||||
| users, the default will work fine. | |||||
| if you are unsure of the differences. To establish a new connection, | |||||
| we will create an instance of [DiscordSocketClient] in the new async | |||||
| main. You may pass in an optional @Discord.WebSocket.DiscordSocketConfig | |||||
| if necessary. For most users, the default will work fine. | |||||
| Before connecting, we should hook the client's `Log` event to the | Before connecting, we should hook the client's `Log` event to the | ||||
| log handler that we had just created. Events in Discord.Net work | log handler that we had just created. Events in Discord.Net work | ||||
| @@ -142,22 +140,33 @@ similarly to any other events in C#. | |||||
| Next, you will need to "log in to Discord" with the [LoginAsync] | Next, you will need to "log in to Discord" with the [LoginAsync] | ||||
| method with the application's "token." | method with the application's "token." | ||||
|  | |||||
| > [!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 | |||||
| > to store this token in an external source if you plan on distributing | |||||
| > **do not** share this token with anyone else! You should store this | |||||
| > token in an external source if you plan on distributing | |||||
| > the source code for your bot. | > the source code for your bot. | ||||
| > | |||||
| > In the following example, we retrieve the token from the environment | |||||
| > variable `DiscordToken`. Please note that this is *not* designed to | |||||
| > be used in a production environment, as the secrets are stored in | |||||
| > plain-text. | |||||
| > | |||||
| > For information on how to set an environment variable, please see | |||||
| > instructions below, | |||||
| > | |||||
| > * Windows: [How to Create Environment Variables Shortcut in Windows](https://www.tenforums.com/tutorials/121742-create-environment-variables-shortcut-windows.html) | |||||
| > * Linux: [How To Read and Set Environmental and Shell Variables on a Linux VPS](https://www.digitalocean.com/community/tutorials/how-to-read-and-set-environmental-and-shell-variables-on-a-linux-vps) | |||||
| > * macOS: [How do I set environment variables on OS X?](https://apple.stackexchange.com/questions/106778/how-do-i-set-environment-variables-on-os-x) | |||||
| We may now invoke the client's [StartAsync] method, which will | We may now invoke the client's [StartAsync] method, which will | ||||
| start connection/reconnection logic. It is important to note that | start connection/reconnection logic. It is important to note that | ||||
| **this method will return as soon as connection logic has been started!** | **this method will return as soon as connection logic has been started!** | ||||
| Any methods that rely on the client's state should go in an event | Any methods that rely on the client's state should go in an event | ||||
| handler. This means that you should **not** directly be interacting with | handler. This means that you should **not** directly be interacting with | ||||
| the client before it is fully ready. | the client before it is fully ready. | ||||
| @@ -173,81 +182,34 @@ The following lines can now be added: | |||||
| At this point, feel free to start your program and see your bot come | At this point, feel free to start your program and see your bot come | ||||
| online in Discord. | online in Discord. | ||||
| > [!TIP] | |||||
| > [!WARNING] | |||||
| > Getting a warning about `A supplied token was invalid.` and/or | > Getting a warning about `A supplied token was invalid.` and/or | ||||
| > having trouble logging in? Double-check whether you have put in | > having trouble logging in? Double-check whether you have put in | ||||
| > the correct credentials and make sure that it is _not_ a client | > the correct credentials and make sure that it is _not_ a client | ||||
| > secret, which is different from a token. | > secret, which is different from a token. | ||||
| > [!TIP] | |||||
| > [!WARNING] | |||||
| > Encountering a `PlatformNotSupportedException` when starting your bot? | > Encountering a `PlatformNotSupportedException` when starting your bot? | ||||
| > This means that you are targeting a platform where .NET's default | > This means that you are targeting a platform where .NET's default | ||||
| > WebSocket client is not supported. Refer to the [installation guide] | > WebSocket client is not supported. Refer to the [installation guide] | ||||
| > for how to fix this. | > for how to fix this. | ||||
| > [!NOTE] | |||||
| > For your reference, you may view the [completed program]. | |||||
| [DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient | [DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient | ||||
| [LoginAsync]: xref:Discord.Rest.BaseDiscordClient.LoginAsync* | [LoginAsync]: xref:Discord.Rest.BaseDiscordClient.LoginAsync* | ||||
| [StartAsync]: xref:Discord.WebSocket.DiscordSocketClient.StartAsync* | [StartAsync]: xref:Discord.WebSocket.DiscordSocketClient.StartAsync* | ||||
| [installation guide]: xref:Guides.GettingStarted.Installation | [installation guide]: xref:Guides.GettingStarted.Installation | ||||
| ### Handling a 'ping' | |||||
| > [!WARNING] | |||||
| > Please note that this is *not* a proper way to create a command. | |||||
| > Use the `CommandService` provided by the library instead, as explained | |||||
| > in the [Command Guide](xref:Guides.Commands.Intro) section. | |||||
| Now that we have learned to open a connection to Discord, we can | |||||
| begin handling messages that the users are sending. To start out, our | |||||
| bot will listen for any message whose content is equal to `!ping` and | |||||
| will respond back with "Pong!". | |||||
| Since we want to listen for new messages, the event to hook into | |||||
| is [MessageReceived]. | |||||
| In your program, add a method that matches the signature of the | |||||
| `MessageReceived` event - it must be a method (`Func`) that returns | |||||
| the type `Task` and takes a single parameter, a [SocketMessage]. Also, | |||||
| since we will be sending data to Discord in this method, we will flag | |||||
| it as `async`. | |||||
| In this method, we will add an `if` block to determine if the message | |||||
| content fits the rules of our scenario - recall that it must be equal | |||||
| to `!ping`. | |||||
| Inside the branch of this condition, we will want to send a message, | |||||
| `Pong!`, back to the channel from which the message comes from. To | |||||
| find the channel, look for the `Channel` property on the message | |||||
| parameter. | |||||
| Next, we will want to send a message to this channel. Since the | |||||
| channel object is of type [ISocketMessageChannel], we can invoke the | |||||
| [SendMessageAsync] instance method. For the message content, send back | |||||
| a string, "Pong!". | |||||
| You should have now added the following lines, | |||||
| [!code-csharp[Message](samples/first-bot/message.cs)] | |||||
| Now that your first bot is complete. You may continue to add on to this | |||||
| if you desire, but for any bots that will be carrying out multiple | |||||
| commands, it is strongly recommended to use the command framework as | |||||
| shown below. | |||||
| > [!NOTE] | |||||
| > For your reference, you may view the [completed program]. | |||||
| [MessageReceived]: xref:Discord.WebSocket.BaseSocketClient.MessageReceived | |||||
| [SocketMessage]: xref:Discord.WebSocket.SocketMessage | |||||
| [ISocketMessageChannel]: xref:Discord.WebSocket.ISocketMessageChannel | |||||
| [SendMessageAsync]: xref:Discord.WebSocket.ISocketMessageChannel.SendMessageAsync* | |||||
| [completed program]: samples/first-bot/complete.cs | [completed program]: samples/first-bot/complete.cs | ||||
| # Building a bot with commands | # Building a bot with commands | ||||
| @Guides.Commands.Intro will guide you through how to setup a program | |||||
| that is ready for [CommandService], a service that is ready for | |||||
| advanced command usage. | |||||
| To create commands for your bot, you may choose from a variety of | |||||
| command processors available. Throughout the guides, we will be using | |||||
| the one that Discord.Net ships with. @Guides.Commands.Intro will | |||||
| guide you through how to setup a program that is ready for | |||||
| [CommandService]. | |||||
| For reference, view an [annotated example] of this structure. | For reference, view an [annotated example] of this structure. | ||||
| @@ -42,37 +42,45 @@ published to our [MyGet feed]. See | |||||
| ### [Using Visual Studio](#tab/vs-install) | ### [Using Visual Studio](#tab/vs-install) | ||||
| 1. Create a new solution for your bot. | |||||
| 1. Create a new solution for your bot | |||||
| 2. In the Solution Explorer, find the "Dependencies" element under your | 2. In the Solution Explorer, find the "Dependencies" element under your | ||||
| bot's project. | |||||
| 3. Right click on "Dependencies", and select "Manage NuGet packages." | |||||
|  | |||||
| 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) | ### [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) | ### [Using Visual Studio Code](#tab/vs-code) | ||||
| 1. Create a new project for your bot. | |||||
| 2. Add `Discord.Net` to your .csproj. | |||||
| 1. Create a new project for your bot | |||||
| 2. Add `Discord.Net` to your `*.csproj` | |||||
| [!code[Sample .csproj](samples/project.xml)] | [!code[Sample .csproj](samples/project.xml)] | ||||
| ### [Using dotnet CLI](#tab/dotnet-cli) | ### [Using dotnet CLI](#tab/dotnet-cli) | ||||
| 1. Open command-line and navigate to where your .csproj is located. | |||||
| 2. Enter `dotnet add package Discord.Net`. | |||||
| 1. Launch your terminal | |||||
| 2. Navigate to where your `*.csproj` is located | |||||
| 3. Enter `dotnet add package Discord.Net` | |||||
| *** | *** | ||||
| @@ -115,16 +123,16 @@ by installing one or more custom packages as listed below. | |||||
| 1. Install or compile the following packages: | 1. Install or compile the following packages: | ||||
| * `Discord.Net.Providers.WS4Net` | |||||
| * `Discord.Net.Providers.UDPClient` (Optional) | |||||
| * This is _only_ required if your bot will be utilizing voice chat. | |||||
| * `Discord.Net.Providers.WS4Net` | |||||
| * `Discord.Net.Providers.UDPClient` (Optional) | |||||
| * This is _only_ required if your bot will be utilizing voice chat. | |||||
| 2. Configure your [DiscordSocketClient] to use these custom providers | 2. Configure your [DiscordSocketClient] to use these custom providers | ||||
| over the default ones. | over the default ones. | ||||
| * To do this, set the `WebSocketProvider` and the optional | |||||
| `UdpSocketProvider` properties on the [DiscordSocketConfig] that you | |||||
| are passing into your client. | |||||
| * To do this, set the `WebSocketProvider` and the optional | |||||
| `UdpSocketProvider` properties on the [DiscordSocketConfig] that you | |||||
| are passing into your client. | |||||
| [!code-csharp[Example](samples/netstd11.cs)] | [!code-csharp[Example](samples/netstd11.cs)] | ||||
| @@ -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: | |||||
| 1. [Official samples] | |||||
| 2. [Official template] | |||||
| An official collection of samples can be found | |||||
| in [our GitHub repository]. | |||||
| > [!NOTE] | > [!NOTE] | ||||
| > Please note that you should *not* try to blindly copy paste | > Please note that you should *not* try to blindly copy paste | ||||
| > the code. The examples are meant to be a template or a guide. | > the code. The examples are meant to be a template or a guide. | ||||
| [Official template]: https://github.com/foxbot/DiscordBotBase/tree/csharp/src/DiscordBot | |||||
| [Official samples]: https://github.com/RogueException/Discord.Net/tree/dev/samples | |||||
| [our GitHub repository]: https://github.com/RogueException/Discord.Net/tree/dev/samples | |||||
| [Task-based Asynchronous Pattern]: https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap | [Task-based Asynchronous Pattern]: https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap | ||||
| [polymorphism]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/polymorphism | [polymorphism]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/polymorphism | ||||
| [interface]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/interfaces/ | [interface]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/interfaces/ | ||||
| @@ -39,15 +39,8 @@ namespace Discord.Commands | |||||
| /// Occurs when a command is successfully executed without any error. | /// Occurs when a command is successfully executed without any error. | ||||
| /// </summary> | /// </summary> | ||||
| /// <remarks> | /// <remarks> | ||||
| /// <para> | |||||
| /// This event is fired when a command has been successfully executed without any of the following errors: | |||||
| /// </para> | |||||
| /// <para>* Parsing error</para> | |||||
| /// <para>* Precondition error</para> | |||||
| /// <para>* Runtime exception</para> | |||||
| /// <para> | |||||
| /// Should the command encounter any of the aforementioned error, this event will not be raised. | |||||
| /// </para> | |||||
| /// This event is fired when a command has been executed, successfully or not. When a command fails to | |||||
| /// execute during parsing or precondition stage, the CommandInfo may not be returned. | |||||
| /// </remarks> | /// </remarks> | ||||
| public event Func<Optional<CommandInfo>, ICommandContext, IResult, Task> CommandExecuted { add { _commandExecutedEvent.Add(value); } remove { _commandExecutedEvent.Remove(value); } } | public event Func<Optional<CommandInfo>, ICommandContext, IResult, Task> CommandExecuted { add { _commandExecutedEvent.Add(value); } remove { _commandExecutedEvent.Remove(value); } } | ||||
| internal readonly AsyncEvent<Func<Optional<CommandInfo>, ICommandContext, IResult, Task>> _commandExecutedEvent = new AsyncEvent<Func<Optional<CommandInfo>, ICommandContext, IResult, Task>>(); | internal readonly AsyncEvent<Func<Optional<CommandInfo>, ICommandContext, IResult, Task>> _commandExecutedEvent = new AsyncEvent<Func<Optional<CommandInfo>, ICommandContext, IResult, Task>>(); | ||||
| @@ -97,26 +97,12 @@ namespace Discord | |||||
| /// Adds or updates the permission overwrite for the given role. | /// Adds or updates the permission overwrite for the given role. | ||||
| /// </summary> | /// </summary> | ||||
| /// <example> | /// <example> | ||||
| /// The following example fetches a role via <see cref="IGuild.GetRole"/> and a channel via | |||||
| /// <para>The following example fetches a role via <see cref="IGuild.GetRole"/> and a channel via | |||||
| /// <see cref="IGuild.GetChannelAsync"/>. Next, it checks if an overwrite had already been set via | /// <see cref="IGuild.GetChannelAsync"/>. Next, it checks if an overwrite had already been set via | ||||
| /// <see cref="GetPermissionOverwrite(Discord.IRole)"/>; if not, it denies the role from sending any | /// <see cref="GetPermissionOverwrite(Discord.IRole)"/>; if not, it denies the role from sending any | ||||
| /// messages to the channel. | |||||
| /// <code lang="cs"> | |||||
| /// // Fetches the role and channels | |||||
| /// var role = guild.GetRole(339805618376540160); | |||||
| /// var channel = await guild.GetChannelAsync(233937283911385098); | |||||
| /// | |||||
| /// // If either the of the object does not exist, bail | |||||
| /// if (role == null || channel == null) return; | |||||
| /// | |||||
| /// // Fetches the previous overwrite and bail if one is found | |||||
| /// var previousOverwrite = channel.GetPermissionOverwrite(role); | |||||
| /// if (previousOverwrite.HasValue) return; | |||||
| /// | |||||
| /// // Creates a new OverwritePermissions with send message set to deny and pass it into the method | |||||
| /// await channel.AddPermissionOverwriteAsync(role, | |||||
| /// new OverwritePermissions(sendMessage: PermValue.Deny)); | |||||
| /// </code> | |||||
| /// messages to the channel.</para> | |||||
| /// <code language="cs" region="AddPermissionOverwriteAsyncRole" | |||||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IGuildChannel.Examples.cs"/> | |||||
| /// </example> | /// </example> | ||||
| /// <param name="role">The role to add the overwrite to.</param> | /// <param name="role">The role to add the overwrite to.</param> | ||||
| /// <param name="permissions">The overwrite to add to the role.</param> | /// <param name="permissions">The overwrite to add to the role.</param> | ||||
| @@ -130,26 +116,12 @@ namespace Discord | |||||
| /// Adds or updates the permission overwrite for the given user. | /// Adds or updates the permission overwrite for the given user. | ||||
| /// </summary> | /// </summary> | ||||
| /// <example> | /// <example> | ||||
| /// The following example fetches a user via <see cref="IGuild.GetUserAsync"/> and a channel via | |||||
| /// <para>The following example fetches a user via <see cref="IGuild.GetUserAsync"/> and a channel via | |||||
| /// <see cref="IGuild.GetChannelAsync"/>. Next, it checks if an overwrite had already been set via | /// <see cref="IGuild.GetChannelAsync"/>. Next, it checks if an overwrite had already been set via | ||||
| /// <see cref="GetPermissionOverwrite(Discord.IUser)"/>; if not, it denies the user from sending any | /// <see cref="GetPermissionOverwrite(Discord.IUser)"/>; if not, it denies the user from sending any | ||||
| /// messages to the channel. | |||||
| /// <code lang="cs"> | |||||
| /// // Fetches the role and channels | |||||
| /// var user = await guild.GetUserAsync(168693960628371456); | |||||
| /// var channel = await guild.GetChannelAsync(233937283911385098); | |||||
| /// | |||||
| /// // If either the of the object does not exist, bail | |||||
| /// if (user == null || channel == null) return; | |||||
| /// | |||||
| /// // Fetches the previous overwrite and bail if one is found | |||||
| /// var previousOverwrite = channel.GetPermissionOverwrite(user); | |||||
| /// if (previousOverwrite.HasValue) return; | |||||
| /// | |||||
| /// // Creates a new OverwritePermissions with send message set to deny and pass it into the method | |||||
| /// await channel.AddPermissionOverwriteAsync(role, | |||||
| /// new OverwritePermissions(sendMessage: PermValue.Deny)); | |||||
| /// </code> | |||||
| /// messages to the channel.</para> | |||||
| /// <code language="cs" region="AddPermissionOverwriteAsyncUser" | |||||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IGuildChannel.Examples.cs"/> | |||||
| /// </example> | /// </example> | ||||
| /// <param name="user">The user to add the overwrite to.</param> | /// <param name="user">The user to add the overwrite to.</param> | ||||
| /// <param name="permissions">The overwrite to add to the user.</param> | /// <param name="permissions">The overwrite to add to the user.</param> | ||||
| @@ -14,13 +14,10 @@ namespace Discord | |||||
| /// Sends a message to this message channel. | /// Sends a message to this message channel. | ||||
| /// </summary> | /// </summary> | ||||
| /// <example> | /// <example> | ||||
| /// The following example sends a message with the current system time in RFC 1123 format to the channel and | |||||
| /// deletes itself after 5 seconds. | |||||
| /// <code language="cs"> | |||||
| /// var message = await channel.SendMessageAsync(DateTimeOffset.UtcNow.ToString("R")); | |||||
| /// await Task.Delay(TimeSpan.FromSeconds(5)) | |||||
| /// .ContinueWith(x => message.DeleteAsync()); | |||||
| /// </code> | |||||
| /// <para>The following example sends a message with the current system time in RFC 1123 format to the channel and | |||||
| /// deletes itself after 5 seconds.</para> | |||||
| /// <code language="cs" region="SendMessageAsync" | |||||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" /> | |||||
| /// </example> | /// </example> | ||||
| /// <param name="text">The message to be sent.</param> | /// <param name="text">The message to be sent.</param> | ||||
| /// <param name="isTTS">Determines whether the message should be read aloud by Discord or not.</param> | /// <param name="isTTS">Determines whether the message should be read aloud by Discord or not.</param> | ||||
| @@ -35,18 +32,14 @@ namespace Discord | |||||
| /// Sends a file to this message channel with an optional caption. | /// Sends a file to this message channel with an optional caption. | ||||
| /// </summary> | /// </summary> | ||||
| /// <example> | /// <example> | ||||
| /// The following example uploads a local file called <c>wumpus.txt</c> along with the text | |||||
| /// <c>good discord boi</c> to the channel. | |||||
| /// <code language="cs"> | |||||
| /// await channel.SendFileAsync("wumpus.txt", "good discord boi"); | |||||
| /// </code> | |||||
| /// | |||||
| /// The following example uploads a local image called <c>b1nzy.jpg</c> embedded inside a rich embed to the | |||||
| /// channel. | |||||
| /// <code language="cs"> | |||||
| /// await channel.SendFileAsync("b1nzy.jpg", | |||||
| /// embed: new EmbedBuilder {ImageUrl = "attachment://b1nzy.jpg"}.Build()); | |||||
| /// </code> | |||||
| /// <para>The following example uploads a local file called <c>wumpus.txt</c> along with the text | |||||
| /// <c>good discord boi</c> to the channel.</para> | |||||
| /// <code language="cs" region="SendFileAsync.FilePath" | |||||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" /> | |||||
| /// <para>The following example uploads a local image called <c>b1nzy.jpg</c> embedded inside a rich embed to the | |||||
| /// channel.</para> | |||||
| /// <code language="cs" region="SendFileAsync.FilePath.EmbeddedImage" | |||||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" /> | |||||
| /// </example> | /// </example> | ||||
| /// <remarks> | /// <remarks> | ||||
| /// This method sends a file as if you are uploading an attachment directly from your Discord client. | /// This method sends a file as if you are uploading an attachment directly from your Discord client. | ||||
| @@ -70,12 +63,10 @@ namespace Discord | |||||
| /// Sends a file to this message channel with an optional caption. | /// Sends a file to this message channel with an optional caption. | ||||
| /// </summary> | /// </summary> | ||||
| /// <example> | /// <example> | ||||
| /// The following example uploads a streamed image that will be called <c>b1nzy.jpg</c> embedded inside a | |||||
| /// rich embed to the channel. | |||||
| /// <code language="cs"> | |||||
| /// await channel.SendFileAsync(b1nzyStream, "b1nzy.jpg", | |||||
| /// embed: new EmbedBuilder {ImageUrl = "attachment://b1nzy.jpg"}.Build()); | |||||
| /// </code> | |||||
| /// <para>The following example uploads a streamed image that will be called <c>b1nzy.jpg</c> embedded inside a | |||||
| /// rich embed to the channel.</para> | |||||
| /// <code language="cs" region="SendFileAsync.FileStream.EmbeddedImage" | |||||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" /> | |||||
| /// </example> | /// </example> | ||||
| /// <remarks> | /// <remarks> | ||||
| /// This method sends a file as if you are uploading an attachment directly from your Discord client. | /// This method sends a file as if you are uploading an attachment directly from your Discord client. | ||||
| @@ -130,12 +121,10 @@ namespace Discord | |||||
| /// of flattening. | /// of flattening. | ||||
| /// </remarks> | /// </remarks> | ||||
| /// <example> | /// <example> | ||||
| /// The following example downloads 300 messages and gets messages that belong to the user | |||||
| /// <c>53905483156684800</c>. | |||||
| /// <code lang="cs"> | |||||
| /// var messages = await messageChannel.GetMessagesAsync(300).FlattenAsync(); | |||||
| /// var userMessages = messages.Where(x => x.Author.Id == 53905483156684800); | |||||
| /// </code> | |||||
| /// <para>The following example downloads 300 messages and gets messages that belong to the user | |||||
| /// <c>53905483156684800</c>.</para> | |||||
| /// <code language="cs" region="GetMessagesAsync.FromLimit.Standard" | |||||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" /> | |||||
| /// </example> | /// </example> | ||||
| /// <param name="limit">The numbers of message to be gotten from.</param> | /// <param name="limit">The numbers of message to be gotten from.</param> | ||||
| /// <param name="mode">The <see cref="CacheMode" /> that determines whether the object should be fetched from | /// <param name="mode">The <see cref="CacheMode" /> that determines whether the object should be fetched from | ||||
| @@ -168,10 +157,13 @@ namespace Discord | |||||
| /// of flattening. | /// of flattening. | ||||
| /// </remarks> | /// </remarks> | ||||
| /// <example> | /// <example> | ||||
| /// The following example gets 5 message prior to the message identifier <c>442012544660537354</c>. | |||||
| /// <code lang="cs"> | |||||
| /// var messages = await channel.GetMessagesAsync(442012544660537354, Direction.Before, 5).FlattenAsync(); | |||||
| /// </code> | |||||
| /// <para>The following example gets 5 message prior to the message identifier <c>442012544660537354</c>.</para> | |||||
| /// <code language="cs" region="GetMessagesAsync.FromId.FromMessage" | |||||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" /> | |||||
| /// <para>The following example attempts to retrieve <c>messageCount</c> number of messages from the | |||||
| /// beginning of the channel and prints them to the console.</para> | |||||
| /// <code language="cs" region="GetMessagesAsync.FromId.BeginningMessages" | |||||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" /> | |||||
| /// </example> | /// </example> | ||||
| /// <param name="fromMessageId">The ID of the starting message to get the messages from.</param> | /// <param name="fromMessageId">The ID of the starting message to get the messages from.</param> | ||||
| /// <param name="dir">The direction of the messages to be gotten from.</param> | /// <param name="dir">The direction of the messages to be gotten from.</param> | ||||
| @@ -206,10 +198,9 @@ namespace Discord | |||||
| /// of flattening. | /// of flattening. | ||||
| /// </remarks> | /// </remarks> | ||||
| /// <example> | /// <example> | ||||
| /// The following example gets 5 message prior to a specific message, <c>oldMessage</c>. | |||||
| /// <code lang="cs"> | |||||
| /// var messages = await channel.GetMessagesAsync(oldMessage, Direction.Before, 5).FlattenAsync(); | |||||
| /// </code> | |||||
| /// <para>The following example gets 5 message prior to a specific message, <c>oldMessage</c>.</para> | |||||
| /// <code language="cs" region="GetMessagesAsync.FromMessage" | |||||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" /> | |||||
| /// </example> | /// </example> | ||||
| /// <param name="fromMessage">The starting message to get the messages from.</param> | /// <param name="fromMessage">The starting message to get the messages from.</param> | ||||
| /// <param name="dir">The direction of the messages to be gotten from.</param> | /// <param name="dir">The direction of the messages to be gotten from.</param> | ||||
| @@ -262,13 +253,9 @@ namespace Discord | |||||
| /// object is disposed. | /// object is disposed. | ||||
| /// </summary> | /// </summary> | ||||
| /// <example> | /// <example> | ||||
| /// The following example keeps the client in the typing state until <c>LongRunningAsync</c> has finished. | |||||
| /// <code lang="cs"> | |||||
| /// using (messageChannel.EnterTypingState()) | |||||
| /// { | |||||
| /// await LongRunningAsync(); | |||||
| /// } | |||||
| /// </code> | |||||
| /// <para>The following example keeps the client in the typing state until <c>LongRunningAsync</c> has finished.</para> | |||||
| /// <code language="cs" region="EnterTypingState" | |||||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" /> | |||||
| /// </example> | /// </example> | ||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | /// <returns> | ||||
| @@ -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"> | |||||
| /// var categories = await guild.GetCategoriesAsync(); | |||||
| /// var targetCategory = categories.FirstOrDefault(x => x.Name == "wumpus"); | |||||
| /// if (targetCategory == null) return; | |||||
| /// await Context.Guild.CreateTextChannelAsync(name, x => | |||||
| /// { | |||||
| /// x.CategoryId = targetCategory.Id; | |||||
| /// x.Topic = $"This channel was created at {DateTimeOffset.UtcNow} by {user}."; | |||||
| /// }); | |||||
| /// </code> | |||||
| /// <code language="cs" region="CreateTextChannelAsync" | |||||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Guilds\IGuild.Examples.cs"/> | |||||
| /// </example> | /// </example> | ||||
| /// <param name="name">The new name for the text channel.</param> | /// <param name="name">The new name for the text channel.</param> | ||||
| /// <param name="func">The delegate containing the properties to be applied to the channel upon its creation.</param> | /// <param name="func">The delegate containing the properties to be applied to the channel upon its creation.</param> | ||||
| @@ -23,10 +23,8 @@ namespace Discord | |||||
| /// <example> | /// <example> | ||||
| /// The following example attempts to retrieve the user's current avatar and send it to a channel; if one is | /// The following example attempts to retrieve the user's current avatar and send it to a channel; if one is | ||||
| /// not set, a default avatar for this user will be returned instead. | /// not set, a default avatar for this user will be returned instead. | ||||
| /// <code language="cs"> | |||||
| /// var userAvatarUrl = user.GetAvatarUrl() ?? user.GetDefaultAvatarUrl(); | |||||
| /// await textChannel.SendMessageAsync(userAvatarUrl); | |||||
| /// </code> | |||||
| /// <code language="cs" region="GetAvatarUrl" | |||||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Users\IUser.Examples.cs"/> | |||||
| /// </example> | /// </example> | ||||
| /// <param name="format">The format to return.</param> | /// <param name="format">The format to return.</param> | ||||
| /// <param name="size">The size of the image to return in. This can be any power of two between 16 and 2048. | /// <param name="size">The size of the image to return in. This can be any power of two between 16 and 2048. | ||||
| @@ -84,27 +82,18 @@ namespace Discord | |||||
| /// <remarks> | /// <remarks> | ||||
| /// This method is used to obtain or create a channel used to send a direct message. | /// This method is used to obtain or create a channel used to send a direct message. | ||||
| /// <note type="warning"> | /// <note type="warning"> | ||||
| /// In event that the current user cannot send a message to the target user, a channel can and will still be | |||||
| /// created by Discord. However, attempting to send a message will yield a | |||||
| /// <see cref="Discord.Net.HttpException"/> with a 403 as its | |||||
| /// <see cref="Discord.Net.HttpException.HttpCode"/>. There are currently no official workarounds by | |||||
| /// Discord. | |||||
| /// In event that the current user cannot send a message to the target user, a channel can and will | |||||
| /// still be created by Discord. However, attempting to send a message will yield a | |||||
| /// <see cref="Discord.Net.HttpException"/> with a 403 as its | |||||
| /// <see cref="Discord.Net.HttpException.HttpCode"/>. There are currently no official workarounds by | |||||
| /// Discord. | |||||
| /// </note> | /// </note> | ||||
| /// </remarks> | /// </remarks> | ||||
| /// <example> | /// <example> | ||||
| /// The following example attempts to send a direct message to the target user and logs the incident should | /// The following example attempts to send a direct message to the target user and logs the incident should | ||||
| /// it fail. | /// it fail. | ||||
| /// <code language="cs"> | |||||
| /// var channel = await user.GetOrCreateDMChannelAsync(); | |||||
| /// try | |||||
| /// { | |||||
| /// await channel.SendMessageAsync("Awesome stuff!"); | |||||
| /// } | |||||
| /// catch (Discord.Net.HttpException ex) when (ex.HttpCode == 403) | |||||
| /// { | |||||
| /// Console.WriteLine($"Boo, I cannot message {user}"); | |||||
| /// } | |||||
| /// </code> | |||||
| /// <code region="GetOrCreateDMChannelAsync" language="cs" | |||||
| /// source="../../../Discord.Net.Examples/Core/Entities/Users/IUser.Examples.cs"/> | |||||
| /// </example> | /// </example> | ||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | /// <returns> | ||||
| @@ -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 | |||||
| } | |||||
| } | |||||
| @@ -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 | |||||
| /// guilds (i.e. guild with 100+ members) actively. To download users on startup, consider enabling | |||||
| /// <see cref="DiscordSocketConfig.AlwaysDownloadUsers"/>. | |||||
| /// Sometimes a user may return <c>null</c> due to Discord not sending offline users in large guilds | |||||
| /// (i.e. guild with 100+ members) actively. To download users on startup and to see more information | |||||
| /// about this subject, see <see cref="Discord.WebSocket.DiscordSocketConfig.AlwaysDownloadUsers" />. | |||||
| /// </note> | /// </note> | ||||
| /// <note> | /// <note> | ||||
| /// This method does not attempt to fetch users that the logged-in user does not have access to (i.e. | /// This method does not attempt to fetch users that the logged-in user does not have access to (i.e. | ||||
| /// users who don't share mutual guild(s) with the current user). | |||||
| /// users who don't share mutual guild(s) with the current user). If you wish to get a user that you do | |||||
| /// not have access to, consider using the REST implementation of | |||||
| /// <see cref="DiscordRestClient.GetUserAsync(System.UInt64,Discord.RequestOptions)" />. | |||||
| /// </note> | /// </note> | ||||
| /// </remarks> | /// </remarks> | ||||
| /// <returns> | /// <returns> | ||||
| @@ -114,20 +116,22 @@ namespace Discord.WebSocket | |||||
| /// <summary> | /// <summary> | ||||
| /// Gets a user. | /// Gets a user. | ||||
| /// </summary> | /// </summary> | ||||
| /// <param name="username">The name of the user.</param> | |||||
| /// <param name="discriminator">The discriminator value of the user.</param> | |||||
| /// <remarks> | /// <remarks> | ||||
| /// This method gets the user present in the WebSocket cache with the given condition. | /// This method gets the user present in the WebSocket cache with the given condition. | ||||
| /// <note type="warning"> | /// <note type="warning"> | ||||
| /// Sometimes a user may return <c>null</c> due to Discord not sending offline users in large | |||||
| /// guilds (i.e. guild with 100+ members) actively. To download users on startup, consider enabling | |||||
| /// <see cref="DiscordSocketConfig.AlwaysDownloadUsers"/>. | |||||
| /// Sometimes a user may return <c>null</c> due to Discord not sending offline users in large guilds | |||||
| /// (i.e. guild with 100+ members) actively. To download users on startup and to see more information | |||||
| /// about this subject, see <see cref="Discord.WebSocket.DiscordSocketConfig.AlwaysDownloadUsers" />. | |||||
| /// </note> | /// </note> | ||||
| /// <note> | /// <note> | ||||
| /// This method does not attempt to fetch users that the logged-in user does not have access to (i.e. | /// This method does not attempt to fetch users that the logged-in user does not have access to (i.e. | ||||
| /// users who don't share mutual guild(s) with the current user). | |||||
| /// users who don't share mutual guild(s) with the current user). If you wish to get a user that you do | |||||
| /// not have access to, consider using the REST implementation of | |||||
| /// <see cref="DiscordRestClient.GetUserAsync(System.UInt64,Discord.RequestOptions)" />. | |||||
| /// </note> | /// </note> | ||||
| /// </remarks> | /// </remarks> | ||||
| /// <param name="username">The name of the user.</param> | |||||
| /// <param name="discriminator">The discriminator value of the user.</param> | |||||
| /// <returns> | /// <returns> | ||||
| /// A generic WebSocket-based user; <c>null</c> when the user cannot be found. | /// A generic WebSocket-based user; <c>null</c> when the user cannot be found. | ||||
| /// </returns> | /// </returns> | ||||
| @@ -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> | ||||