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