diff --git a/CHANGELOG.md b/CHANGELOG.md index 21e37b295..dd07045e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,98 @@ # Changelog +## [3.0.0] - 2021-12-13 + +### Added + +- #1767 Add method to clear guild user cache (19a66bf) +- #1847 Bump API version to 9 (06a64b7) +- #1848 Remove obsolete sync voice regions methods and properties (ed8e573) +- #1851 Remove DM cache and fix references (7a201e9) +- #1860 Remove /users/@me call for socket and rework sharded client a bit (384ad85) +- #1863 Change GuildMemberUpdate before state to cacheable (c2e87f5) +- #1879 Add Name property to Teams (c5b4b64) +- #1896 IVoiceChannel implements IMentionable (3395700) +- #1943 Handle bidirectional usernames (10afd96) +- #1948 warn on invalid gateway intents (51e06e9) +- #1949 default application games (82276e3) +- #1923 Added Interaction Support (933ea42). +- #1923 Added Application commands (933ea42). +- #1923 Added Message Components (933ea42). +- #1923 Added Thread Channels (933ea42). +- #1923 Added Stage Channels (933ea42). +- #1923 Added Guild Events (933ea42). +- #1923 Revamped Stickers (933ea42). +- #1923 Added `TimestampTag` (933ea42). +- #1923 Added `Name` property to teams (933ea42). +- #1923 Added url validation to embeds (933ea42). +- #1923 Added `NsfwLevel` to Guilds (933ea42). +- #1923 Added helpers to `Emoji` for parsing (933ea42). +- #1923 Added banner and accent color to guild users (933ea42). +- #1923 Added `RatelimitCallback` to `RequestOptions` (933ea42). +- #1923 Added `Emoji` to roles (933ea42). +- #1923 Added `UseInteractionSnowflakeDate` to config (933ea42). +- #1923 Added checks for gateway intent in some methods. DownloadUsersAsync will throw an exception if you don't have the gateway intent enabled locally now, this will help with the vauge error that was given before (933ea42). +- #1923 Added SendFilesAsync to channels (933ea42). +- #1923 Added Attachments property to MessageProperties (933ea42). +- #1958 Added NET5.0 and NET6.0 builds (aa6bb5e). +- #1958 Added `Discord.Interactions` (aa6bb5e). + +### Fixed + +- #1832 Grab correct Uses value for vanity urls (8ed8714) +- #1849 Remove obsolete methods and properties (70aab6c) +- #1850 Create DM channel with id and author alone (95bae78) +- #1853 Fire GuildMemberUpdated without cached user (d176fef) +- #1854 Gateway events for DMs (a7ff6ce) +- #1858 MessageUpdated without author (8b29e0f) +- #1859 Add missing AddRef and related (de7f9b5) +- #1862 Message update without author (fabe034) +- #1864 ApiClient.CurrentUser being null (08507c0) +- #1871 Add empty role list if not present (f47001a) +- #1872 Connection deadlock when trying to Send and Disconnect (97d90b9) +- #1873 Remove OperationCanceledException handling in connecting logic (7cf8499) +- #1876 Message type (ac52a11) +- #1877 Rest message type (22bb1b0) +- #1886 Change embed description max length to 4096 (8349cd7) +- #1890 Add default avatar to WithAuthor extension (c200861) + +### Misc + +- #20 Update EmbedBuilder.Overwrites.md (76a878a) +- #21 Fix line about PriorityAttribute (75b74e1) +- #1152 Add characters commonly use in links to Sanitize (b9274d1) +- #1518 Add default nullable enum typereader (f7a07ae) +- #1666 Added negative TimeSpan handling (6abdfcb) +- #1852 Internal change to GetOrCreateUser (dfaaa21) +- #1861 Add MaxBitrate to the interface (e0dbe7c) +- #1865 Add null check to AllowedMentions.ToModel() (3cb662f) +- #1923 Merge Labs 3.X into dev (933ea42) +- #1941 Fix emoto try parse (900c1f4) +- #1942 Implement multi-file upload to webhooks (bc440ab) +- #1944 Add Voice binaries (b5c150d) +- #1945 Update socket presence and add new presence event (9d6dc62) +- #1946 fix NRE when adding parameters thru builders (143ca6d) +- #1947 fix sharded client current user (d5f5ae1) +- #1950 Add custom setter to Group property of ModuleBuilder to automatically invoke AddAliases (ba656e9) +- #1958 Update from Discord .Net Labs 3.4.8 (aa6bb5e) +- #1959 Update isRequired (98b03be) +- Add `MatchResult` (d1b31c8) +- Add requested changes (a92ec56) +- Fix incorrect casing on `HandleCommandPipeline` (adf3a9c) +- Merge branch 'commands/validate-get-best-match' of https://github.com/siscodeorg/Discord.Net into siscodeorg-commands/validate-get-best-match (3cd9f39) +- Merge branch 'siscodeorg-commands/validate-get-best-match' into dev (4f1fe2b) +- Remove docs build from azure pipelines (2336b98) +- use async main (125f6c7) +- #1923 Made `Hierarchy` a `IGuildUser` property. +- #1923 Changed embed discription length to 4096 (933ea42). +- #1923 Fixed gateway serialization to include nulls for API v9 (933ea42). +- #1923 Removed error log for gateway reconnects (933ea42). +- #1966 Updated docs. + ## [2.4.0] - 2021-05-22 + ### Added + - #1726 Add stickers (91a9063) - #1753 Webhook message edit & delete functionality (f67cd8e) - #1757 Add ability to add/remove roles by id (4c9910c) @@ -12,15 +103,19 @@ - #1844 Add Discord Certified Moderator user flag (4b8d444) ### Fixed + - #1486 Add type reader when entity type reader exists (c46daaa) - #1835 Cached message emoji cleanup at MESSAGE_REACTION_REMOVE_EMOJI (8afef82) ### Misc + - #1778 Remove URI check from EmbedBuilder (25b04c4) - #1800 Fix spelling in SnowflakeUtils.FromSnowflake (6aff419) ## [2.3.1] - 2021-03-10 + ### Fixed + - #1761 Deadlock in DiscordShardedClient when Ready is never received (73e5cc2) - #1773 Private methods aren't added as commands (0fc713a) - #1780 NullReferenceException in pin/unpin audit logs (f794163) @@ -29,10 +124,13 @@ - #1794 Audit log UserId can be null (d41aeee) ### Misc + - #1774 Add remark regarding CustomStatus as the activity (51b7afe) ## [2.3.0] - 2021-01-28 + ### Added + - #1491 Add INVITE_CREATE and INVITE_DELETE events (1ab670b) - #1520 Support reading multiple activities (421a0c1) - #1521 Allow for inherited commands in modules (a51cdf6) @@ -57,6 +155,7 @@ - #1690 Add max bitrate value to SocketGuild (aacfea0) ### Fixed + - #1244 Missing AddReactions permission for DM channels. (e40ca4a) - #1469 unsupported property causes an exception (468f826) - #1525 AllowedMentions and AllowedMentionTypes (3325031) @@ -99,6 +198,7 @@ - Missing MessageReference when sending files (2095701) ### Misc + - #1545 MutualGuilds optimization (323a677) - #1551 Update webhook regex to support discord.com (7585789) - #1556 Add SearchUsersAsync (57880de) @@ -123,7 +223,9 @@ - Move bulk deletes remarks from to (62539f0) ## [2.2.0] - 2020-04-16 + ### Added + - #1247 Implement Client Status Support (9da11b4) - #1310 id overload for RemoveReactionAsync (c88b1da) - #1319 BOOST (faf23de) @@ -146,6 +248,7 @@ - suppress messages (cd28892) ### Fixed + - #1318 #1314 Don't parse tags within code blocks (c977f2e) - #1333 Remove null coalescing on ToEmbedBuilder Color (120c0f7) - #1337 Fixed attempting to access a non-present optional value (4edda5b) @@ -161,11 +264,13 @@ - include MessageFlags and SuppressEmbedParams (d6d4429) ### Changed + - #1368 Update ISystemMessage interface to allow reactions (07f4d5f) - #1417 fix #1415 Re-add support for overwrite permissions for news channels (e627f07) - use millisecond precision by default (bcb3534) ### Misc + - #1290 Split Unit and Integration tests into separate projects (a797be9) - #1328 Fix #1327 Color.ToString returns wrong value (1e8aa08) - #1329 Fix invalid cref values in docs (363d1c6) @@ -190,14 +295,16 @@ - 2.2.0 (4b602b4) - target the Process env-var scope (3c6b376) - fix metapackage build (1794f95) -- copy only _site to docs-static (a8cdadc) +- copy only \_site to docs-static (a8cdadc) - do not exit on failed robocopy (fd204ee) - add idn debugger (91aec9f) - rename IsStream to IsStreaming (dcd9cdd) - feature (40844b9) ## [2.1.1] - 2019-06-08 + ### Fixed + - #994: Remainder parameters now ignore character escaping, as there is no reason to escape characters here (2e95c49) - #1316: `Emote.Equals` now pays no respect to the Name property, since Discord's API does not care about an emote's name (abf3e90) - #1317: `Emote.GetHashCode` now pays no respect to the Name property, see above (1b54883) @@ -206,16 +313,20 @@ - News embeds will be processed as `EmbedType.Unknown`, rather than throwing an error and dropping the message (d287ed1) ### Changed + - #1311: Members may now be disconnected from voice channels by passing `null` as `GuildUserProperties.Channel` (fc48c66) - #1313: `IMessage.Tags` now includes the EveryoneRole on @everyone and @here mentions (1f55f01) - #1320: The maximum value for setting slow-mode has been updated to 6 hours, per the new API limit (4433ca7) ### Misc + - This library's compatibility with Semantic Versioning has been clarified. Please see the README (4d7de17) - The depency on System.Interactive.Async has been bumped to `3.2.0` (3e65e03) ## [2.1.0] - 2019-05-18 + ### Added + - #1236: Bulk deletes (for messages) may now be accessed via the `MessagesBulkDeleted` event (dec353e) - #1240: OAuth applications utilizing the `guilds.join` scope may now add users to guilds through any client (1356ea9) - #1255: Message and attachment spoilers may now be set or detected (f3b20b2) @@ -227,9 +338,11 @@ - `ExclusiveBulkDelete` configuration setting can be used to control bulk delete event behavior (03e6401) ### Removed + - #1294: The `IGuildUser` overload of `EmbedBuilder.WithAuthor` no longer exists (b52b54d) ### Fixed + - #1256: Fetching audit logs no longer raises null reference exceptions when a webhook has been deleted (049b014) - #1268: Null reference exceptions on `MESSAGE_CREATE` concerning partial member objects no longer occur (377622b) - #1278: The token validator now internally pads tokens to the proper length (48b327b) @@ -238,9 +351,11 @@ - Exceptions in event handlers are now always logged (f6e3200) ### Changed + - #1305: Token validation will fail when tokens contain whitespace (bb61efa) ### Misc + - #1241: Added documentation samples for Webhooks (655a006) - #1243: Happy new year 🎉 (0275f7d) - #1257: Improved clarity in comments in the command samples (2473619) @@ -251,16 +366,20 @@ - IDisposableAnalyzers should now be a development dependency (8003ac8) ## [2.0.1] - 2019-01-04 + ### Fixed + - #1226: Only escape the closing quotation mark of non-remainder strings (65b8c09) - Commands with async RunModes will now propagate exceptions up to CommandExecuted (497918e) ### Misc + - #1225: Commands sample no longer hooks the log event twice (552f34c) - #1227: The logo on the docs index page should scale responsively (d39bf6e) - #1230: Replaced precondition sample on docs (feed4fd) ## [2.0.0] - 2018-12-28 + ### Added - #747: `CommandService` now has a `CommandExecuted` event (e991715) @@ -316,6 +435,7 @@ - Reactions can now be added to messages in bulk (5421df1) ### Fixed + - #742: `DiscordShardedClient#GetGuildFor` will now direct null guilds to Shard 0 (d5e9d6f) - #743: Various issues with permissions and inheritance of permissions (f996338) - #755: `IRole.Mention` will correctly tag the @everyone role (6b5a6e7) @@ -363,6 +483,7 @@ - The default WebSocket will now close correctly (ac389f5) ### Changed + - #731: `IUserMessage#GetReactionUsersAsync` now takes an `IEmote` instead of a `string` (5d7f2fc) - #744: IAsyncEnumerable has been redesigned (5bbd9bb) - #777: `IGuild#DefaultChannel` will now resolve the first accessible channel, per changes to Discord (1ffcd4b) @@ -397,14 +518,15 @@ - The socket client will now use additional fields to fill in member/guild information on messages (8fb2c71) - The Audio Client now uses Voice WS v3 (9ba38d7) - ### Removed + - #790: Redundant overloads for `AddField` removed from EmbedBuilder (479361b) - #925: RPC is no longer being maintained nor packaged (b30af57) - #958: Remove support for user tokens (2fd4f56) - User logins (including selfbots) are no longer supported (fc5adca) ### Misc + - #786: Unit tests for the Color structure (22b969c) - #828: We now include a contributing guide (cd82a0f) - #876: We now include a standard editorconfig (5c8c784) @@ -425,11 +547,13 @@ - Fixed documentation layout for the logo (bafdce4) ## [1.0.2] - 2017-09-09 + ### Fixed - Guilds utilizing Channel Categories will no longer crash bots on the `READY` event. ## [1.0.1] - 2017-07-05 + ### Fixed - #732: Fixed parameter preconditions not being loaded from class-based modules (b6dcc9e) @@ -438,4 +562,5 @@ - Fixed module auto-detection for nested modules (d2afb06) ### Changed + - ShardedCommandContext now inherits from SocketCommandContext (8cd99be) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 752b40931..840650cf8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,7 +17,7 @@ any member of the community. We prefer pull-requests that are descriptive of the changes being made and highlight any potential benefits/drawbacks of the change, but these -types of write-ups are not required. See this [merge request](https://github.com/RogueException/Discord.Net/pull/793) +types of write-ups are not required. See this [merge request](https://github.com/discord-net/Discord.Net/pull/793) for an example of a well-written description. ## Semantic Versioning @@ -28,7 +28,7 @@ that are SemVer compliant with the latest version of the library in development. The working release should be the latest build off of the `dev` branch, -but can also be found on the [development board](https://github.com/RogueException/Discord.Net/projects/1). +but can also be found on the [development board](https://github.com/discord-net/Discord.Net/projects/1). We follow the .NET Foundation's [Breaking Change Rules](https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/breaking-change-rules.md) when determining the SemVer compliance of a change. @@ -59,4 +59,4 @@ The length of the documentation should also follow the ruler as suggested by our #### Recommended Reads * [Official Microsoft Documentation](https://docs.microsoft.com) -* [Sandcastle User Manual](https://ewsoftware.github.io/XMLCommentsGuide/html/4268757F-CE8D-4E6D-8502-4F7F2E22DDA3.htm) \ No newline at end of file +* [Sandcastle User Manual](https://ewsoftware.github.io/XMLCommentsGuide/html/4268757F-CE8D-4E6D-8502-4F7F2E22DDA3.htm) diff --git a/README.md b/README.md index a10a6cf51..69a24a157 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,10 @@ The individual components may also be installed from NuGet: Nightly builds are available through our MyGet feed (`https://www.myget.org/F/discord-net/api/v3/index.json`). +### Unstable (Labs) + +Labs builds are avaiable on nuget (`https://www.nuget.org/packages/Discord.Net.Labs/`) and myget (`https://www.myget.org/F/discord-net-labs/api/v3/index.json`). + ## Compiling In order to compile Discord.Net, you require the following: diff --git a/docs/_overwrites/Commands/ICommandContext.Inclusion.md b/docs/_overwrites/Commands/ICommandContext.Inclusion.md index 4c1257b23..a5eaeeab6 100644 --- a/docs/_overwrites/Commands/ICommandContext.Inclusion.md +++ b/docs/_overwrites/Commands/ICommandContext.Inclusion.md @@ -1,5 +1,5 @@ An example of how this class is used the command system can be seen below: -[!code[Sample module](../../guides/commands/samples/intro/empty-module.cs)] -[!code[Command handler](../../guides/commands/samples/intro/command_handler.cs)] \ No newline at end of file +[!code[Sample module](../../guides/text_commands/samples/intro/empty-module.cs)] +[!code[Command handler](../../guides/text_commands/samples/intro/command_handler.cs)] diff --git a/docs/_template/description-generator/plugins/DocFX.Plugin.DescriptionGenerator.dll b/docs/_template/description-generator/plugins/DocFX.Plugin.DescriptionGenerator.dll index 095ea46ef..6e8839796 100644 Binary files a/docs/_template/description-generator/plugins/DocFX.Plugin.DescriptionGenerator.dll and b/docs/_template/description-generator/plugins/DocFX.Plugin.DescriptionGenerator.dll differ diff --git a/docs/_template/description-generator/plugins/LICENSE b/docs/_template/description-generator/plugins/LICENSE index eb92c0a03..d74703f3d 100644 --- a/docs/_template/description-generator/plugins/LICENSE +++ b/docs/_template/description-generator/plugins/LICENSE @@ -19,3 +19,11 @@ 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. + +============================================================================== + +Humanizer (https://github.com/Humanizr/Humanizer) +The MIT License (MIT) +Copyright (c) .NET Foundation and Contributors + +============================================================================== \ 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 ccb2b0e37..be36969c9 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 316515072..5b3568103 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 21a2b5601..c93e4cc22 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/linux-arm/libgit2-6777db8.so b/docs/_template/last-modified/plugins/lib/linux-arm/libgit2-6777db8.so new file mode 100644 index 000000000..4324c2ad9 Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/linux-arm/libgit2-6777db8.so differ diff --git a/docs/_template/last-modified/plugins/lib/linux-arm64/libgit2-6777db8.so b/docs/_template/last-modified/plugins/lib/linux-arm64/libgit2-6777db8.so new file mode 100644 index 000000000..a72d7419d Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/linux-arm64/libgit2-6777db8.so differ diff --git a/docs/_template/last-modified/plugins/lib/linux-musl-x64/libgit2-6777db8.so b/docs/_template/last-modified/plugins/lib/linux-musl-x64/libgit2-6777db8.so new file mode 100644 index 000000000..a1f59dfb3 Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/linux-musl-x64/libgit2-6777db8.so differ diff --git a/docs/_template/last-modified/plugins/lib/linux-x64/libgit2-6777db8.so b/docs/_template/last-modified/plugins/lib/linux-x64/libgit2-6777db8.so new file mode 100644 index 000000000..b47f83e21 Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/linux-x64/libgit2-6777db8.so differ diff --git a/docs/_template/last-modified/plugins/lib/osx/libgit2-6777db8.dylib b/docs/_template/last-modified/plugins/lib/osx/libgit2-6777db8.dylib new file mode 100644 index 000000000..cb1e7eb8c Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/osx/libgit2-6777db8.dylib differ diff --git a/docs/_template/last-modified/plugins/lib/win32/x64/git2-6777db8.dll b/docs/_template/last-modified/plugins/lib/win32/x64/git2-6777db8.dll new file mode 100644 index 000000000..af7d32a6d Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/win32/x64/git2-6777db8.dll differ diff --git a/docs/_template/last-modified/plugins/lib/win32/x86/git2-6777db8.dll b/docs/_template/last-modified/plugins/lib/win32/x86/git2-6777db8.dll new file mode 100644 index 000000000..c680911e2 Binary files /dev/null and b/docs/_template/last-modified/plugins/lib/win32/x86/git2-6777db8.dll differ diff --git a/docs/_template/light-dark-theme/partials/head.tmpl.partial b/docs/_template/light-dark-theme/partials/head.tmpl.partial index c214e7548..8a01b48cf 100644 --- a/docs/_template/light-dark-theme/partials/head.tmpl.partial +++ b/docs/_template/light-dark-theme/partials/head.tmpl.partial @@ -10,7 +10,7 @@ - + diff --git a/docs/docfx.json b/docs/docfx.json index 759dc174f..f691d3a8f 100644 --- a/docs/docfx.json +++ b/docs/docfx.json @@ -1,19 +1,22 @@ { - "metadata": [{ - "src": [{ - "src": "../src", - "files": [ - "**.csproj" - ] - }], - "dest": "api", - "filter": "filterConfig.yml", - "properties": { - "TargetFramework": "netstandard2.0" + "metadata": [ + { + "src": [ + { + "src": "../src", + "files": ["**.csproj"] + } + ], + "dest": "api", + "filter": "filterConfig.yml", + "properties": { + "TargetFramework": "net5.0" + } } - }], + ], "build": { - "content": [{ + "content": [ + { "files": ["api/**.yml", "api/index.md"] }, { @@ -27,19 +30,21 @@ }, { "src": "../", - "files": [ "CHANGELOG.md" ] + "files": ["CHANGELOG.md"] + } + ], + "resource": [ + { + "files": [ + "**/images/**", + "**/samples/**", + "langwordMapping.yml", + "marketing/logo/**.svg", + "marketing/logo/**.png", + "favicon.ico" + ] } ], - "resource": [{ - "files": [ - "**/images/**", - "**/samples/**", - "langwordMapping.yml", - "marketing/logo/**.svg", - "marketing/logo/**.png", - "favicon.ico" - ] - }], "dest": "_site", "template": [ "default", @@ -47,17 +52,19 @@ "_template/last-modified", "_template/description-generator" ], - "postProcessors": ["ExtractSearchIndex", "LastModifiedPostProcessor", "DescriptionPostProcessor"], + "postProcessors": [ + "ExtractSearchIndex", + "LastModifiedPostProcessor", + "DescriptionPostProcessor" + ], "overwrite": "_overwrites/**/**.md", "globalMetadata": { "_appTitle": "Discord.Net Documentation", - "_appFooter": "Discord.Net (c) 2015-2020 2.2.0", + "_appFooter": "Discord.Net (c) 2015-2021 3.0.0", "_enableSearch": true, "_appLogoPath": "marketing/logo/SVG/Logomark Purple.svg", - "_appFaviconPath": "favicon.ico" + "_appFaviconPath": "favicon.ico" }, - "xrefService": [ - "https://xref.docs.microsoft.com/query?uid={uid}" - ] + "xrefService": ["https://xref.docs.microsoft.com/query?uid={uid}"] } } diff --git a/docs/faq/basics/basic-operations.md b/docs/faq/basics/basic-operations.md index 35c71709f..ee2edef94 100644 --- a/docs/faq/basics/basic-operations.md +++ b/docs/faq/basics/basic-operations.md @@ -13,7 +13,7 @@ language-specific tips when using this library. > [!WARNING] > Direct casting (e.g., `(Type)type`) is **the least recommended** -> way of casting, as it *can* throw an [InvalidCastException] +> way of casting, as it _can_ throw an [InvalidCastException] > when the object isn't the desired type. > > Please refer to [this post] for more details. @@ -26,7 +26,7 @@ A good and safe casting example: [!code-csharp[Casting](samples/cast.cs)] -[InvalidCastException]: https://docs.microsoft.com/en-us/dotnet/api/system.invalidcastexception +[invalidcastexception]: https://docs.microsoft.com/en-us/dotnet/api/system.invalidcastexception [this post]: https://docs.microsoft.com/en-us/dotnet/csharp/how-to/safely-cast-using-pattern-matching-is-and-as-operators ## How do I send a message? @@ -45,15 +45,15 @@ means casting is your friend. You should attempt to cast the channel as an [IMessageChannel] or any other entity that implements it to be able to message. -[SendMessageAsync]: xref:Discord.IMessageChannel.SendMessageAsync* -[GetChannel]: xref:Discord.WebSocket.DiscordSocketClient.GetChannel* +[sendmessageasync]: xref:Discord.IMessageChannel.SendMessageAsync* +[getchannel]: xref:Discord.WebSocket.DiscordSocketClient.GetChannel* ## How can I tell if a message is from X, Y, Z channel? You may check the message channel type. Visit [Glossary] to see the various types of channels. -[Glossary]: xref:FAQ.Glossary#message-channels +[glossary]: xref:FAQ.Glossary#message-channels ## How can I get the guild from a message? @@ -86,9 +86,9 @@ implement [IEmote] and are valid options. [!code-csharp[Emoji](samples/emoji-self.cs)] -*** +--- -[AddReactionAsync]: xref:Discord.IMessage.AddReactionAsync* +[addreactionasync]: xref:Discord.IMessage.AddReactionAsync* ## What is a "preemptive rate limit?" @@ -107,17 +107,17 @@ reactions. ## Can I opt-out of preemptive rate limits? -Unfortunately, not at the moment. See [#401](https://github.com/RogueException/Discord.Net/issues/401). - -[IChannel]: xref:Discord.IChannel -[ICategoryChannel]: xref:Discord.ICategoryChannel -[IGuildChannel]: xref:Discord.IGuildChannel -[ITextChannel]: xref:Discord.ITextChannel -[IGuild]: xref:Discord.IGuild -[IVoiceChannel]: xref:Discord.IVoiceChannel -[IGuildUser]: xref:Discord.IGuildUser -[IMessageChannel]: xref:Discord.IMessageChannel -[IUserMessage]: xref:Discord.IUserMessage -[IEmote]: xref:Discord.IEmote -[Emote]: xref:Discord.Emote -[Emoji]: xref:Discord.Emoji \ No newline at end of file +Unfortunately, not at the moment. See [#401](https://github.com/discord-net/Discord.Net/issues/401). + +[ichannel]: xref:Discord.IChannel +[icategorychannel]: xref:Discord.ICategoryChannel +[iguildchannel]: xref:Discord.IGuildChannel +[itextchannel]: xref:Discord.ITextChannel +[iguild]: xref:Discord.IGuild +[ivoicechannel]: xref:Discord.IVoiceChannel +[iguilduser]: xref:Discord.IGuildUser +[imessagechannel]: xref:Discord.IMessageChannel +[iusermessage]: xref:Discord.IUserMessage +[iemote]: xref:Discord.IEmote +[emote]: xref:Discord.Emote +[emoji]: xref:Discord.Emoji diff --git a/docs/faq/basics/client-basics.md b/docs/faq/basics/client-basics.md index 1176ee3fd..72ad472f2 100644 --- a/docs/faq/basics/client-basics.md +++ b/docs/faq/basics/client-basics.md @@ -28,8 +28,8 @@ There are few possible reasons why this may occur. mind that a token is **different** from a *client secret*. [TokenType]: xref:Discord.TokenType -[827]: https://github.com/RogueException/Discord.Net/issues/827 -[958]: https://github.com/RogueException/Discord.Net/issues/958 +[827]: https://github.com/discord-net/Discord.Net/issues/827 +[958]: https://github.com/discord-net/Discord.Net/issues/958 [Discord API Terms of Service]: https://discord.com/developers/docs/legal ## How do I do X, Y, Z when my bot connects/logs on? Why do I get a `NullReferenceException` upon calling any client methods after connect? @@ -73,7 +73,9 @@ instances, with each one serving a different amount of guilds. There are very few differences from the [DiscordSocketClient] class, and it is very straightforward to modify your existing code to use a [DiscordShardedClient] when necessary. -1. You need to specify the total amount of shards, or shard ids, via [DiscordShardedClient]'s constructors. +1. You can specify the total amount of shards, or shard ids, via [DiscordShardedClient]'s constructors. +If the total shards are not specified then the library will get the recommended shard count via the +[Get Gateway Bot](https://discord.com/developers/docs/topics/gateway#get-gateway-bot) route. 2. The [Connected], [Disconnected], [Ready], and [LatencyUpdated] events are replaced with [ShardConnected], [ShardDisconnected], [ShardReady], and [ShardLatencyUpdated]. 3. Every event handler you apply/remove to the [DiscordShardedClient] is applied/removed to each shard. diff --git a/docs/faq/basics/getting-started.md b/docs/faq/basics/getting-started.md index e254226d0..dc4b11548 100644 --- a/docs/faq/basics/getting-started.md +++ b/docs/faq/basics/getting-started.md @@ -79,4 +79,4 @@ Several common ways to do this: ![Roles](images/role-copy.png) 2. Make the role mentionable and mention the role, and escape it using the `\` character in front. -3. Inspect the roles collection within the guild via your debugger. \ No newline at end of file +3. Inspect the roles collection within the guild via your debugger. diff --git a/docs/faq/basics/images/scope.png b/docs/faq/basics/images/scope.png new file mode 100644 index 000000000..04d60bce2 Binary files /dev/null and b/docs/faq/basics/images/scope.png differ diff --git a/docs/faq/basics/interactions.md b/docs/faq/basics/interactions.md new file mode 100644 index 000000000..5a4f41097 --- /dev/null +++ b/docs/faq/basics/interactions.md @@ -0,0 +1,84 @@ +--- +uid: FAQ.Basics.InteractionBasics +title: Basics of interactions, common practice +--- + +# Interactions basics, where to get started + +This section answers basic questions and common mistakes in handling application commands, and responding to them. + +## What's the difference between RespondAsync, DeferAsync and FollowupAsync? + +The difference between these 3 functions is in how you handle the command response. +[RespondAsync] and +[DeferAsync] let the API know you have succesfully received the command. This is also called 'acknowledging' a command. +DeferAsync will not send out a response, RespondAsync will. +[FollowupAsync] follows up on succesful acknowledgement. + +> [!WARNING] +> If you have not acknowledged the command FollowupAsync will not work! the interaction has not been resonded to, so you cannot follow it up! + +[RespondAsync]: xref:Discord.IDiscordInteraction +[DeferAsync]: xref:Discord.IDiscordInteraction +[FollowUpAsync]: xref:Discord.IDiscordInteraction + +## Bad form Exception when I try to create my commands, why do I get this? + +Bad form exceptions are thrown if the slash, user or message command builder has invalid values. +The following options could resolve your error. + +#### Is your command name lowercase? + +If your command name is not lowercase, it is not seen as a valid command entry. +`Avatar` is invalid; `avatar` is valid. + +#### Are your values below or above the required amount? (This also applies to message components) + +Discord expects all values to be below maximum allowed. +Going over this maximum amount of characters causes an exception. + +> [!NOTE] +> All maximum and minimum value requirements can be found in the [Discord Developer Docs]. +> For components, structure documentation is found [here]. + +[Discord Developer Docs]: https://discord.com/developers/docs/interactions/application-commands#application-commands +[here]: https://discord.com/developers/docs/interactions/message-components#message-components + +#### Is your subcommand branching correct? + +Branching structure is covered properly here: xref:Guides.SlashCommands.SubCommand + +## My interaction commands are not showing up? + +If you registered your commands globally, it can take up to 1 hour for them to register. +Did you register a guild command (should be instant), or waited more than an hour and still don't have them show up? + +- Try to check for any errors in the console, there is a good chance something might have been thrown. + +- Register your commands after the Ready event in the client. The client is not configured to register commands before this moment. + +- Check if no bad form exception is thrown; If so, refer to the above question. + +- Do you have the application commands scope checked when adding your bot to guilds? + +![Scope check](images/scope.png) + +## There are many options for creating commands, which do I use? + +[!code-csharp[Register examples](samples/registerint.cs)] + +> [!NOTE] +> You can use bulkoverwrite even if there are no commands in guild, nor globally. +> The bulkoverwrite method disposes the old set of commands and replaces it with the new. + +## Do I need to create commands on startup? + +If you are registering your commands for the first time, it is required to create them once. +After this, commands will exist indefinitely until you overwrite them. +Overwriting is only required if you make changes to existing commands, or add new ones. + +## I can't see all of my user/message commands, why? + +Message and user commands have a limit of 5 per guild, and another 5 globally. +If you have more than 5 guild-only message commands being registered, no more than 5 will actually show up. +You can get up to 10 entries to show if you register 5 per guild, and another 5 globally. diff --git a/docs/faq/basics/samples/registerint.cs b/docs/faq/basics/samples/registerint.cs new file mode 100644 index 000000000..68fd2e608 --- /dev/null +++ b/docs/faq/basics/samples/registerint.cs @@ -0,0 +1,21 @@ +private async Task ReadyAsync() +{ + // pull your commands from some array, everyone has a different approach for this. + var commands = _builders.ToArray(); + + // write your list of commands globally in one go. + await _client.Rest.BulkOverwriteGlobalCommands(commands); + + // write your array of commands to one guild in one go. + // You can do a foreach (... in _client.Guilds) approach to write to all guilds. + await _client.Rest.BulkOverwriteGuildCommands(commands, /* some guild ID */); + + foreach (var c in commands) + { + // Create a global command, repeating usage for multiple commands. + await _client.Rest.CreateGlobalCommand(c); + + // Create a guild command, repeating usage for multiple commands. + await _client.Rest.CreateGuildCommand(c, guildId); + } +} diff --git a/docs/faq/commands/dependency-injection.md b/docs/faq/commands/dependency-injection.md index 0a5de3e32..d6b7f8b58 100644 --- a/docs/faq/commands/dependency-injection.md +++ b/docs/faq/commands/dependency-injection.md @@ -23,7 +23,7 @@ throughout execution. Think of it like a chest that holds whatever you throw at it that won't be affected by anything unless you want it to. Note that you should also learn Microsoft's implementation of [Dependency Injection] \([video]) before proceeding, -as well as how it works in [Discord.Net](xref:Guides.Commands.DI#usage-in-modules). +as well as how it works in [Discord.Net](xref:Guides.TextCommands.DI#usage-in-modules). A brief example of service and dependency injection can be seen below. diff --git a/docs/faq/commands/general.md b/docs/faq/commands/general.md index de6d48dc1..cff078746 100644 --- a/docs/faq/commands/general.md +++ b/docs/faq/commands/general.md @@ -1,9 +1,9 @@ --- uid: FAQ.Commands.General -title: General Questions about Commands +title: General Questions about chat Commands --- -# Command-related Questions +# Chat Command-related Questions In the following section, you will find commonly asked questions and answered regarding general command usage when using @Discord.Commands. @@ -144,4 +144,4 @@ For #4, exceptions are caught in [CommandService.Log] event under [LogMessage.Exception]: xref:Discord.LogMessage.Exception* [ExecuteResult.Exception]: xref:Discord.Commands.ExecuteResult.Exception* [CommandException]: xref:Discord.Commands.CommandException -[IResult]: xref:Discord.Commands.IResult \ No newline at end of file +[IResult]: xref:Discord.Commands.IResult diff --git a/docs/faq/commands/interaction.md b/docs/faq/commands/interaction.md new file mode 100644 index 000000000..db61bc3f5 --- /dev/null +++ b/docs/faq/commands/interaction.md @@ -0,0 +1,52 @@ +--- +uid: FAQ.Commands.Interactions +title: Interaction service +--- + +# Interaction commands in services + +A chapter talking about the interaction service framework. +For questions about interactions in general, refer to the [Interactions FAQ] + +## Module dependencies aren't getting populated by Property Injection? + +Make sure the properties are publicly accessible and publicly settable. + +## How do I use this * interaction specific method/property? + +If your interaction context holds a down-casted version of the interaction object, you need to up-cast it. +Ideally, use pattern matching to make sure its the type of interaction you are expecting it to be. + +## `InteractionService.ExecuteAsync()` always returns a successful result, how do i access the failed command execution results? + +If you are using `RunMode.Async` you need to setup your post-execution pipeline around `CommandExecuted` events. + +## How do I check if the executing user has * permission? + +Refer to the [documentation about preconditions] + +## How do I send the HTTP Response from inside the command modules. + +Set the `RestResponseCallback` property of [InteractionServiceConfig] with a delegate for handling HTTP Responses and use +`RestInteractionModuleBase` to create your command modules. `RespondAsync()` and `DeferAsync()` methods of this module base will use the +`RestResponseCallback` to create interaction responses. + +## Is there a cleaner way of creating parameter choices other than using `[Choice]`? + +The default `enum` [TypeConverter] of the Interaction Service will +automatically register `enum`s as multiple choice options. + +## How do I add an optional `enum` parameter but make the default value not visible to the user? + +The default `enum` [TypeConverter] of the Interaction Service comes with `[Hide]` attribute that +can be used to prevent certain enum values from getting registered. + +## How does the InteractionService determine the generic TypeConverter to use for a parameter type? + +It compares the _target base type_ key of the +[TypeConverter] and chooses the one that sits highest on the inheritance hierarchy. + +[TypeConverter]: xref:Discord.Interactions.TypeConverter +[Interactions FAQ]: xref: FAQ.Basics.Interactions +[InteractionServiceConfig]: xref:Discord.Interactions.InteractionServiceConfig +[documentation about preconditions]: xref: Guides.ChatCommands.Preconditions diff --git a/docs/faq/misc/glossary.md b/docs/faq/misc/glossary.md index 4b661f65c..232690917 100644 --- a/docs/faq/misc/glossary.md +++ b/docs/faq/misc/glossary.md @@ -26,11 +26,11 @@ channels, and are often referred to as "servers". ## Channel Types ### Message Channels -* A **Text Channel** ([ITextChannel]) is a message channel from a -Guild. +* A **Text Channel** ([ITextChannel]) is a message channel from a Guild. +* A **Thread Channel** ([IThreadChannel]) is a thread channel from a Guild. +* A **News Channel** ([INewsChannel]) (also goes as announcement channel) is a news channel from a Guild. * A **DM Channel** ([IDMChannel]) is a message channel from a DM. -* A **Group Channel** ([IGroupChannel]) is a message channel from a -Group. +* A **Group Channel** ([IGroupChannel]) is a message channel from a Group. - This is rarely used due to the bot's inability to join groups. * A **Private Channel** ([IPrivateChannel]) is a DM or a Group. * A **Message Channel** ([IMessageChannel]) can be any of the above. @@ -39,11 +39,15 @@ Group. * A **Guild Channel** ([IGuildChannel]) is a guild channel in a guild. - This can be any channels that may exist in a guild. * A **Voice Channel** ([IVoiceChannel]) is a voice channel in a guild. +* A **Stage Channel** ([IStageChannel]) is a stage channel in a guild. * A **Category Channel** ([ICategoryChannel]) (2.0+) is a category that holds one or more sub-channels. * A **Nested Channel** ([INestedChannel]) (2.0+) is a channel that can exist under a category. +> [!NOTE] +> A Channel ([IChannel]) can be all types of channels. + [INestedChannel]: xref:Discord.INestedChannel [IGuildChannel]: xref:Discord.IGuildChannel [IMessageChannel]: xref:Discord.IMessageChannel @@ -53,6 +57,33 @@ exist under a category. [IPrivateChannel]: xref:Discord.IPrivateChannel [IVoiceChannel]: xref:Discord.IVoiceChannel [ICategoryChannel]: xref:Discord.ICategoryChannel +[IChannel]: xref:Discord.IChannel +[IThreadChannel]: xref:Discord.IThreadChannel +[IStageChannel]: xref:Discord.IStageChannel +[INewsChannel]: xref:Discord.INewsChannel + +## Message Types + +* An **User Message** ([IUserMessage]) is a message sent by a user. +* A **System Message** ([ISystemMessage]) is a message sent by Discord itself. +* A **Message** ([IMessage]) can be any of the above. + +[IUserMessage]: xref:Discord.IUserMessage +[ISystemMessage]: xref:Discord.ISystemMessage +[IMessage]: xref:Discord.IMessage + +## User Types + +* A **Guild User** ([IGuildUser]) is a user available inside a guild. +* A **Group User** ([IGroupUser]) is a user available inside a group. + - This is rarely used due to the bot's inability to join groups. +* A **Self User** ([ISelfUser]) is the bot user the client is currently logged in as. +* An **User** ([IUser]) can be any of the above. + +[IGuildUser]: xref:Discord.IGuildUser +[IGroupUser]: xref:Discord.IGroupUser +[ISelfUser]: xref:Discord.ISelfUser +[IUser]: xref:Discord.IUser ## Emoji Types @@ -64,6 +95,15 @@ exist under a category. [Emote]: xref:Discord.Emote [Emoji]: xref:Discord.Emoji + +## Sticker Types + +* A **Sticker** ([ISticker]) is a standard Discord sticker. +* A **Custom Sticker ([ICustomSticker]) is a Guild-unique sticker. + +[ISticker]: xref:Discord.ISticker +[ICustomSticker]: xref:Discord.ICustomSticker + ## Activity Types * A **Game** ([Game]) refers to a user's game activity. diff --git a/docs/faq/misc/legacy.md b/docs/faq/misc/legacy.md index 5931579d3..ddb2d03c6 100644 --- a/docs/faq/misc/legacy.md +++ b/docs/faq/misc/legacy.md @@ -18,7 +18,7 @@ their breaking nature. Visit the repo's [release tag] to see the latest public pre-release. -[release tag]: https://github.com/RogueException/Discord.Net/releases +[release tag]: https://github.com/discord-net/Discord.Net/releases ## I came from an earlier version of Discord.Net 1.0, and DependencyMap doesn't seem to exist anymore in the later revision? What happened to it? @@ -26,4 +26,4 @@ The `DependencyMap` has been replaced with Microsoft's [DependencyInjection] Abstractions. An example usage can be seen [here](https://github.com/foxbot/DiscordBotBase/blob/csharp/src/DiscordBot/Program.cs#L36). -[DependencyInjection]: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection \ No newline at end of file +[DependencyInjection]: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection diff --git a/docs/faq/toc.yml b/docs/faq/toc.yml index 393e948f6..5a7e7e7f2 100644 --- a/docs/faq/toc.yml +++ b/docs/faq/toc.yml @@ -6,10 +6,14 @@ topicUid: FAQ.Basics.BasicOp - name: Client Basics topicUid: FAQ.Basics.ClientBasics + - name: Interactions + topicUid: FAQ.Basics.InteractionBasics - name: Commands items: - - name: General + - name: String commands topicUid: FAQ.Commands.General + - name: Interaction commands + topicUid: FAQ.Commands.Interactions - name: Dependency Injection topicUid: FAQ.Commands.DI - name: Glossary diff --git a/docs/guides/concepts/entities.md b/docs/guides/concepts/entities.md index 5ad5b01f2..e3ca7db32 100644 --- a/docs/guides/concepts/entities.md +++ b/docs/guides/concepts/entities.md @@ -31,8 +31,7 @@ But that doesn't mean a message _can't_ come from a retrieve information about a guild from a message entity, you will need to cast its channel object to a `SocketTextChannel`. -You can find out various types of entities in the @FAQ.Misc.Glossary -page. +You can find out various types of entities in the [Glossary page.](xref:FAQ.Glossary) ## Navigation @@ -63,4 +62,4 @@ a variant of the type that you need. ## Sample -[!code-csharp[Entity Sample](samples/entities.cs)] \ No newline at end of file +[!code-csharp[Entity Sample](samples/entities.cs)] diff --git a/docs/guides/getting_started/images/nightlies-vs-note.png b/docs/guides/getting_started/images/nightlies-vs-note.png deleted file mode 100644 index 0dcf2dea3..000000000 Binary files a/docs/guides/getting_started/images/nightlies-vs-note.png and /dev/null differ diff --git a/docs/guides/getting_started/images/nightlies-vs-step1.png b/docs/guides/getting_started/images/nightlies-vs-step1.png deleted file mode 100644 index a399ca66c..000000000 Binary files a/docs/guides/getting_started/images/nightlies-vs-step1.png and /dev/null differ diff --git a/docs/guides/getting_started/images/nightlies-vs-step2.png b/docs/guides/getting_started/images/nightlies-vs-step2.png deleted file mode 100644 index 75cecbb8d..000000000 Binary files a/docs/guides/getting_started/images/nightlies-vs-step2.png and /dev/null differ diff --git a/docs/guides/getting_started/images/nightlies-vs-step4.png b/docs/guides/getting_started/images/nightlies-vs-step4.png deleted file mode 100644 index 6462ab994..000000000 Binary files a/docs/guides/getting_started/images/nightlies-vs-step4.png and /dev/null differ diff --git a/docs/guides/getting_started/installing.md b/docs/guides/getting_started/installing.md index 61e3bb6ec..09dc32837 100644 --- a/docs/guides/getting_started/installing.md +++ b/docs/guides/getting_started/installing.md @@ -11,9 +11,9 @@ may also compile this library yourself should you so desire. ## Supported Platforms -Discord.Net targets [.NET Standard] both 1.3 and 2.0; this also means -that creating applications using the latest version of [.NET Core] is -the most recommended. If you are bound by Windows-specific APIs or +Discord.Net targets [.NET 6.0] and [.NET 5.0], but is also available on older versions, like [.NET Standard] and [.NET Core]; this still means +that creating applications using the latest version of .NET (6.0) +is most recommended. If you are bound by Windows-specific APIs or other limitations, you may also consider targeting [.NET Framework] 4.6.1 or higher. @@ -22,10 +22,12 @@ other limitations, you may also consider targeting [.NET Framework] > notice. It is known to have issues with the library's WebSockets > implementation and may crash the application upon startup. -[Mono]: https://www.mono-project.com/ -[.NET Standard]: https://docs.microsoft.com/en-us/dotnet/articles/standard/library -[.NET Core]: https://docs.microsoft.com/en-us/dotnet/articles/core/ -[.NET Framework]: https://docs.microsoft.com/en-us/dotnet/framework/get-started/ +[mono]: https://www.mono-project.com/ +[.net 6.0]: https://docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-6 +[.net 5.0]: https://docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-5 +[.net standard]: https://docs.microsoft.com/en-us/dotnet/articles/standard/library +[.net core]: https://docs.microsoft.com/en-us/dotnet/articles/core/ +[.net framework]: https://docs.microsoft.com/en-us/dotnet/framework/get-started/ [additional steps]: #installing-on-net-standard-11 ## Installing with NuGet @@ -37,37 +39,37 @@ Development builds of Discord.Net, as well as add-ons, will be published to our [MyGet feed]. See @Guides.GettingStarted.Installation.Nightlies to learn more. -[official NuGet feed]: https://nuget.org -[MyGet feed]: https://www.myget.org/feed/Packages/discord-net +[official nuget feed]: https://nuget.org +[myget feed]: https://www.myget.org/feed/Packages/discord-net ### [Using Visual Studio](#tab/vs-install) 1. Create a new solution for your bot 2. In the Solution Explorer, find the "Dependencies" element under your - bot's project + bot's project 3. Right click on "Dependencies", and select "Manage NuGet packages" - ![Step 3](images/install-vs-deps.png) + ![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) + ![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) + ![Step 2](images/install-rider-nuget-manager.png) 3. In the "Packages" tab, search for `Discord.Net` - ![Step 3](images/install-rider-search.png) + ![Step 3](images/install-rider-search.png) 4. Install by adding the package to your project - ![Step 4](images/install-rider-add.png) + ![Step 4](images/install-rider-add.png) ### [Using Visual Studio Code](#tab/vs-code) @@ -82,7 +84,7 @@ published to our [MyGet feed]. See 2. Navigate to where your `*.csproj` is located 3. Enter `dotnet add package Discord.Net` -*** +--- ## Compiling from Source @@ -90,15 +92,15 @@ In order to compile Discord.Net, you will need the following: ### Using Visual Studio -* [Visual Studio 2019](https://visualstudio.microsoft.com/) -* [.NET Core SDK] +- [Visual Studio 2019](https://visualstudio.microsoft.com/) or later. +- [.NET 5 SDK] -The .NET Core and Docker workload is required during Visual Studio +The .NET 5 workload is required during Visual Studio installation. ### Using Command Line -* [.NET Core SDK] +- [.NET 5 SDK] ## Additional Information @@ -117,28 +119,28 @@ by installing one or more custom packages as listed below. 1. Download the latest [.NET Core SDK]. 2. Create or move your existing project to use .NET Core. 3. Modify your `` tag to at least `netcoreapp2.1`, or - by adding the `--framework netcoreapp2.1` switch when building. + by adding the `--framework netcoreapp2.1` switch when building. #### [Custom Packages](#tab/custom-pkg) -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 -over the default ones. +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)] -[DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient -[DiscordSocketConfig]: xref:Discord.WebSocket.DiscordSocketConfig +[discordsocketclient]: xref:Discord.WebSocket.DiscordSocketClient +[discordsocketconfig]: xref:Discord.WebSocket.DiscordSocketConfig -*** +--- -[.NET Core SDK]: https://dotnet.microsoft.com/download \ No newline at end of file +[.net 5 sdk]: https://dotnet.microsoft.com/download diff --git a/docs/guides/getting_started/labs.md b/docs/guides/getting_started/labs.md new file mode 100644 index 000000000..e52014312 --- /dev/null +++ b/docs/guides/getting_started/labs.md @@ -0,0 +1,30 @@ +--- +uid: Guides.GettingStarted.Installation.Labs +title: Installing Labs builds +--- + +# Installing Discord.NET Labs + +Discord.NET Labs is the experimental repository that introduces new features & chips away at all bugs until ready for merging into Discord.NET. +Are you looking to test or play with new features? + +> [!IMPORTANT] +> It is very ill advised to use Discord.NET Labs in a production environment normally, +> considering it can include bugs that have not been discovered yet, as features are freshly added. +> However if approached correctly, will work as a pre-release to Discord.NET. +> Make sure to report any bugs at the Labs [repository] or on [Discord] + +[Discord]: https://discord.gg/dnet +[repository]: https://github.com/Discord-Net-Labs/Discord.Net-Labs + +## Installation: + +[NuGet] - This only includes releases, on which features are ready to test. + +> [!NOTE] +> Installing NuGet packages is covered fully at [Installing Discord NET](xref:Guides.GettingStarted.Installation) + +[MyGet] - Available for current builds and unreleased features. + +[NuGet]: https://www.nuget.org/packages/Discord.Net.Labs/ +[MyGet]: https://www.myget.org/feed/Packages/discord-net-labs diff --git a/docs/guides/getting_started/nightlies.md b/docs/guides/getting_started/nightlies.md deleted file mode 100644 index 2b9fde87b..000000000 --- a/docs/guides/getting_started/nightlies.md +++ /dev/null @@ -1,97 +0,0 @@ ---- -uid: Guides.GettingStarted.Installation.Nightlies -title: Installing Nightly Build ---- - -# Installing Discord.Net Nightly Build - -Before Discord.Net pushes a new set of features into the stable -version, we use nightly builds to test the features with the -community for an extensive period of time. Each nightly build is -compiled by AppVeyor whenever a new commit is made and will be pushed -to our MyGet feed. - -> [!IMPORTANT] -> Although nightlies are generally stable and have more features -> and bug fixes than the current stable build on NuGet, there -> will be breaking changes during the development or -> breaking bugs; these bugs are usually fixed as soon as they -> are discovered, but you should still be aware of that. - -## Installing with MyGet (Recommended) - -MyGet is typically used by many development teams to publish their -latest pre-release packages before the features are finalized and -pushed to NuGet. - -The following is the feed link of Discord.Net, - -* `https://www.myget.org/F/discord-net/api/v3/index.json` - -Depending on which IDE you use, there are many different ways of -adding the feed to your package source. - -### [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 pre-release` checkbox to see the -> nightly builds! -> ![Checkbox](images/nightlies-vs-note.png) - -### [Using dotnet CLI](#tab/cli) - -1. Launch a terminal of your choice -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 -your project. - -To do this, create a file named `NuGet.Config` alongside the root of -your application, where the project is located. - -Paste the following snippets into this configuration file, adding any -additional feeds if necessary. - -[!code[NuGet Configuration](samples/nuget.config)] - -After which, you may install the packages by directly modifying the -project file and specifying a version, or by using -the [Package Manager Console](https://docs.microsoft.com/en-us/nuget/tools/powershell-reference) -(`Install-Package Discord.Net -IncludePrerelease`). - -*** - -## Installing from AppVeyor Artifacts - -As mentioned in the first paragraph, we utilize AppVeyor to perform -automated tests and publish the new build. During the publishing -process, we also upload the NuGet packages onto -AppVeyor's Artifact collection. - -The latest build status can be found within our [AppVeyor project]. - -[AppVeyor project]: https://ci.appveyor.com/project/rogueexception/discord-net - -1. In the project, you may find our latest build including the - aforementioned artifacts. - ![Artifacts](images/appveyor-artifacts.png) -2. In the artifacts collection, you should see the latest packages - packed in `*.nupkg` form which you could download from and use. - ![NuPkgs](images/appveyor-nupkg.png) diff --git a/docs/guides/interactions/application-commands/context-menu-commands/creating-context-menu-commands.md b/docs/guides/int_basics/application-commands/context-menu-commands/creating-context-menu-commands.md similarity index 89% rename from docs/guides/interactions/application-commands/context-menu-commands/creating-context-menu-commands.md rename to docs/guides/int_basics/application-commands/context-menu-commands/creating-context-menu-commands.md index 02a9cde14..4e6210ba8 100644 --- a/docs/guides/interactions/application-commands/context-menu-commands/creating-context-menu-commands.md +++ b/docs/guides/int_basics/application-commands/context-menu-commands/creating-context-menu-commands.md @@ -16,9 +16,9 @@ Guild commands are specific to the guild you specify when making them. Guild com - Your app can have a global and guild command with the same name - Multiple apps can have commands with the same names -**Note**: Apps can have a maximum of 5 global context menu commands, and an additional 5 guild-specific context menu commands per guild. - -If you don't have the code for a bot ready yet please follow [this guide](https://docs.stillu.cc/guides/getting_started/first-bot.html). +[!IMPORTANT] +> Apps can have a maximum of 5 global context menu commands, +> and an additional 5 guild-specific context menu commands per guild. ## UserCommandBuilder @@ -40,7 +40,9 @@ The context menu message command builder will help you create message commands. | WithName | Function | Sets the field name. | | Build | Function | Builds the builder into the appropriate `MessageCommandProperties` class used to make Menu commands | -**Note**: Context Menu command names can be upper and lowercase, and use spaces. +> [!NOTE] +> Context Menu command names can be upper and lowercase, and use spaces. +> They cannot be registered pre-ready. Let's use the user command builder to make a global and guild command. @@ -102,4 +104,7 @@ public async Task Client_Ready() ``` > [!NOTE] -> Application commands only need to be created once. They do _not_ have to be 'created' on every startup or connection. The example simple shows creating them in the ready event as it's simpler than creating normal bot commands to register application commands. +> Application commands only need to be created once. They do _not_ have to be +> 'created' on every startup or connection. +> The example simple shows creating them in the ready event +> as it's simpler than creating normal bot commands to register application commands. diff --git a/docs/guides/interactions/application-commands/context-menu-commands/receiving-context-menu-command-events.md b/docs/guides/int_basics/application-commands/context-menu-commands/receiving-context-menu-command-events.md similarity index 100% rename from docs/guides/interactions/application-commands/context-menu-commands/receiving-context-menu-command-events.md rename to docs/guides/int_basics/application-commands/context-menu-commands/receiving-context-menu-command-events.md diff --git a/docs/guides/int_basics/application-commands/intro.md b/docs/guides/int_basics/application-commands/intro.md new file mode 100644 index 000000000..f55d0a2fc --- /dev/null +++ b/docs/guides/int_basics/application-commands/intro.md @@ -0,0 +1,51 @@ +--- +uid: Guides.SlashCommands.Intro +title: Introduction to slash commands +--- + + +# Getting started with application commands. + +This guide will show you how to use application commands. +If you have extra questions that aren't covered here you can come to our +[Discord](https://discord.com/invite/dvSfUTet3K) server and ask around there. + +## What is an application command? + +Application commands consist of three different types. Slash commands, context menu User commands and context menu Message commands. +Slash commands are made up of a name, description, and a block of options, which you can think of like arguments to a function. +The name and description help users find your command among many others, and the options validate user input as they fill out your command. +Message and User commands are only a name, to the user. So try to make the name descriptive. +They're accessed by right clicking (or long press, on mobile) a user or a message, respectively. + +> [!IMPORTANT] +> Context menu commands are currently not supported on mobile. + +All three varieties of application commands have both Global and Guild variants. +Your global commands are available in every guild that adds your application. +You can also make commands for a specific guild; they're only available in that guild. +The User and Message commands are more limited in quantity than the slash commands. +For specifics, check out their respective guide pages. + +An Interaction is the message that your application receives when a user uses a command. +It includes the values that the user submitted, as well as some metadata about this particular instance of the command being used: +the guild_id, +channel_id, +member and other fields. +You can find all the values in our data models. + +## Authorizing your bot for application commands + +There is a new special OAuth2 scope for applications called `applications.commands`. +In order to make Application Commands work within a guild, the guild must authorize your application +with the `applications.commands` scope. The bot scope is not enough. + +Head over to your discord applications OAuth2 screen and make sure to select the `application.commands` scope. + +![OAuth2 scoping](slash-commands/images/oauth.png) + +From there you can then use the link to add your bot to a server. + +> [!NOTE] +> In order for users in your guild to use your slash commands, they need to have +> the "Use Application Commands" permission on the guild. diff --git a/docs/guides/interactions/application-commands/slash-commands/08-bulk-overwrite-of-global-slash-commands.md b/docs/guides/int_basics/application-commands/slash-commands/bulk-overwrite-of-global-slash-commands.md similarity index 100% rename from docs/guides/interactions/application-commands/slash-commands/08-bulk-overwrite-of-global-slash-commands.md rename to docs/guides/int_basics/application-commands/slash-commands/bulk-overwrite-of-global-slash-commands.md diff --git a/docs/guides/interactions/application-commands/slash-commands/07-choice-slash-command.md b/docs/guides/int_basics/application-commands/slash-commands/choice-slash-command.md similarity index 100% rename from docs/guides/interactions/application-commands/slash-commands/07-choice-slash-command.md rename to docs/guides/int_basics/application-commands/slash-commands/choice-slash-command.md diff --git a/docs/guides/interactions/application-commands/slash-commands/02-creating-slash-commands.md b/docs/guides/int_basics/application-commands/slash-commands/creating-slash-commands.md similarity index 100% rename from docs/guides/interactions/application-commands/slash-commands/02-creating-slash-commands.md rename to docs/guides/int_basics/application-commands/slash-commands/creating-slash-commands.md diff --git a/docs/guides/interactions/application-commands/slash-commands/images/ephemeral1.png b/docs/guides/int_basics/application-commands/slash-commands/images/ephemeral1.png similarity index 100% rename from docs/guides/interactions/application-commands/slash-commands/images/ephemeral1.png rename to docs/guides/int_basics/application-commands/slash-commands/images/ephemeral1.png diff --git a/docs/guides/interactions/application-commands/slash-commands/images/feedback1.png b/docs/guides/int_basics/application-commands/slash-commands/images/feedback1.png similarity index 100% rename from docs/guides/interactions/application-commands/slash-commands/images/feedback1.png rename to docs/guides/int_basics/application-commands/slash-commands/images/feedback1.png diff --git a/docs/guides/interactions/application-commands/slash-commands/images/feedback2.png b/docs/guides/int_basics/application-commands/slash-commands/images/feedback2.png similarity index 100% rename from docs/guides/interactions/application-commands/slash-commands/images/feedback2.png rename to docs/guides/int_basics/application-commands/slash-commands/images/feedback2.png diff --git a/docs/guides/interactions/application-commands/slash-commands/images/listroles1.png b/docs/guides/int_basics/application-commands/slash-commands/images/listroles1.png similarity index 100% rename from docs/guides/interactions/application-commands/slash-commands/images/listroles1.png rename to docs/guides/int_basics/application-commands/slash-commands/images/listroles1.png diff --git a/docs/guides/interactions/application-commands/slash-commands/images/listroles2.png b/docs/guides/int_basics/application-commands/slash-commands/images/listroles2.png similarity index 100% rename from docs/guides/interactions/application-commands/slash-commands/images/listroles2.png rename to docs/guides/int_basics/application-commands/slash-commands/images/listroles2.png diff --git a/docs/guides/interactions/application-commands/slash-commands/images/oauth.png b/docs/guides/int_basics/application-commands/slash-commands/images/oauth.png similarity index 100% rename from docs/guides/interactions/application-commands/slash-commands/images/oauth.png rename to docs/guides/int_basics/application-commands/slash-commands/images/oauth.png diff --git a/docs/guides/interactions/application-commands/slash-commands/images/settings1.png b/docs/guides/int_basics/application-commands/slash-commands/images/settings1.png similarity index 100% rename from docs/guides/interactions/application-commands/slash-commands/images/settings1.png rename to docs/guides/int_basics/application-commands/slash-commands/images/settings1.png diff --git a/docs/guides/interactions/application-commands/slash-commands/images/settings2.png b/docs/guides/int_basics/application-commands/slash-commands/images/settings2.png similarity index 100% rename from docs/guides/interactions/application-commands/slash-commands/images/settings2.png rename to docs/guides/int_basics/application-commands/slash-commands/images/settings2.png diff --git a/docs/guides/interactions/application-commands/slash-commands/images/settings3.png b/docs/guides/int_basics/application-commands/slash-commands/images/settings3.png similarity index 100% rename from docs/guides/interactions/application-commands/slash-commands/images/settings3.png rename to docs/guides/int_basics/application-commands/slash-commands/images/settings3.png diff --git a/docs/guides/interactions/application-commands/slash-commands/images/slashcommand1.png b/docs/guides/int_basics/application-commands/slash-commands/images/slashcommand1.png similarity index 100% rename from docs/guides/interactions/application-commands/slash-commands/images/slashcommand1.png rename to docs/guides/int_basics/application-commands/slash-commands/images/slashcommand1.png diff --git a/docs/guides/interactions/application-commands/slash-commands/images/slashcommand2.png b/docs/guides/int_basics/application-commands/slash-commands/images/slashcommand2.png similarity index 100% rename from docs/guides/interactions/application-commands/slash-commands/images/slashcommand2.png rename to docs/guides/int_basics/application-commands/slash-commands/images/slashcommand2.png diff --git a/docs/guides/interactions/application-commands/slash-commands/04-parameters.md b/docs/guides/int_basics/application-commands/slash-commands/parameters.md similarity index 100% rename from docs/guides/interactions/application-commands/slash-commands/04-parameters.md rename to docs/guides/int_basics/application-commands/slash-commands/parameters.md diff --git a/docs/guides/interactions/application-commands/slash-commands/05-responding-ephemerally.md b/docs/guides/int_basics/application-commands/slash-commands/responding-ephemerally.md similarity index 100% rename from docs/guides/interactions/application-commands/slash-commands/05-responding-ephemerally.md rename to docs/guides/int_basics/application-commands/slash-commands/responding-ephemerally.md diff --git a/docs/guides/interactions/application-commands/slash-commands/03-responding-to-slash-commands.md b/docs/guides/int_basics/application-commands/slash-commands/responding-to-slash-commands.md similarity index 100% rename from docs/guides/interactions/application-commands/slash-commands/03-responding-to-slash-commands.md rename to docs/guides/int_basics/application-commands/slash-commands/responding-to-slash-commands.md diff --git a/docs/guides/interactions/application-commands/slash-commands/06-subcommands.md b/docs/guides/int_basics/application-commands/slash-commands/subcommands.md similarity index 100% rename from docs/guides/interactions/application-commands/slash-commands/06-subcommands.md rename to docs/guides/int_basics/application-commands/slash-commands/subcommands.md diff --git a/docs/guides/interactions/intro.md b/docs/guides/int_basics/intro.md similarity index 100% rename from docs/guides/interactions/intro.md rename to docs/guides/int_basics/intro.md diff --git a/docs/guides/interactions/message-components/05-advanced.md b/docs/guides/int_basics/message-components/advanced.md similarity index 100% rename from docs/guides/interactions/message-components/05-advanced.md rename to docs/guides/int_basics/message-components/advanced.md diff --git a/docs/guides/interactions/message-components/03-buttons-in-depth.md b/docs/guides/int_basics/message-components/buttons-in-depth.md similarity index 100% rename from docs/guides/interactions/message-components/03-buttons-in-depth.md rename to docs/guides/int_basics/message-components/buttons-in-depth.md diff --git a/docs/guides/interactions/message-components/images/image1.png b/docs/guides/int_basics/message-components/images/image1.png similarity index 100% rename from docs/guides/interactions/message-components/images/image1.png rename to docs/guides/int_basics/message-components/images/image1.png diff --git a/docs/guides/interactions/message-components/images/image2.png b/docs/guides/int_basics/message-components/images/image2.png similarity index 100% rename from docs/guides/interactions/message-components/images/image2.png rename to docs/guides/int_basics/message-components/images/image2.png diff --git a/docs/guides/interactions/message-components/images/image3.png b/docs/guides/int_basics/message-components/images/image3.png similarity index 100% rename from docs/guides/interactions/message-components/images/image3.png rename to docs/guides/int_basics/message-components/images/image3.png diff --git a/docs/guides/interactions/message-components/images/image4.png b/docs/guides/int_basics/message-components/images/image4.png similarity index 100% rename from docs/guides/interactions/message-components/images/image4.png rename to docs/guides/int_basics/message-components/images/image4.png diff --git a/docs/guides/interactions/message-components/images/image5.png b/docs/guides/int_basics/message-components/images/image5.png similarity index 100% rename from docs/guides/interactions/message-components/images/image5.png rename to docs/guides/int_basics/message-components/images/image5.png diff --git a/docs/guides/interactions/message-components/images/image6.png b/docs/guides/int_basics/message-components/images/image6.png similarity index 100% rename from docs/guides/interactions/message-components/images/image6.png rename to docs/guides/int_basics/message-components/images/image6.png diff --git a/docs/guides/interactions/message-components/01-getting-started.md b/docs/guides/int_basics/message-components/intro.md similarity index 98% rename from docs/guides/interactions/message-components/01-getting-started.md rename to docs/guides/int_basics/message-components/intro.md index cd5eadd0a..cc22d54c5 100644 --- a/docs/guides/interactions/message-components/01-getting-started.md +++ b/docs/guides/int_basics/message-components/intro.md @@ -1,5 +1,5 @@ --- -uid: Guides.MessageComponents.GettingStarted +uid: Guides.MessageComponents.Intro title: Getting Started with Components --- diff --git a/docs/guides/interactions/message-components/02-responding-to-buttons.md b/docs/guides/int_basics/message-components/responding-to-buttons.md similarity index 100% rename from docs/guides/interactions/message-components/02-responding-to-buttons.md rename to docs/guides/int_basics/message-components/responding-to-buttons.md diff --git a/docs/guides/interactions/message-components/04-select-menus.md b/docs/guides/int_basics/message-components/select-menus.md similarity index 100% rename from docs/guides/interactions/message-components/04-select-menus.md rename to docs/guides/int_basics/message-components/select-menus.md diff --git a/docs/guides/int_framework/autocompletion.md b/docs/guides/int_framework/autocompletion.md new file mode 100644 index 000000000..834db2b4f --- /dev/null +++ b/docs/guides/int_framework/autocompletion.md @@ -0,0 +1,47 @@ +--- +uid: Guides.IntFw.AutoCompletion +title: Command Autocompletion +--- + +# AutocompleteHandlers + +[Autocompleters] provide a similar pattern to TypeConverters. +[Autocompleters] are cached, singleton services and they are used by the +Interaction Service to handle Autocomplete Interations targeted to a specific Slash Command parameter. + +To start using AutocompleteHandlers, use the `[AutocompleteAttribute(Type type)]` overload of the [AutocompleteAttribute]. +This will dynamically link the parameter to the [AutocompleteHandler] type. + +AutocompleteHandlers raise the `AutocompleteHandlerExecuted` event on execution. This event can be also used to create a post-execution logic, just like the `*CommandExecuted` events. + +## Creating AutocompleteHandlers + +A valid AutocompleteHandlers must inherit [AutocompleteHandler] base type and implement all of its abstract methods. + +### GenerateSuggestionsAsync() + +The Interactions Service uses this method to generate a response of an Autocomplete Interaction. +This method should return `AutocompletionResult.FromSuccess(IEnumerable)` to +display parameter suggestions to the user. If there are no suggestions to be presented to the user, you have two results: + +1. Returning the parameterless `AutocompletionResult.FromSuccess()` will display a "No options match your search." message to the user. +2. Returning `AutocompleteResult.FromError()` will make the Interaction Service **not** respond to the interaction, +consequently displaying the user a "Loading options failed." message. `AutocompletionResult.FromError()` is solely used for error handling purposes. Discord currently doesn't allow +you to display custom error messages. This result type will be directly returned to the `AutocompleteHandlerExecuted` method. + +## Resolving AutocompleteHandler Dependencies + +AutocompleteHandler dependencies are resolved using the same dependency injection +pattern as the Interaction Modules. +Property injection and constructor injection are both valid ways to get service dependencies. + +Because [AutocompleterHandlers] are constructed at service startup, +class dependencies are resolved only once. + +> [!NOTE] +> If you need to access per-request dependencies you can use the +> IServiceProvider parameter of the `GenerateSuggestionsAsync()` method. + +[AutoCompleteHandlers]: xref:Discord.Interactions.AutocompleteHandler +[AutoCompleteHandler]: xref:Discord.Interactions.AutocompleteHandler +[AutoCompleteAttribute]: diff --git a/docs/guides/int_framework/dependency-injection.md b/docs/guides/int_framework/dependency-injection.md new file mode 100644 index 000000000..31d001f4b --- /dev/null +++ b/docs/guides/int_framework/dependency-injection.md @@ -0,0 +1,13 @@ +--- +uid: Guides.IntFw.DI +title: Dependency Injection +--- + +# Dependency Injection + +Dependency injection in the Interaction Service is mostly based on that of the Text-based command service, +for which further information is found [here](xref:Guides.TextCommands.DI). + +> [!NOTE] +> The 2 are nearly identical, except for one detail: +> [Resolving Module Dependencies](xref:Guides.IntFw.Intro#resolving-module-dependencies) diff --git a/docs/guides/int_framework/intro.md b/docs/guides/int_framework/intro.md new file mode 100644 index 000000000..7dfd7ac6e --- /dev/null +++ b/docs/guides/int_framework/intro.md @@ -0,0 +1,353 @@ +--- +uid: Guides.IntFw.Intro +title: Introduction to the Interaction Service +--- + +# Getting Started + +The Interaction Service provides an attribute based framework for creating Discord Interaction handlers. + +To start using the Interaction Service, you need to create a service instance. +Optionally you can provide the [InteractionService] constructor with a +[InteractionServiceConfig] to change the services behaviour to suit your needs. + +```csharp +... +// _client here is DiscordSocketClient. +// A different approach to passing in a restclient is also possible. +var _interactionService = new InteractionService(_client.Rest); + +... +``` + +## Modules + +Attribute based Interaction handlers must be defined within a command module class. +Command modules are responsible for executing the Interaction handlers and providing them with the necessary execution info and helper functions. + +Command modules are transient objects. +A new module instance is created before a command execution starts then it will be disposed right after the method returns. + +Every module class must: + +- be public +- inherit [InteractionModuleBase] + +Optionally you can override the included : + +- OnModuleBuilding (executed after the module is built) +- BeforeExecute (executed before a command execution starts) +- AfterExecute (executed after a command execution concludes) + +methods to configure the modules behaviour. + +Every command module exposes a set of helper methods, namely: + +- `RespondAsync()` => Respond to the interaction +- `FollowupAsync()` => Create a followup message for an interaction +- `ReplyAsync()` => Send a message to the origin channel of the interaction +- `DeleteOriginalResponseAsync()` => Delete the original interaction response + +## Commands + +Valid **Interaction Commands** must comply with the following requirements: + +| | return type | max parameter count | allowed parameter types | attribute | +|-------------------------------|------------------------------|---------------------|-------------------------------|--------------------------| +|[Slash Command](#slash-commands)| `Task`/`Task` | 25 | any* | `[SlashCommand]` | +|[User Command](#user-commands) | `Task`/`Task` | 1 | Implementations of `IUser` | `[UserCommand]` | +|[Message Command](#message-commands)| `Task`/`Task` | 1 | Implementations of `IMessage` | `[MessageCommand]` | +|[Component Interaction Command](#component-interaction-commands)| `Task`/`Task` | inf | `string` or `string[]` | `[ComponentInteraction]` | +|[Autocomplete Command](#autocomplete-commands)| `Task`/`Task` | - | - | `[AutocompleteCommand]`| + +> [!NOTE] +> a `TypeConverter` that is capable of parsing type in question must be registered to the [InteractionService] instance. +> You should avoid using long running code in your command module. +> Depending on your setup, long running code may block the Gateway thread of your bot, interrupting its connection to Discord. + +## Slash Commands + +Slash Commands are created using the [SlashCommandAttribute]. +Every Slash Command must declare a name and a description. +You can check Discords **Application Command Naming Guidelines** +[here](https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-naming). + +[!code-csharp[Slash Command](samples/intro/slashcommand.cs)] + +### Parameters + +Slash Commands can have up to 25 method parameters. You must name your parameters in accordance with +[Discords Naming Guidelines](https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-naming). +[InteractionService] also features a pascal casing seperator for formatting parameter names with +pascal casing into Discord compliant parameter names('parameterName' => 'parameter-name'). +By default, your methods can feature the following parameter types: + +- Implementations of [IUser] +- Implementations of [IChannel] +- Implementations of [IRole] +- Implementations of [IMentionable] +- `string` +- `float`, `double`, `decimal` +- `bool` +- `char` +- `sbyte`, `byte` +- `int16`, `int32`, `int64` +- `uint16`, `uint32`, `uint64` +- `enum` (Values are registered as multiple choice options and are enforced by Discord. Use `[HideAttribute]` on enum values to prevent them from getting registered.) +- `DateTime` +- `TimeSpan` + +--- + +**You can use more specialized implementations of [IChannel] to restrict the allowed channel types for a channel type option.* +| interface | Channel Type | +|---------------------|-------------------------------| +| `IStageChannel` | Stage Channels | +| `IVoiceChannel` | Voice Channels | +| `IDMChannel` | DM Channels | +| `IGroupChannel` | Group Channels | +| `ICategory Channel` | Category Channels | +| `INewsChannel` | News Channels | +| `IThreadChannel` | Public, Private, News Threads | +| `ITextChannel` | Text Channels | + +--- + +#### Optional Parameters + +Parameters with default values (ie. `int count = 0`) will be displayed as optional parameters on Discord Client. + +#### Parameter Summary + +By using the [SummaryAttribute] you can customize the displayed name and description of a parameter + +[!code-csharp[Summary Attribute](samples/intro/summaryattribute.cs)] + +#### Parameter Choices + +[ChoiceAttribute] can be used to add choices to a parameter. + +[!code-csharp[Choice Attribute](samples/intro/groupattribute.cs)] + +This Slash Command will be displayed exactly the same as the previous example. + +#### Channel Types + +Channel types for an [IChannel] parameter can also be restricted using the [ChannelTypesAttribute]. + +[!code-csharp[Channel Attribute](samples/intro/channelattribute.cs)] + +In this case, user can only input Stage Channels and Text Channels to this parameter. + +#### Min/Max Value + +You can specify the permitted max/min value for a number type parameter using the [MaxValueAttribute] and [MinValueAttribute]. + +## User Commands + +A valid User Command must have the following structure: + +[!code-csharp[User Command](samples/intro/usercommand.cs)] + +> [!WARNING] +> User commands can only have one parameter and its type must be an implementation of [IUser]. + +## Message Commands + +A valid Message Command must have the following structure: + +[!code-csharp[Message Command](samples/intro/messagecommand.cs)] + +> [!WARNING] +> Message commands can only have one parameter and its type must be an implementation of [IMessage]. + +## Component Interaction Commands + +Component Interaction Commands are used to handle interactions that originate from **Discord Message Component**s. +This pattern is particularly useful if you will be reusing a set a **Custom ID**s. + +Component Interaction Commands support wild card matching, +by default `*` character can be used to create a wild card pattern. +Interaction Service will use lazy matching to capture the words corresponding to the wild card character. +And the captured words will be passed on to the command method in the same order they were captured. + +[!code-csharp[Button](samples/intro/button.cs)] + +You may use as many wild card characters as you want. + +> [!NOTE] +> If Interaction Service recieves a component interaction with **player:play,rickroll** custom id, +> `op` will be *play* and `name` will be *rickroll* + +## Select Menus + +Unlike button interactions, select menu interactions also contain the values of the selected menu items. +In this case, you should structure your method to accept a string array. + +[!code-csharp[Dropdown](samples/intro/dropdown.cs)] + +> [!NOTE] +> Wildcards may also be used to match a select menu ID, +> though keep in mind that the array containing the select menu values should be the last parameter. + +## Autocomplete Commands + +Autocomplete commands must be parameterless methods. A valid Autocomplete command must have the following structure: + +[!code-csharp[Autocomplete Command](samples/intro/autocomplete.cs)] + +Alternatively, you can use the [AutocompleteHandlers] to simplify this workflow. + +## Interaction Context + +Every command module provides its commands with an execution context. +This context property includes general information about the underlying interaction that triggered the command execution. +The base command context. + +You can design your modules to work with different implementation types of [IInteractionContext]. +To achieve this, make sure your module classes inherit from the generic variant of the [InteractionModuleBase]. + +> [!NOTE] +> Context type must be consistent throughout the project, or you will run into issues during runtime. + +The [InteractionService] ships with 4 different kinds of [InteractionContext]: + +1. [InteractionContext]]: A bare-bones execution context consisting of only implementation neutral interfaces +2. [SocketInteractionContext]: An execution context for use with [DiscordSocketClient]. Socket entities are exposed in this context without the need of casting them. +3. [ShardedInteractionContext]: [DiscordShardedClient] variant of the [SocketInteractionContext] +4. [RestInteractionContext]: An execution context designed to be used with a [DiscordRestClient] and webhook based interactions pattern + +You can create custom Interaction Contexts by implementing the [IInteractionContext] interface. + +One problem with using the concrete type InteractionContexts is that you cannot access the information that is specific to different interaction types without casting. Concrete type interaction contexts are great for creating shared interaction modules but you can also use the generic variants of the built-in interaction contexts to create interaction specific interaction modules. + +> [!INFO] +> Message component interactions have access to a special method called `UpdateAsync()` to update the body of the method the interaction originated from. +> Normally this wouldn't be accessable without casting the `Context.Interaction`. + +[!code-csharp[Context Example](samples/intro/context.cs)] + +## Loading Modules + +[InteractionService] can automatically discover and load modules that inherit [InteractionModuleBase] from an `Assembly`. +Call `InteractionService.AddModulesAsync()` to use this functionality. + +> [!NOTE] +> You can also manually add Interaction modules using the `InteractionService.AddModuleAsync()` +> method by providing the module type you want to load. + +## Resolving Module Dependencies + +Module dependencies are resolved using the Constructor Injection and Property Injection patterns. +Meaning, the constructor parameters and public settable properties of a module will be assigned using the `IServiceProvider`. +For more information on dependency injection, read the [DependencyInjection] guides. + +> [!NOTE] +> On every command execution, module dependencies are resolved using a new service scope which allows you to utilize scoped service instances, just like in Asp.Net. +> Including the precondition checks, every module method is executed using the same service scope and service scopes are disposed right after the `AfterExecute` method returns. + +## Module Groups + +Module groups allow you to create sub-commands and sub-commands groups. +By nesting commands inside a module that is tagged with [GroupAttribute] you can create prefixed commands. + +> [!WARNING] +> Although creating nested module stuctures are allowed, +> you are not permitted to use more than 2 [GroupAttribute]'s in module hierarchy. + +## Executing Commands + +Any of the following socket events can be used to execute commands: + +- [InteractionCreated] +- [ButtonExecuted] +- [SelectMenuExecuted] +- [AutocompleteExecuted] +- [UserCommandExecuted] +- [MessageCommandExecuted] + +Commands can be either executed on the gateway thread or on a seperate thread from the thread pool. This behaviour can be configured by changing the *RunMode* property of `InteractionServiceConfig` or by setting the *runMode* parameter of a command attribute. + +You can also configure the way [InteractionService] executes the commands. +By default, commands are executed using `ConstructorInfo.Invoke()` to create module instances and +`MethodInfo.Invoke()` method for executing the method bodies. +By setting, `InteractionServiceConfig.UseCompiledLambda` to `true`, you can make [InteractionService] create module instances and execute commands using +*Compiled Lambda* expressions. This cuts down on command execution time but it might add some memory overhead. + +Time it takes to create a module instance and execute a `Task.Delay(0)` method using the Reflection methods compared to Compiled Lambda expressions: + +| Method | Mean | Error | StdDev | +|----------------- |----------:|---------:|---------:| +| ReflectionInvoke | 225.93 ns | 4.522 ns | 7.040 ns | +| CompiledLambda | 48.79 ns | 0.981 ns | 1.276 ns | + +## Registering Commands to Discord + +Application commands loaded to the Interaction Service can be registered to Discord using a number of different methods. +In most cases `RegisterCommandsGloballyAsync()` and `RegisterCommandsToGuildAsync()` are the methods to use. +Command registration methods can only be used after the gateway client is ready or the rest client is logged in. + +[!code-csharp[Registering Commands Example](samples/intro/registering.cs)] + +Methods like `AddModulesToGuildAsync()`, `AddCommandsToGuildAsync()`, `AddModulesGloballyAsync()` and `AddCommandsGloballyAsync()` +can be used to register cherry picked modules or commands to global/guild scopes. + +> [!NOTE] +> In debug environment, since Global commands can take up to 1 hour to register/update, +> it is adviced to register your commands to a test guild for your changes to take effect immediately. +> You can use preprocessor directives to create a simple logic for registering commands as seen above + +## Interaction Utility + +Interaction Service ships with a static `InteractionUtiliy` +class which contains some helper methods to asynchronously waiting for Discord Interactions. +For instance, `WaitForInteractionAsync()` method allows you to wait for an Interaction for a given amount of time. +This method returns the first encountered Interaction that satisfies the provided predicate. + +> [!WARNING] +> If you are running the Interaction Service on `RunMode.Sync` you should avoid using this method in your commands, +> as it will block the gateway thread and interrupt your bots connection. + +## Webhook Based Interactions + +Instead of using the gateway to recieve Discord Interactions, Discord allows you to recieve Interaction events over Webhooks. +Interaction Service also supports this Interaction type but to be able to +respond to the Interactions within your command modules you need to perform the following: + +- Make your modules inherit `RestInteractionModuleBase` +- Set the `ResponseCallback` property of `InteractionServiceConfig` so that the `ResponseCallback` +delegate can be used to create HTTP responses from a deserialized json object string. +- Use the interaction endpoints of the module base instead of the interaction object (ie. `RespondAsync()`, `FollowupAsync()`...). + +[AutocompleteHandlers]: xref:Guides.IntFw.AutoCompletion +[DependencyInjection]: xref:Guides.TextCommands.DI + +[GroupAttribute]: xref:Discord.Interactions.GroupAttribute +[InteractionService]: xref:Discord.Interactions.InteractionService +[InteractionServiceConfig]: xref:Discord.Interactions.InteractionServiceConfig +[InteractionModuleBase]: xref:Discord.Interactions.InteractionModuleBase +[SlashCommandAttribute]: xref:Discord.Interactions.SlashCommandAttribute +[InteractionCreated]: xref:Discord.WebSocket.BaseSocketClient +[ButtonExecuted]: xref:Discord.WebSocket.BaseSocketClient +[SelectMenuExecuted]: xref:Discord.WebSocket.BaseSocketClient +[AutocompleteExecuted]: xref:Discord.WebSocket.BaseSocketClient +[UserCommandExecuted]: xref:Discord.WebSocket.BaseSocketClient +[MessageCommandExecuted]: xref:Discord.WebSocket.BaseSocketClient +[DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient +[DiscordRestClient]: xref:Discord.Rest.DiscordRestClient +[SocketInteractionContext]: xref:Discord.Interactions.SocketInteractionContext +[ShardedInteractionContext]: xref:Discord.Interactions.ShardedInteractionContext +[InteractionContext]: xref:Discord.Interactions.InteractionContext +[IInteractionContect]: xref:Discord.Interactions.IInteractionContext +[RestInteractionContext]: xref:Discord.Rest.RestInteractionContext +[SummaryAttribute]: xref:Discord.Interactions.SummaryAttribute +[ChoiceAttribute]: xref:Discord.Interactions.ChoiceAttribute +[ChannelTypesAttribute]: xref:Discord.Interactions.ChannelTypesAttribute +[MaxValueAttribute]: xref:Discord.Interactions.MaxValueAttribute +[MinValueAttribute]: xref:Discord.Interactions.MinValueAttribute + +[IChannel]: xref:Discord.IChannel +[IRole]: xref:Discord.IRole +[IUser]: xref:Discord.IUser +[IMessage]: xref:Discord.IMessage +[IMentionable]: xref:Discord.IMentionable diff --git a/docs/guides/int_framework/post-execution.md b/docs/guides/int_framework/post-execution.md new file mode 100644 index 000000000..c68d14fa5 --- /dev/null +++ b/docs/guides/int_framework/post-execution.md @@ -0,0 +1,69 @@ +--- +uid: Guides.IntFw.PostExecution +title: Post-Command execution +--- + +# Post-Execution Logic + +Interaction Service uses [IResult] to provide information about the state of command execution. +These can be used to log internal exceptions or provide some insight to the command user. + +If you are running your commands using `RunMode.Sync` these command results can be retrieved from +the return value of [InteractionService.ExecuteCommandAsync] method or by +registering delegates to Interaction Service events. + +If you are using the `RunMode.Async` to run your commands, +you must use the Interaction Service events to get the execution results. When using `RunMode.Async`, +[InteractionService.ExecuteCommandAsync] will always return a successful result. + +[InteractionService.ExecuteCommandAsync]: xref:Discord.Interactions.InteractionService.ExecuteCommandAsync* + +## Results + +Interaction Result come in a handful of different flavours: + +1. [AutocompletionResult]: returned by Autocompleters +2. [ExecuteResult]: contains the result of method body execution process +3. [PreconditionGroupResult]: returned by Precondition groups +4. [PreconditionResult]: returned by preconditions +5. [RuntimeResult]: a user implementable result for returning user defined results +6. [SearchResult]: returned by command lookup map +7. [TypeConverterResult]: returned by TypeConverters + +> [!NOTE] +> You can either use the [IResult.Error] property of an Interaction result or create type check for the +> afformentioned result types to branch out your post-execution logic to handle different situations. + + +[AutocompletionResult]: xref:Discord.AutocompleteResult +[ExecuteResult]: xref:Discord.Interactions.ExecuteResult +[PreconditionGroupResult]: xref:Discord.Interactions.PreconditionGroupResult +[PreconditionResult]: xref:Discord.Interactions.PreconditionResult +[SearchResult]: xref:Discord.Interactions.SearchResult`1 +[TypeConverterResult]: xref:Discord.Interactions.TypeConverterResult +[IResult.Error]: xref:Discord.Interactions.IResult.Error* + +## CommandExecuted Events + +Every time a command gets executed, Interaction Service raises a `CommandExecuted` event. +These events can be used to create a post-execution pipeline. + +[!code-csharp[Error Review](samples/postexecution/error_review.cs) + +## Log Event + +InteractionService regularly outputs information about the occuring events to keep the developer informed. + +## Runtime Result + +Interaction commands allow you to return `Task` to pass on additional information about the command execution +process back to your post-execution logic. + +Custom [RuntimeResult] classes can be created by inheriting the base [RuntimeResult] class. + +If command execution process reaches the method body of the command and no exceptions are thrown during +the execution of the method body, [RuntimeResult] returned by your command will be accessible by casting/type-checking the +[IResult] parameter of the `CommandExecuted` event delegate. + +[RuntimeResult]: xref:Discord.Interactions.RuntimeResult +[IResult]: xref:Discord.Interactions.IResult diff --git a/docs/guides/int_framework/preconditions.md b/docs/guides/int_framework/preconditions.md new file mode 100644 index 000000000..75e572798 --- /dev/null +++ b/docs/guides/int_framework/preconditions.md @@ -0,0 +1,77 @@ +--- +uid: Guides.IntFw.Preconditions +title: Preconditions +--- + +# Preconditions + +Precondition logic is the same as it is for Text-based commands. +A list of attributes and usage is still given for people who are new to both. + +There are two types of Preconditions you can use: + +* [PreconditionAttribute] can be applied to Modules, Groups, or Commands. +* [ParameterPreconditionAttribute] can be applied to Parameters. + +You may visit their respective API documentation to find out more. + +[PreconditionAttribute]: xref:Discord.Interactions.PreconditionAttribute +[ParameterPreconditionAttribute]: xref:Discord.Interactions.ParameterPreconditionAttribute + +## Bundled Preconditions + +@Discord.Interactions ships with several bundled Preconditions for you +to use. + +* @Discord.Interactions.RequireContextAttribute +* @Discord.Interactions.RequireOwnerAttribute +* @Discord.Interactions.RequireBotPermissionAttribute +* @Discord.Interactions.RequireUserPermissionAttribute +* @Discord.Interactions.RequireNsfwAttribute +* @Discord.Interactions.RequireRoleAttribute + +## Using Preconditions + +To use a precondition, simply apply any valid precondition candidate to +a command method signature as an attribute. + +[!code-csharp[Precondition usage](samples/preconditions/precondition_usage.cs)] + +## ORing Preconditions + +When writing commands, you may want to allow some of them to be +executed when only some of the precondition checks are passed. + +This is where the [Group] property of a precondition attribute comes in +handy. By assigning two or more preconditions to a group, the command +system will allow the command to be executed when one of the +precondition passes. + +### Example - ORing Preconditions + +[!code-csharp[OR Precondition](samples/preconditions/group_precondition.cs)] + +[Group]: xref:Discord.Commands.PreconditionAttribute.Group + +## Custom Preconditions + +To write your own Precondition, create a new class that inherits from +either [PreconditionAttribute] or [ParameterPreconditionAttribute] +depending on your use. + +In order for your Precondition to function, you will need to override +the [CheckPermissionsAsync] method. + +If the context meets the required parameters, return +[PreconditionResult.FromSuccess], otherwise return +[PreconditionResult.FromError] and include an error message if +necessary. + +> [!NOTE] +> Visual Studio can help you implement missing members +> from the abstract class by using the "Implement Abstract Class" +> IntelliSense hint. + +[CheckPermissionsAsync]: xref:Discord.Commands.PreconditionAttribute.CheckPermissionsAsync* +[PreconditionResult.FromSuccess]: xref:Discord.Commands.PreconditionResult.FromSuccess* +[PreconditionResult.FromError]: xref:Discord.Commands.PreconditionResult.FromError* diff --git a/docs/guides/int_framework/samples/intro/autocomplete.cs b/docs/guides/int_framework/samples/intro/autocomplete.cs new file mode 100644 index 000000000..f93c56eaa --- /dev/null +++ b/docs/guides/int_framework/samples/intro/autocomplete.cs @@ -0,0 +1,9 @@ +[AutocompleteCommand("parameter_name", "command_name")] +public async Task Autocomplete() +{ + IEnumerable results; + + ... + + await (Context.Interaction as SocketAutocompleteInteraction).RespondAsync(results); +} diff --git a/docs/guides/int_framework/samples/intro/button.cs b/docs/guides/int_framework/samples/intro/button.cs new file mode 100644 index 000000000..2b04d081b --- /dev/null +++ b/docs/guides/int_framework/samples/intro/button.cs @@ -0,0 +1,5 @@ +[ComponentInteraction("player:*,*")] +public async Task Play(string op, string name) +{ + ... +} diff --git a/docs/guides/int_framework/samples/intro/channelattribute.cs b/docs/guides/int_framework/samples/intro/channelattribute.cs new file mode 100644 index 000000000..09eb0275f --- /dev/null +++ b/docs/guides/int_framework/samples/intro/channelattribute.cs @@ -0,0 +1,5 @@ +[SlashCommand("name", "Description")] +public async Task Command([ChannelTypes(ChannelType.Stage, ChannelType.Text)] IChannel channel) +{ + ... +} diff --git a/docs/guides/int_framework/samples/intro/context.cs b/docs/guides/int_framework/samples/intro/context.cs new file mode 100644 index 000000000..5976ffc5c --- /dev/null +++ b/docs/guides/int_framework/samples/intro/context.cs @@ -0,0 +1,14 @@ +discordClient.ButtonExecuted += async (interaction) => +{ + var ctx = new SocketInteractionContext(discordClient, interaction); + await _interactionService.ExecuteAsync(ctx, serviceProvider); +}; + +public class MessageComponentModule : InteractionModuleBase> +{ + [ComponentInteraction("custom_id")] + public async Command() + { + Context.Interaction.UpdateAsync(...); + } +} diff --git a/docs/guides/int_framework/samples/intro/dropdown.cs b/docs/guides/int_framework/samples/intro/dropdown.cs new file mode 100644 index 000000000..2b0af477f --- /dev/null +++ b/docs/guides/int_framework/samples/intro/dropdown.cs @@ -0,0 +1,11 @@ +[ComponentInteraction("role_selection")] +public async Task RoleSelection(string[] selectedRoles) +{ + ... +} + +[ComponentInteraction("role_selection_*")] +public async Task RoleSelection(string id, string[] selectedRoles) +{ + ... +} diff --git a/docs/guides/int_framework/samples/intro/groupattribute.cs b/docs/guides/int_framework/samples/intro/groupattribute.cs new file mode 100644 index 000000000..86a492c31 --- /dev/null +++ b/docs/guides/int_framework/samples/intro/groupattribute.cs @@ -0,0 +1,21 @@ +[SlashCommand("blep", "Send a random adorable animal photo")] +public async Task Blep([Choice("Dog", "dog"), Choice("Cat", "cat"), Choice("Penguin", "penguin")] string animal) +{ + ... +} + +// In most cases, you can use an enum to replace the seperate choice attributes in a command. + +public enum Animal +{ + Cat, + Dog, + Penguin +} + +[SlashCommand("blep", "Send a random adorable animal photo")] +public async Task Blep(Animal animal) +{ + ... +} +``` diff --git a/docs/guides/int_framework/samples/intro/messagecommand.cs b/docs/guides/int_framework/samples/intro/messagecommand.cs new file mode 100644 index 000000000..5cb0c4b60 --- /dev/null +++ b/docs/guides/int_framework/samples/intro/messagecommand.cs @@ -0,0 +1,5 @@ +[MessageCommand("Bookmark")] +public async Task Bookmark(IMessage msg) +{ + ... +} diff --git a/docs/guides/int_framework/samples/intro/registering.cs b/docs/guides/int_framework/samples/intro/registering.cs new file mode 100644 index 000000000..f603df7bf --- /dev/null +++ b/docs/guides/int_framework/samples/intro/registering.cs @@ -0,0 +1,5 @@ +#if DEBUG + await interactionService.RegisterCommandsToGuildAsync(); +#else + await interactionService.RegisterCommandsGloballyAsync(); +#endif diff --git a/docs/guides/int_framework/samples/intro/slashcommand.cs b/docs/guides/int_framework/samples/intro/slashcommand.cs new file mode 100644 index 000000000..5f4f7fb0f --- /dev/null +++ b/docs/guides/int_framework/samples/intro/slashcommand.cs @@ -0,0 +1,5 @@ +[SlashCommand("echo", "Echo an input")] +public async Task Echo(string input) +{ + await RespondAsync(input); +} diff --git a/docs/guides/int_framework/samples/intro/summaryattribute.cs b/docs/guides/int_framework/samples/intro/summaryattribute.cs new file mode 100644 index 000000000..8a9b7d3e1 --- /dev/null +++ b/docs/guides/int_framework/samples/intro/summaryattribute.cs @@ -0,0 +1 @@ +[Summary(description: "this is a parameter description")] string input diff --git a/docs/guides/int_framework/samples/intro/usercommand.cs b/docs/guides/int_framework/samples/intro/usercommand.cs new file mode 100644 index 000000000..02c4a1e63 --- /dev/null +++ b/docs/guides/int_framework/samples/intro/usercommand.cs @@ -0,0 +1,5 @@ +[UserCommand("Say Hello")] +public async Task SayHello(IUser user) +{ + ... +} diff --git a/docs/guides/int_framework/samples/postexecution/error_review.cs b/docs/guides/int_framework/samples/postexecution/error_review.cs new file mode 100644 index 000000000..dd397b2c9 --- /dev/null +++ b/docs/guides/int_framework/samples/postexecution/error_review.cs @@ -0,0 +1,28 @@ +interactionService.SlashCommandExecuted += SlashCommandExecuted; + +async Task SlashCommandExecuted(SlashCommandInfo arg1, Discord.IInteractionContext arg2, IResult arg3) +{ + if (!arg3.IsSuccess) + { + switch (arg3.Error) + { + case InteractionCommandError.UnmetPrecondition: + await arg2.Interaction.RespondAsync($"Unmet Precondition: {arg3.ErrorReason}"); + break; + case InteractionCommandError.UnknownCommand: + await arg2.Interaction.RespondAsync("Unknown command"); + break; + case InteractionCommandError.BadArgs: + await arg2.Interaction.RespondAsync("Invalid number or arguments"); + break; + case InteractionCommandError.Exception: + await arg2.Interaction.RespondAsync("Command exception:{arg3.ErrorReason}"); + break; + case InteractionCommandError.Unsuccessful: + await arg2.Interaction.RespondAsync("Command could not be executed"); + break; + default: + break; + } + } +} diff --git a/docs/guides/commands/samples/preconditions/group_precondition.cs b/docs/guides/int_framework/samples/preconditions/group_precondition.cs similarity index 100% rename from docs/guides/commands/samples/preconditions/group_precondition.cs rename to docs/guides/int_framework/samples/preconditions/group_precondition.cs diff --git a/docs/guides/int_framework/samples/preconditions/precondition_usage.cs b/docs/guides/int_framework/samples/preconditions/precondition_usage.cs new file mode 100644 index 000000000..bea2918dc --- /dev/null +++ b/docs/guides/int_framework/samples/preconditions/precondition_usage.cs @@ -0,0 +1,3 @@ +[RequireOwner] +[SlashCommand("hi")] +public Task SayHiAsync() => RespondAsync("hello owner!"); diff --git a/docs/guides/int_framework/samples/typeconverters/enum_converter.cs b/docs/guides/int_framework/samples/typeconverters/enum_converter.cs new file mode 100644 index 000000000..6e1b9ded6 --- /dev/null +++ b/docs/guides/int_framework/samples/typeconverters/enum_converter.cs @@ -0,0 +1,30 @@ +internal sealed class EnumConverter : TypeConverter where T : struct, Enum +{ + public override ApplicationCommandOptionType GetDiscordType() => ApplicationCommandOptionType.String; + + public override Task ReadAsync(IInteractionCommandContext context, SocketSlashCommandDataOption option, IServiceProvider services) + { + if (Enum.TryParse((string)option.Value, out var result)) + return Task.FromResult(TypeConverterResult.FromSuccess(result)); + else + return Task.FromResult(TypeConverterResult.FromError(InteractionCommandError.ConvertFailed, $"Value {option.Value} cannot be converted to {nameof(T)}")); + } + + public override void Write(ApplicationCommandOptionProperties properties, IParameterInfo parameterInfo) + { + var names = Enum.GetNames(typeof(T)); + if (names.Length <= 25) + { + var choices = new List(); + + foreach (var name in names) + choices.Add(new ApplicationCommandOptionChoiceProperties + { + Name = name, + Value = name + }); + + properties.Choices = choices; + } + } +} diff --git a/docs/guides/int_framework/typeconverters.md b/docs/guides/int_framework/typeconverters.md new file mode 100644 index 000000000..96bdcb906 --- /dev/null +++ b/docs/guides/int_framework/typeconverters.md @@ -0,0 +1,118 @@ +--- +uid: Guides.IntFw.TypeConverters +title: Parameter Type Converters +--- + +# TypeConverters + +[TypeConverters] are responsible for registering command parameters to Discord and parsing the user inputs into method parameters. + +By default, TypeConverters for the following types are provided with @Discord.Interactions library. + +- Implementations of [IUser] +- Implementations of [IChannel] +- Implementations of [IRole] +- Implementations of [IMentionable] +- `string` +- `float`, `double`, `decimal` +- `bool` +- `char` +- `sbyte`, `byte` +- `int16`, `int32`, `int64` +- `uint16`, `uint32`, `uint64` +- `enum` +- `DateTime` +- `TimeSpan` + +## Creating TypeConverters + +Depending on your needs, there are two types of TypeConverters you can create: + +- Concrete type +- Generic type + +A valid converter must inherit [TypeConverter] base type. And override the abstract base methods. + +### CanConvertTo() Method + +This method is used by Interaction Service to search for alternative Type Converters. + +Interaction Services determines the most suitable [TypeConverter] for a parameter type in the following order: + +1. It searches for a [TypeConverter] that is registered to specifically target that parameter type +2. It searches for a [TypeConverter] that returns `true` when its `CanConvertTo()` method is invoked for thaty parameter type. +3. It searches for a generic `TypeConverter` with a matching type constraint. If there are more multiple matches, +the one whose type constraint is the most specialized will be chosen. + +> [!NOTE} +> Alternatively, you can use the generic variant (`TypeConverter`) of the +> [TypeConverter] base class which implements the following method body for `CanConvertTo()` method + +```csharp +public sealed override bool CanConvertTo (Type type) => + typeof(T).IsAssignableFrom(type); +``` + +### GetDiscordType() Method + +This method is used by [InteractionService] to determine the +[Discord Application Command Option type](https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-type) +of a parameter type. + +### ReadAsync() Method + +This method is used by [InteractionService] to parse the user input. +This method should return @Discord.Interactions.TypeConverterResult.FromSuccess* if the parsing operation is successful, +otherwise it should return @Discord.Interactions.TypeConverterResult.FromError* . +The inner logic of this method is totally up to you, +however you should avoid using long running code. + +### Write() Method + +This method is used to configure the **Discord Application Command Option** before it gets registered to Discord. +Command Option is configured by modifying the `ApplicationCommandOptionProperties` instance. + +> [!WARNING] +> The default parameter building pipeline is isolated and will not be disturbed by the [TypeConverter] workflow. +> But changes made in this method will override the values generated by the +> [InteractionService] for a **Discord Application Command Option**. + +## Example Enum TypeConverter + +[!code-csharp[Enum Converter](samples/typeconverters/enum_converter.cs)] + +> [!IMPORTANT] +> TypeConverters must be registered prior to module discovery. +> If Interaction Service encounters a parameter type that doesn't belong to any of the +> registered [TypeConverters] during this phase, it will throw an exception. + +## Concrete TypeConverters + +Registering Concrete TypeConverters are as simple as creating an instance of your custom converter and invoking `AddTypeConverter()` method. + +```csharp +interactionService.AddTypeConverter(new StringArrayConverter()); +``` + +## Generic TypeConverters + +To register a generic `TypeConverter`, you need to invoke the `AddGenericTypeConverter()` method of the Interaction Service class. +You need to pass the type of your `TypeConverter` and a target base type to this method. + +For instance, to register the previously mentioned enum converter the following can be used: + +```csharp +interactionService.AddGenericTypeConverter(typeof(EnumConverter<>)); +``` + +Interaction service checks if the target base type satisfies the type constraints of the Generic `TypeConverter` class. + +> [!NOTE] +> Dependencies of Generic TypeConverters are also resolved using the Dependency Injection pattern. + +[TypeConverter]: xref:Discord.Interactions.TypeConverter +[InteractionService]: xref:Discord.Interactions.InteractionService +[IChannel]: xref:Discord.IChannel +[IRole]: xref:Discord.IRole +[IUser]: xref:Discord.IUser +[IMentionable]: xref:Discord.IMentionable diff --git a/docs/guides/interactions/application-commands/01-getting-started.md b/docs/guides/interactions/application-commands/01-getting-started.md deleted file mode 100644 index fc8c8fe30..000000000 --- a/docs/guides/interactions/application-commands/01-getting-started.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -uid: Guides.SlashCommands.Intro -title: Introduction to slash commands ---- - - -# Getting started with application commands. - -Welcome! This guide will show you how to use application commands. - -## What is an application command? - -Application commands consist of three different types. Slash commands, context menu User commands and context menu Message commands. -Slash commands are made up of a name, description, and a block of options, which you can think of like arguments to a function. The name and description help users find your command among many others, and the options validate user input as they fill out your command. -Message and User commands are only a name, to the user. So try to make the name descriptive. They're accessed by right clicking (or long press, on mobile) a user or a message, respectively. - -All three varieties of application commands have both Global and Guild variants. Your global commands are available in every guild that adds your application. You can also make commands for a specific guild; they're only available in that guild. The User and Message commands are more limited in quantity than the slash commands. For specifics, check out their respective guide pages. - -An Interaction is the message that your application receives when a user uses a command. It includes the values that the user submitted, as well as some metadata about this particular instance of the command being used: the guild_id, channel_id, member and other fields. You can find all the values in our data models. - -## Authorizing your bot for application commands - -There is a new special OAuth2 scope for applications called `applications.commands`. In order to make Application Commands work within a guild, the guild must authorize your application with the `applications.commands` scope. The bot scope is not enough. - -Head over to your discord applications OAuth2 screen and make sure to select the `application.commands` scope. - -![OAuth2 scoping](slash-commands/images/oauth.png) - -From there you can then use the link to add your bot to a server. - -> [!NOTE] -> In order for users in your guild to use your slash commands, they need to have the "Use Slash Command" permission on the guild. diff --git a/docs/guides/interactions_framework/autocompleters.md b/docs/guides/interactions_framework/autocompleters.md deleted file mode 100644 index 9f84ace75..000000000 --- a/docs/guides/interactions_framework/autocompleters.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -uid: Guides.InteractionsFramework.Autocompleters -title: Autocompleters ---- - -# Autocompleters - -Autocompleters provide a similar pattern to TypeConverters. Autocompleters are cached, singleton services and they are used by the Interaction Service to handle Autocomplete Interations targeted to a specific Slash Command parameter. - -To start using Autocompleters, use the `[AutocompleteAttribute(Type autocompleterType)]` overload of the `[AutocompleteAttribute]`. This will dynamically link the parameter to the Autocompleter type. - -## Creating Autocompleters - -A valid Autocompleter must inherit `AutocompleteHandler` base type and implement all of its abstract methods. - -### GenerateSuggestionsAsync() - -Interactions Service uses this method to generate a response to a Autocomplete Interaction. This method should return `AutocompletionResult.FromSuccess(IEnumerable)` to display parameter sugesstions to the user. If there are no suggestions to be presented to the user, you have two options: - -1. Returning the parameterless `AutocompletionResult.FromSuccess()` will display "No options match your search." message to the user. -2. Returning `AutocompleteResult.FromError()` will make the Interaction Service not respond to the interation, consequently displaying the user "Loading options failed." message. - -## Resolving Autocompleter Dependencies - -Autocompleter dependencies are resolved using the same dependency injection pattern as the Interaction Modules. Property injection and constructor injection are both valid ways to get service dependencies. - -Because Autocompleters are constructed at service startup, class dependencies are resolved only once. If you need to access per-request dependencies you can use the IServiceProvider parameter of the `GenerateSuggestionsAsync()` method. diff --git a/docs/guides/interactions_framework/dependency-injection.md b/docs/guides/interactions_framework/dependency-injection.md deleted file mode 100644 index bed58e1c3..000000000 --- a/docs/guides/interactions_framework/dependency-injection.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -uid: Guides.InteractionsFramework.DependencyInjection -title: Dependency Injection ---- - -# Dependency Injection - -Interaction Service uses dependency injection to perform most of its operations. This way, you can access service dependencies throughout the framework. - -## Setup - -1. Create a `Microsoft.Extensions.DependencyInjection.ServiceCollection`. -2. Add the dependencies you wish to use in the modules. -3. Build a `IServiceProvider` using the `BuildServiceProvider()` method of the `ServiceCollection`. -4. Pass the `IServiceProvider` to `AddModulesAsync()`, `AddModuleAsync()` and `ExecuteAsync()` methods. - -## Accessing the Dependencies - -Services of a `IServiceProvider` can be accessed using *Contructor Injection* and *Property Injection*. - -Interaction Service will populate the constructor parameters using the provided `IServiceProvider`. Any public settable class Property will also be populated in the same manner. - -## Service Scopes - -Interaction Service has built-in support for scoped service types. Scoped lifetime services are instantiated once per command execution. Including the Preconditon checks, every module operation is executed within a single service scope (which is sepearate from the global service scope). - -> For more in-depth information about service lifetimes check out [Microsoft Docs](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-5.0#service-lifetimes-1). diff --git a/docs/guides/interactions_framework/intro.md b/docs/guides/interactions_framework/intro.md deleted file mode 100644 index f240eddd0..000000000 --- a/docs/guides/interactions_framework/intro.md +++ /dev/null @@ -1,360 +0,0 @@ ---- -uid: Guides.InteractionsFramework.Intro -title: Introduction to the Interaction Framework ---- - -# Getting Started - -Interaction Service provides an attribute based framework for creating Discord Interaction handlers. - -To start using the Interaction Service, you need to create a service instance. Optionally you can provide the `InterctionService` constructor with a `InteractionServiceConfig` to change the services behaviour to suit your needs. - -```csharp -... - -var commands = new InteractionService(discord); - -... -``` - -## Modules - -Attribute based Interaction handlers must be defined within a command module class. Command modules are responsible for executing the Interaction handlers and providing them with the necessary execution info and helper functions. - -Command modules are transient objects. A new module instance is created before a command execution starts then it will be disposed right after the method returns. - -Every module class must: - -- be public -- inherit `InteractionModuleBase` - -Optionally you can override the included : - -- OnModuleBuilding (executed after the module is built) -- BeforeExecute (executed before a command execution starts) -- AfterExecute (executed after a command execution concludes) - -methods to configure the modules behaviour. - -Every command module exposes a set of helper methods, namely: - -- `RespondAsync()` => Respond to the interaction -- `FollowupAsync()` => Create a followup message for an interaction -- `ReplyAsync()` => Send a message to the origin channel of the interaction -- `DeleteOriginalResponseAsync()` => Delete the original interaction response - -## Commands - -Valid **Interaction Commands** must comply with the following requirements: - -| | return type | max parameter count | allowed parameter types | attribute | -|-------------------------------|------------------------------|---------------------|-------------------------------|--------------------------| -|[Slash Command](#slash-commands)| `Task`/`Task` | 25 | any* | `[SlashCommand]` | -|[User Command](#user-commands) | `Task`/`Task` | 1 | Implementations of `IUser` | `[UserCommand]` | -|[Message Command](#message-commands)| `Task`/`Task` | 1 | Implementations of `IMessage` | `[MessageCommand]` | -|[Component Interaction Command](#component-interaction-commands)| `Task`/`Task` | inf | `string` or `string[]` | `[ComponentInteraction]` | -|[Autocomplete Command](#autocomplete-commands)| `Task`/`Task` | - | - | `[AutocompleteCommand]`| - -> [!NOTE] -> a `TypeConverter` that is capable of parsing type in question must be registered to the `InteractionService` instance. - -You should avoid using long running code in your command module. Depending on your setup, long running code may block the Gateway thread of your bot, interrupting its connection to Discord. - -### Slash Commands - -Slash Commands are created using the `[SlashCommandAttribute]`. Every Slash Command must declare a name and a description. You can check Discords **Application Command Naming Guidelines** [here](https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-naming). - -```csharp -[SlashCommand("echo", "Echo an input")] -public async Task Echo(string input) -{ - await RespondAsync(input); -} -``` - -#### Parameters - -Slash Commands can have up to 25 method parameters. You must name your parameters in accordance with [Discords Naming Guidelines](https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-naming). Interaction Service also features a pascal casing seperator for formatting parameter names with pascal casing into Discord compliant parameter names('parameterName' => 'parameter-name'). By default, your methods can feature the following parameter types: - -- Implementations of `IUser` -- Implementations of `IChannel`* -- Implementations of `IRole` -- Implementations of `IMentionable` -- `string` -- `float`, `double`, `decimal` -- `bool` -- `char` -- `sbyte`, `byte` -- `int16`, `int32`, `int64` -- `uint16`, `uint32`, `uint64` -- `enum` (Values are registered as multiple choice options and are enforced by Discord. Use `[HideAttribute]' on enum values to prevent them from getting registered.) -- `DateTime` -- `TimeSpan` - ---- - -**You can use more specialized implementations of `IChannel` to restrict the allowed channel types for a channel type option.* -| interface | Channel Type | -|---------------------|-------------------------------| -| `IStageChannel` | Stage Channels | -| `IVoiceChannel` | Voice Channels | -| `IDMChannel` | DM Channels | -| `IGroupChannel` | Group Channels | -| `ICategory Channel` | Category Channels | -| `INewsChannel` | News Channels | -| `IThreadChannel` | Public, Private, News Threads | -| `ITextChannel` | Text Channels | - ---- - -##### Optional Parameters - -Parameters with default values (ie. `int count = 0`) will be displayed as optional parameters on Discord Client. - -##### Parameter Summary - -By using the `[SummaryAttribute]` you can customize the displayed name and description of a parameter - -```csharp -[Summary(description: "this is a parameter description")] string input -``` - -##### Parameter Choices - -`[ChoiceAttribute]` can be used to add choices to a parameter. - -```csharp -[SlashCommand("blep", "Send a random adorable animal photo")] -public async Task Blep([Choice("Dog","dog"), Choice("Cat", "cat"), Choice("Penguin", "penguin")] string animal) -{ - ... -} -``` - -In most cases, instead of relying on this attribute, you should use an `Enum` to create multiple choice parameters. Ex. - -```csharp -public enum Animal -{ - Cat, - Dog, - Penguin -} - -[SlashCommand("blep", "Send a random adorable animal photo")] -public async Task Blep(Animal animal) -{ - ... -} -``` - -This Slash Command will be displayed exactly the same as the previous example. - -##### Channel Types - -Channel types for an `IChannel` parameter can also be restricted using the `[ChannelTypesAttribute]`. - -```csharp -[SlashCommand("name", "Description")] -public async Task Command([ChannelTypes(ChannelType.Stage, ChannelType.Text)]IChannel channel) -{ - ... -} -``` - -In this case, user can only input Stage Channels and Text Channels to this parameter. - -##### Autocomplete - -You can enable Autocomple Interactions for a Slash Command parameter using the `[AutocompleteAttribute]`. To handle the Autocomplete Interactions raised by this parameter you can either create [Autocomplete Commands](#autocomplete-commands) or you can opt to use the [Autocompleters Pattern](./autocompleters) - -##### Min/Max Value - -You can specify the permitted max/min value for a number type parameter using the `[MaxValueAttribute]` and `[MinValueAttribute]`. - -### User Commands - -A valid User Command must have the following structure: - -```csharp -[UserCommand("Say Hello")] -public async Task SayHello(IUser user) -{ - ... -} -``` - -User commands can only have one parameter and its type must be an implementation of `IUser`. - -### Message Commands - -A valid Message Command must have the following structure: - -```csharp -[MessageCommand("Bookmark")] -public async Task Bookmark(IMessage user) -{ - ... -} -``` - -Message commands can only have one parameter and its type must be an implementation of `IMessage`. - -### Component Interaction Commands - -Component Interaction Commands are used to handle interactions that originate from **Discord Message Component**s. This pattern is particularly useful if you will be reusing a set a **Custom ID**s. - -```csharp -[ComponentInteraction("custom_id")] -public async Task RoleSelection() -{ - ... -} -``` - -Component Interaction Commands support wild card matching, by default `*` character can be used to create a wild card pattern. Interaction Service will use lazy matching to capture the words corresponding to the wild card character. And the captured words will be passed on to the command method in the same order they were captured. - -*Ex.* - -If Interaction Service recieves a component interaction with **player:play,rickroll** custom id, `op` will be *play* and `name` will be *rickroll* - -```csharp -[ComponentInteraction("player:*,*")] -public async Task Play(string op, string name) -{ - ... -} -``` - -You may use as many wild card characters as you want. - -#### Select Menus - -Unlike button interactions, select menu interactions also contain the values of the selected menu items. In this case, you should structure your method to accept a string array. - -```csharp -[ComponentInteraction("role_selection")] -public async Task RoleSelection(string[] selectedRoles) -{ - ... -} -``` - - Wild card pattern can also be used to match select menu custom ids but remember that the array containing the select menu values should be the last parameter. - -```csharp -[ComponentInteraction("role_selection_*")] -public async Task RoleSelection(string id, string[] selectedRoles) -{ - ... -} -``` - -### Autocomplete Commands - -Autocomplete commands must be parameterless methods. A valid Autocomplete command must have the following structure: - -```csharp -[AutocompleteCommand("command_name", "parameter_name")] -public async Task Autocomplete() -{ - IEnumerable results; - - ... - - await (Context.Interaction as SocketAutocompleteInteraction).RespondAsync(results); -} -``` - -Alternatively, you can use the *Autocompleters* to simplify this workflow. - -## Interaction Context - -Every command module provides its commands with an execution context. This context property includes general information about the underlying interaction that triggered the command execution. The base command context. - -You can design your modules to work with different implementation types of `IInteractionContext`. To achieve this, make sure your module classes inherit from the generic variant of the `InteractionModuleBase`. - -> Context type must be consistent throughout the project, or you will run into issues during runtime. - -Interaction Service ships with 4 different kinds of `InteractionContext`s: - -1. InteractionContext: A bare-bones execution context consisting of only implementation netural interfaces -2. SocketInteractionContext: An execution context for use with `DiscordSocketClient`. Socket entities are exposed in this context without the need of casting them. -3. ShardedInteractionContext: `DiscordShardedClient` variant of the `SocketInteractionContext` -4. RestInteractionContext: An execution context designed to be used with a `DiscordRestClient` and webhook based interactions pattern - -You can create custom Interaction Contexts by implementing the `IInteracitonContext` interface. - -One problem with using the concrete type InteractionContexts is that you cannot access the information that is specific to different interaction types without casting. Concrete type interaction contexts are great for creating shared interaction modules but you can also use the generic variants of the built-in interaction contexts to create interaction specific interaction modules. - -Ex. -Message component interactions have access to a special method called `UpdateAsync()` to update the body of the method the interaction originated from. Normally this wouldn't be accessable without casting the `Context.Interaction`. - -```csharp -discordClient.ButtonExecuted += async (interaction) => -{ - var ctx = new SocketInteractionContext(discordClient, interaction); - await interactionService.ExecuteAsync(ctx, serviceProvider); -}; - -public class MessageComponentModule : InteractionModuleBase> -{ - [ComponentInteraction("custom_id")] - public async Command() - { - Context.Interaction.UpdateAsync(...); - } -} -``` - -## Loading Modules - -Interaction Service can automatically discover and load modules that inherit `InteractionModuleBase` from an `Assembly`. Call `InteractionService.AddModulesAsync()` to use this functionality. - -You can also manually add Interaction modules using the `InteractionService.AddModuleAsync()` method by providing the module type you want to load. - -## Resolving Module Dependencies - -Module dependencies are resolved using the Constructor Injection and Property Injection patterns. Meaning, the constructor parameters and public settable properties of a module will be assigned using the `IServiceProvider`. For more information on dependency injection, check out [Dependency Injection](./dependency-injection.md) - -## Module Groups - -Module groups allow you to create sub-commands and sub-commands groups. By nesting commands inside a module that is tagged with `[GroupAttribute]` you can create prefixed commands. - -Although creating nested module stuctures are allowed, you are not permitted to use more than 2 `[GroupAttribute]`s in module hierarchy. - -## Executing Commands - -Any of the following socket events can be used to execute commands: - -- InteractionCreated -- ButtonExecuted -- SelectMenuExecuted -- AutocompleteExecuted -- UserCommandExecuted -- MessageCommandExecuted - -Commands can be either executed on the gateway thread or on a seperate thread from the thread pool. This behaviour can be configured by changing the *RunMode* property of `InteractionServiceConfig` or by setting the *runMode* parameter of a command attribute. - -You can also configure the way `InteractionService` executes the commands. By default, commands are executed using `ConstructorInfo.Invoke()` to create module instances and `MethodInfo.Invoke()` method for executing the method bodies. By setting, `InteractionServiceConfig.UseCompiledLambda` to `true`, you can make `InteractionService` create module instances and execute commands using *Compiled Lambda* expressions. This cuts down on command execution time but it might add some memory overhead. - -Time it takes to create a module instance and execute a `Task.Delay(0)` method using the Reflection methods compared to Compiled Lambda expressions: - -| Method | Mean | Error | StdDev | -|----------------- |----------:|---------:|---------:| -| ReflectionInvoke | 225.93 ns | 4.522 ns | 7.040 ns | -| CompiledLambda | 48.79 ns | 0.981 ns | 1.276 ns | - -## Registering Commands to Discord - -Application commands loaded to the Interaction Service can be registered to Discord using a number of different methods. In most cases `RegisterCommandsGloballyAsync()` and `RegisterCommandsToGuildAsync()` are the methods to use. Command registration methods can only be used after the gateway client is ready or the rest client is logged in. - -In debug environment, since Global commands can take up to 1 hour to register/update, you should register your commands to a test guild for your changes to take effect immediately. You can use the preprocessor directives to create a simple logic for registering commands: - -```csharp -#if DEBUG - await interactionService.RegisterCommandsToGuildAsync(); -#else - await interactionService.RegisterCommandsGloballyAsync(); -#endif -``` diff --git a/docs/guides/interactions_framework/post_execution.md b/docs/guides/interactions_framework/post_execution.md deleted file mode 100644 index f34ba5cfe..000000000 --- a/docs/guides/interactions_framework/post_execution.md +++ /dev/null @@ -1,73 +0,0 @@ ---- -uid: Guides.InteractionsFramework.PostEx -title: Post-Execution ---- - -# Post-Execution Logic - -Interaction Service uses `IResult`s to provide information about the state of command execution. These can be used to log internal exceptions or provide some insight to the command user. - -If you are running your commands using `RunMode.Sync` these command results can be retrieved from the return value of `InteractionService.ExecuteCommandAsync()` method or by registering delegates to Interaction Service events. - -If you are using the `RunMode.Async` to run your commands, you must use the Interaction Service events to get the execution results. When using `RunMode.Async`, `InteractionService.ExecuteCommandAsync()` will always return a successful result. - -## Results - -Interaction Result come in a handful of different flavours: - -1. `AutocompletionResult`: returned by Autocompleters -2. `ExecuteResult`: contains the result of method body execution process -3. `PreconditionGroupResult`: returned by Precondition groups -4. `PreconditionResult`: returned by preconditions -5. `RuntimeResult`: a user implementable result for returning user defined results -6. `SearchResult`: returned by command lookup map -7. `TypeConverterResult`: returned by TypeConverters - -You can either use the `IResult.Error` property of an Interaction result or create type check for the afformentioned result types to branch out your post-execution logic to handle different situations. - -## CommandExecuted Events - -Every time a command gets executed, Interaction Service raises a *CommandExecuted event. These events can be used to create a post-execution pipeline. - -```csharp -interactionService.SlashCommandExecuted += SlashCommandExecuted; - -async Task SlashCommandExecuted (SlashCommandInfo arg1, Discord.IInteractionContext arg2, IResult arg3) - { - if (!arg3.IsSuccess) - { - switch (arg3.Error) - { - case InteractionCommandError.UnmetPrecondition: - await arg2.Interaction.RespondAsync($"Unmet Precondition: {arg3.ErrorReason}"); - break; - case InteractionCommandError.UnknownCommand: - await arg2.Interaction.RespondAsync("Unknown command"); - break; - case InteractionCommandError.BadArgs: - await arg2.Interaction.RespondAsync("Invalid number or arguments"); - break; - case InteractionCommandError.Exception: - await arg2.Interaction.RespondAsync($"Command exception:{arg3.ErrorReason}"); - break; - case InteractionCommandError.Unsuccessful: - await arg2.Interaction.RespondAsync("Command could not be executed"); - break; - default: - break; - } - } - } -``` - -## Log Event - -InteractionService regularly outputs information about the occuring events to keep the developer informed. - -## Runtime Result - -Interaction commands allow you to return `Task` to pass on additional information about the command execution process back to your post-execution logic. - -Custom `RuntimeResult` classes can be created by inheriting the base `RuntimeResult` class. - -If command execution process reaches the method body of the command and no exceptions are thrown during the execution of the method body, `RuntimeResult` returned by your command will be accessible by casting/type-checking the `IResult` parameter of the *CommandExecuted event delegate. diff --git a/docs/guides/interactions_framework/preconditions.md b/docs/guides/interactions_framework/preconditions.md deleted file mode 100644 index e9b5d73b6..000000000 --- a/docs/guides/interactions_framework/preconditions.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -uid: Guides.InteractionsFramework.Preconditions -title: Preconditions ---- - -# Preconditions - -Preconditions in Interaction Service work exactly the same as they do in ***Discord.Net.Commands***. For more information, check out [Preconditions](../commands/preconditions.md) \ No newline at end of file diff --git a/docs/guides/interactions_framework/typeconverters.md b/docs/guides/interactions_framework/typeconverters.md deleted file mode 100644 index 12ca7dab5..000000000 --- a/docs/guides/interactions_framework/typeconverters.md +++ /dev/null @@ -1,130 +0,0 @@ ---- -uid: Guides.InteractionsFramework.TypeConverters -title: Type Converters ---- - -# TypeConverters - -TypeConverters are responsible for registering command parameters to Discord and parsing the user inputs into method parameters. - -By default, TypeConverters for the following types are provided with `Discord.Net.Interactions` library. - -- Implementations of `IUser` -- Implementations of `IChannel` -- Implementations of `IRole` -- Implementations of `IMentionable` -- `string` -- `float`, `double`, `decimal` -- `bool` -- `char` -- `sbyte`, `byte` -- `int16`, `int32`, `int64` -- `uint16`, `uint32`, `uint64` -- `enum` -- `DateTime` -- `TimeSpan` - -## Creating TypeConverters - -Depending on your needs, there are two types of `TypeConverter`s you can create: - -- Concrete type -- Generic type - -A valid converter must inherit `TypeConverter` base type. And override the abstract base methods. - -### CanConvertTo() Method - -This method is used by Interaction Service to search for alternative Type Converters. - -Interaction Services determines the most suitable `TypeConverter` for a parameter type in the following order: - -1. It searches for a `TypeConverter` that is registered to specifically target that parameter type -2. It searches for a generic `TypeConverter` with a matching type constraint. If there are more multiple matches, the one whose type constraint is the most specialized will be chosen. -3. It searches for a `TypeConverter` that returns `true` when its `CanConvertTo()` method is invoked for thaty parameter type. - -> Alternatively, you can use the generic variant (`TypeConverter`) of the `TypeConverter` base class which implements the following method body for `CanConvertTo()` method - -```csharp -public sealed override bool CanConvertTo (Type type) => - typeof(T).IsAssignableFrom(type); -``` - -### GetDiscordType() Method - -This method is used by Interaction Service to determine the [Discord Application Command Option type](https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-type) of a parameter type. - -### ReadAsync() Method - -This method is used by Interaction Service to parse the user input. This method should return `TypeConverterResult.FromSuccess` if the parsing operation is successful, otherwise it should return `TypeConverterResult.FromError`. The inner logic of this method is totally up to you, however you should avoid using long running code. - -### Write() Method - -This method is used to configure the **Discord Application Command Option** before it gets registered to Discord. Command Option is configured by modifying the `ApplicationCommandOptionProperties` instance. - -The default parameter building pipeline is isolated and will not be disturbed by the `TypeConverter` workflow. But changes made in this method will override the values generated by the Interaction Service for a **Discord Application Command Option**. - ---- - -### Example Enum TypeConverter - -```csharp -internal sealed class EnumConverter : TypeConverter where T : struct, Enum -{ - public override ApplicationCommandOptionType GetDiscordType ( ) => ApplicationCommandOptionType.String; - - public override Task ReadAsync (IInteractionCommandContext context, SocketSlashCommandDataOption option, IServiceProvider services) - { - if (Enum.TryParse((string)option.Value, out var result)) - return Task.FromResult(TypeConverterResult.FromSuccess(result)); - else - return Task.FromResult(TypeConverterResult.FromError(InteractionCommandError.ConvertFailed, $"Value {option.Value} cannot be converted to {nameof(T)}")); - } - - public override void Write (ApplicationCommandOptionProperties properties, IParameterInfo parameterInfo) - { - var names = Enum.GetNames(typeof(T)); - if (names.Length <= 25) - { - var choices = new List(); - - foreach (var name in names) - choices.Add(new ApplicationCommandOptionChoiceProperties - { - Name = name, - Value = name - }); - - properties.Choices = choices; - } - } -} -``` - ---- - -## Registering TypeConverters - -> TypeConverters must be registered prior to module discovery. If Interaction Service encounters a parameter type that doesn't belong to any of the registered `TypeConverter`s during this phase, it will throw an exception. - -### Concrete TypeConverters - -Registering Concrete TypeConverters are as simple as creating an instance of your custom converter and invoking `AddTypeConverter()` method. - -```csharp -interactionService.AddTypeConverter(new StringArrayConverter()); -``` - -### Generic TypeConverters - -To register a generic TypeConverter, you need to invoke the `AddGenericTypeConverter()` method of the Interaction Service class. You need to pass the type of your `TypeConverter` and a target base type to this method. - -For instance, to register the previously mentioned [Example Enum Converter](#example-enum-converter) the following can be used: - -```csharp -interactionService.AddGenericTypeConverter(typeof(EnumConverter<>)); -``` - -Interaction service checks if the target base type satisfies the type constraints of the Generic TypeConverter class. - -> Dependencies of Generic TypeConverters are also resolved using the Dependency Injection pattern. diff --git a/docs/guides/introduction/intro.md b/docs/guides/introduction/intro.md index 0a4ca26e9..0bc1b90f6 100644 --- a/docs/guides/introduction/intro.md +++ b/docs/guides/introduction/intro.md @@ -23,7 +23,7 @@ in [our GitHub repository]. > Please note that you should *not* try to blindly copy paste > the code. The examples are meant to be a template or a guide. -[our GitHub repository]: https://github.com/RogueException/Discord.Net/tree/dev/samples +[our GitHub repository]: https://github.com/discord-net/Discord.Net/ [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/ @@ -44,8 +44,5 @@ resources to get you started. ## Still have questions? -Please visit us at `#dotnet_discord-net` on the [Discord API] server. -Describe the problem in details to us, what you've done, and, -if any, the problematic code uploaded onto [Hastebin](https://hastebin.com). - -[Discord API]: https://discord.gg/jkrBmQR \ No newline at end of file +Please visit us at our [Discord](https://discord.gg/dnet) server. +Describe the problem in details to us, what you've tried and what you need help with. diff --git a/docs/guides/commands/dependency-injection.md b/docs/guides/text_commands/dependency-injection.md similarity index 85% rename from docs/guides/commands/dependency-injection.md rename to docs/guides/text_commands/dependency-injection.md index 5dc5b02d2..3253643ef 100644 --- a/docs/guides/commands/dependency-injection.md +++ b/docs/guides/text_commands/dependency-injection.md @@ -1,14 +1,18 @@ --- -uid: Guides.Commands.DI +uid: Guides.TextCommands.DI title: Dependency Injection --- # Dependency Injection -The Command Service is bundled with a very barebone Dependency +The Text Command Service is bundled with a very barebone Dependency Injection service for your convenience. It is recommended that you use DI when writing your modules. +> [!WARNING] +> If you were brought here from the Interaction Service guides, +> make sure to replace all namespaces that imply `Discord.Commands` with `Discord.Interactions` + ## Setup 1. Create a @Microsoft.Extensions.DependencyInjection.ServiceCollection. @@ -44,4 +48,4 @@ manner. [!code-csharp[Injection Modules](samples/dependency-injection/dependency_module.cs)] [!code-csharp[Disallow Dependency Injection](samples/dependency-injection/dependency_module_noinject.cs)] -[DontInjectAttribute]: xref:Discord.Commands.DontInjectAttribute \ No newline at end of file +[DontInjectAttribute]: xref:Discord.Commands.DontInjectAttribute diff --git a/docs/guides/commands/intro.md b/docs/guides/text_commands/intro.md similarity index 97% rename from docs/guides/commands/intro.md rename to docs/guides/text_commands/intro.md index 14341a32b..6632c127a 100644 --- a/docs/guides/commands/intro.md +++ b/docs/guides/text_commands/intro.md @@ -1,9 +1,9 @@ --- -uid: Guides.Commands.Intro -title: Introduction to Command Service +uid: Guides.TextCommands.Intro +title: Introduction to the Chat Command Service --- -# The Command Service +# The Text Command Service [Discord.Commands](xref:Discord.Commands) provides an attribute-based command parser. @@ -187,7 +187,7 @@ service provider. ### Module Constructors -Modules are constructed using @Guides.Commands.DI. Any parameters +Modules are constructed using [Dependency Injection](xref:Guides.TextCommands.DI). Any parameters that are placed in the Module's constructor must be injected into an @System.IServiceProvider first. diff --git a/docs/guides/commands/namedarguments.md b/docs/guides/text_commands/namedarguments.md similarity index 98% rename from docs/guides/commands/namedarguments.md rename to docs/guides/text_commands/namedarguments.md index 890a8463f..18281d664 100644 --- a/docs/guides/commands/namedarguments.md +++ b/docs/guides/text_commands/namedarguments.md @@ -1,5 +1,5 @@ --- -uid: Guides.Commands.NamedArguments +uid: Guides.TextCommands.NamedArguments title: Named Arguments --- diff --git a/docs/guides/commands/post-execution.md b/docs/guides/text_commands/post-execution.md similarity index 97% rename from docs/guides/commands/post-execution.md rename to docs/guides/text_commands/post-execution.md index 782d256b2..49fe2f5f9 100644 --- a/docs/guides/commands/post-execution.md +++ b/docs/guides/text_commands/post-execution.md @@ -1,9 +1,9 @@ --- -uid: Guides.Commands.PostExecution +uid: Guides.TextCommands.PostExecution title: Post-command Execution Handling --- -# Post-execution Handling for Commands +# Post-execution Handling for Text Commands When developing commands, you may want to consider building a post-execution handling system so you can have finer control @@ -117,4 +117,4 @@ of the command. [CommandExecuted]: xref:Discord.Commands.CommandService.CommandExecuted [ExecuteAsync]: xref:Discord.Commands.CommandService.ExecuteAsync* [ExecuteResult]: xref:Discord.Commands.ExecuteResult -[Command Guide]: xref:Guides.Commands.Intro \ No newline at end of file +[Command Guide]: xref:Guides.TextCommands.Intro diff --git a/docs/guides/commands/preconditions.md b/docs/guides/text_commands/preconditions.md similarity index 98% rename from docs/guides/commands/preconditions.md rename to docs/guides/text_commands/preconditions.md index 8e8298b86..4be7ca2bb 100644 --- a/docs/guides/commands/preconditions.md +++ b/docs/guides/text_commands/preconditions.md @@ -1,5 +1,5 @@ --- -uid: Guides.Commands.Preconditions +uid: Guides.TextCommands.Preconditions title: Preconditions --- diff --git a/docs/guides/commands/samples/dependency-injection/dependency_map_setup.cs b/docs/guides/text_commands/samples/dependency-injection/dependency_map_setup.cs similarity index 100% rename from docs/guides/commands/samples/dependency-injection/dependency_map_setup.cs rename to docs/guides/text_commands/samples/dependency-injection/dependency_map_setup.cs diff --git a/docs/guides/commands/samples/dependency-injection/dependency_module.cs b/docs/guides/text_commands/samples/dependency-injection/dependency_module.cs similarity index 100% rename from docs/guides/commands/samples/dependency-injection/dependency_module.cs rename to docs/guides/text_commands/samples/dependency-injection/dependency_module.cs diff --git a/docs/guides/commands/samples/dependency-injection/dependency_module_noinject.cs b/docs/guides/text_commands/samples/dependency-injection/dependency_module_noinject.cs similarity index 100% rename from docs/guides/commands/samples/dependency-injection/dependency_module_noinject.cs rename to docs/guides/text_commands/samples/dependency-injection/dependency_module_noinject.cs diff --git a/docs/guides/commands/samples/intro/command_handler.cs b/docs/guides/text_commands/samples/intro/command_handler.cs similarity index 100% rename from docs/guides/commands/samples/intro/command_handler.cs rename to docs/guides/text_commands/samples/intro/command_handler.cs diff --git a/docs/guides/commands/samples/intro/empty-module.cs b/docs/guides/text_commands/samples/intro/empty-module.cs similarity index 100% rename from docs/guides/commands/samples/intro/empty-module.cs rename to docs/guides/text_commands/samples/intro/empty-module.cs diff --git a/docs/guides/commands/samples/intro/groups.cs b/docs/guides/text_commands/samples/intro/groups.cs similarity index 100% rename from docs/guides/commands/samples/intro/groups.cs rename to docs/guides/text_commands/samples/intro/groups.cs diff --git a/docs/guides/commands/samples/intro/module.cs b/docs/guides/text_commands/samples/intro/module.cs similarity index 100% rename from docs/guides/commands/samples/intro/module.cs rename to docs/guides/text_commands/samples/intro/module.cs diff --git a/docs/guides/commands/samples/post-execution/command_exception_log.cs b/docs/guides/text_commands/samples/post-execution/command_exception_log.cs similarity index 100% rename from docs/guides/commands/samples/post-execution/command_exception_log.cs rename to docs/guides/text_commands/samples/post-execution/command_exception_log.cs diff --git a/docs/guides/commands/samples/post-execution/command_executed_adv_demo.cs b/docs/guides/text_commands/samples/post-execution/command_executed_adv_demo.cs similarity index 100% rename from docs/guides/commands/samples/post-execution/command_executed_adv_demo.cs rename to docs/guides/text_commands/samples/post-execution/command_executed_adv_demo.cs diff --git a/docs/guides/commands/samples/post-execution/command_executed_demo.cs b/docs/guides/text_commands/samples/post-execution/command_executed_demo.cs similarity index 100% rename from docs/guides/commands/samples/post-execution/command_executed_demo.cs rename to docs/guides/text_commands/samples/post-execution/command_executed_demo.cs diff --git a/docs/guides/commands/samples/post-execution/customresult_base.cs b/docs/guides/text_commands/samples/post-execution/customresult_base.cs similarity index 100% rename from docs/guides/commands/samples/post-execution/customresult_base.cs rename to docs/guides/text_commands/samples/post-execution/customresult_base.cs diff --git a/docs/guides/commands/samples/post-execution/customresult_extended.cs b/docs/guides/text_commands/samples/post-execution/customresult_extended.cs similarity index 100% rename from docs/guides/commands/samples/post-execution/customresult_extended.cs rename to docs/guides/text_commands/samples/post-execution/customresult_extended.cs diff --git a/docs/guides/commands/samples/post-execution/customresult_usage.cs b/docs/guides/text_commands/samples/post-execution/customresult_usage.cs similarity index 100% rename from docs/guides/commands/samples/post-execution/customresult_usage.cs rename to docs/guides/text_commands/samples/post-execution/customresult_usage.cs diff --git a/docs/guides/commands/samples/post-execution/post-execution_basic.cs b/docs/guides/text_commands/samples/post-execution/post-execution_basic.cs similarity index 100% rename from docs/guides/commands/samples/post-execution/post-execution_basic.cs rename to docs/guides/text_commands/samples/post-execution/post-execution_basic.cs diff --git a/docs/guides/text_commands/samples/preconditions/group_precondition.cs b/docs/guides/text_commands/samples/preconditions/group_precondition.cs new file mode 100644 index 000000000..bae102b9a --- /dev/null +++ b/docs/guides/text_commands/samples/preconditions/group_precondition.cs @@ -0,0 +1,9 @@ +// The following example only requires the user to either have the +// Administrator permission in this guild or own the bot application. +[RequireUserPermission(GuildPermission.Administrator, Group = "Permission")] +[RequireOwner(Group = "Permission")] +public class AdminModule : ModuleBase +{ + [Command("ban")] + public Task BanAsync(IUser user) => Context.Guild.AddBanAsync(user); +} \ No newline at end of file diff --git a/docs/guides/commands/samples/preconditions/precondition_usage.cs b/docs/guides/text_commands/samples/preconditions/precondition_usage.cs similarity index 100% rename from docs/guides/commands/samples/preconditions/precondition_usage.cs rename to docs/guides/text_commands/samples/preconditions/precondition_usage.cs diff --git a/docs/guides/commands/samples/preconditions/require_role.cs b/docs/guides/text_commands/samples/preconditions/require_role.cs similarity index 100% rename from docs/guides/commands/samples/preconditions/require_role.cs rename to docs/guides/text_commands/samples/preconditions/require_role.cs diff --git a/docs/guides/commands/samples/typereaders/typereader-register.cs b/docs/guides/text_commands/samples/typereaders/typereader-register.cs similarity index 100% rename from docs/guides/commands/samples/typereaders/typereader-register.cs rename to docs/guides/text_commands/samples/typereaders/typereader-register.cs diff --git a/docs/guides/commands/samples/typereaders/typereader.cs b/docs/guides/text_commands/samples/typereaders/typereader.cs similarity index 100% rename from docs/guides/commands/samples/typereaders/typereader.cs rename to docs/guides/text_commands/samples/typereaders/typereader.cs diff --git a/docs/guides/commands/typereaders.md b/docs/guides/text_commands/typereaders.md similarity index 97% rename from docs/guides/commands/typereaders.md rename to docs/guides/text_commands/typereaders.md index f942c9341..bf911dac7 100644 --- a/docs/guides/commands/typereaders.md +++ b/docs/guides/text_commands/typereaders.md @@ -1,5 +1,5 @@ --- -uid: Guides.Commands.TypeReaders +uid: Guides.TextCommands.TypeReaders title: Type Readers --- @@ -67,4 +67,4 @@ To register a TypeReader, invoke [CommandService.AddTypeReader]. ### Example - Adding a Type Reader -[!code-csharp[Adding TypeReaders](samples/typereaders/typereader-register.cs)] \ No newline at end of file +[!code-csharp[Adding TypeReaders](samples/typereaders/typereader-register.cs)] diff --git a/docs/guides/toc.yml b/docs/guides/toc.yml index fba188b78..cf4ea5516 100644 --- a/docs/guides/toc.yml +++ b/docs/guides/toc.yml @@ -1,30 +1,57 @@ - name: Introduction topicUid: Guides.Introduction -- name: "Working with Guild Events" +- name: V2 to V3 Guide + topicUid: Guides.V2V3Guide +- name: Getting Started items: - - name: Introduction - topicUid: Guides.GuildEvents.Intro - - name: Creating Events - topicUid: Guides.GuildEvents.Creating - - name: Getting Event Users - topicUid: Guides.GuildEvents.GettingUsers - - name: Modifying Events - topicUid: Guides.GuildEvents.Modifying -- name: Working with Commands + - name: Installation + topicUid: Guides.GettingStarted.Installation + items: + - name: Nightly builds + topicUid: Guides.GettingStarted.Installation.Labs + - name: Your First Bot + topicUid: Guides.GettingStarted.FirstBot + - name: Terminology + topicUid: Guides.GettingStarted.Terminology +- name: Basic Concepts + items: + - name: Logging Data + topicUid: Guides.Concepts.Logging + - name: Working with Events + topicUid: Guides.Concepts.Events + - name: Managing Connections + topicUid: Guides.Concepts.ManageConnections + - name: Entities + topicUid: Guides.Concepts.Entities +- name: Working with Text-based Commands items: - name: Introduction - topicUid: Guides.Commands.Intro + topicUid: Guides.TextCommands.Intro - name: TypeReaders - topicUid: Guides.Commands.TypeReaders + topicUid: Guides.TextCommands.TypeReaders - name: Named Arguments - topicUid: Guides.Commands.NamedArguments + topicUid: Guides.TextCommands.NamedArguments - name: Preconditions - topicUid: Guides.Commands.Preconditions + topicUid: Guides.TextCommands.Preconditions - name: Dependency Injection - topicUid: Guides.Commands.DI + topicUid: Guides.TextCommands.DI - name: Post-execution Handling - topicUid: Guides.Commands.PostExecution -- name: Working with Slash Commands + topicUid: Guides.TextCommands.PostExecution +- name: Working with the Interaction Framework + items: + - name: Introduction + topicUid: Guides.IntFw.Intro + - name: Auto-Completion + topicUid: Guides.IntFw.AutoCompletion + - name: TypeConverters + topicUid: Guides.IntFw.TypeConverters + - name: Preconditions + topicUid: Guides.IntFw.Preconditions + - name: Dependency Injection + topicUid: Guides.IntFw.DI + - name: Post-execution Handling + topicUid: Guides.IntFw.PostExecution +- name: Slash Command Basics items: - name: Introduction topicUid: Guides.SlashCommands.Intro @@ -42,16 +69,16 @@ topicUid: Guides.SlashCommands.Choices - name: Slash commands Bulk Overwrites topicUid: Guides.SlashCommands.BulkOverwrite -- name: Working with Context commands +- name: Context Command Basics items: - name: Creating Context Commands topicUid: Guides.ContextCommands.Creating - name: Receiving Context Commands topicUid: Guides.ContextCommands.Reveiving -- name: Working with Message Components +- name: Message Component Basics items: - - name: Getting started - topicUid: Guides.MessageComponents.GettingStarted + - name: Introduction + topicUid: Guides.MessageComponents.Intro - name: Responding to Components topicUid: Guides.MessageComponents.Responding - name: Buttons in depth @@ -60,14 +87,16 @@ topicUid: Guides.MessageComponents.SelectMenus - name: Advanced Concepts topicUid: Guides.MessageComponents.Advanced -- name: Interaction Framework +- name: Guild Events items: - - name: Getting started - topicUid: Guides.InteractionsFramework.Intro - - name: Dependency Injection - topicUid: Guides.Commands.DI - - name: Post-execution Handling - topicUid: Guides.Commands.PostExecution + - name: Introduction + topicUid: Guides.GuildEvents.Intro + - name: Creating Events + topicUid: Guides.GuildEvents.Creating + - name: Getting Event Users + topicUid: Guides.GuildEvents.GettingUsers + - name: Modifying Events + topicUid: Guides.GuildEvents.Modifying - name: Emoji topicUid: Guides.Emoji - name: Voice diff --git a/docs/guides/v2_v3_guide/v2_to_v3_guide.md b/docs/guides/v2_v3_guide/v2_to_v3_guide.md new file mode 100644 index 000000000..2bd914e17 --- /dev/null +++ b/docs/guides/v2_v3_guide/v2_to_v3_guide.md @@ -0,0 +1,30 @@ +--- +uid: Guides.V2V3Guide +title: V2 -> V3 Guide +--- + +# V2 to V3 Guide + +V3 is designed to be a more feature complete, more reliable, and more flexible library than any previous version. + +Below are the most notable breaking changes that you would need to update your code to work with V3. + +### GuildMemberUpdated Event + +The guild member updated event now passes a `Cacheable` for the first argument instead of a normal `SocketGuildUser`. This new cacheable type allows you to download a `RestGuildUser` if the user isn't cached. + +### ReactionAdded Event + +The reaction added event has been changed to have both parameters cacheable. This allows you to download the channel and message if they aren't cached instead of them being null. + +### UserIsTyping Event + +THe user is typing event has been changed to have both parameters cacheable. This allows you to download the user and channel if they aren't cached instead of them being null. + +### Presence + +There is a new event called `PresenceUpdated` that is called when a user's presence changes, instead of `GuildMemberUpdated` or `UserUpdated`. If your code relied on these events to get presence data then you need to update it to work with the new event. + +## Migrating your commands to slash command + +The new InteractionService was designed to act like the previous service for text-based commands. Your pre-existing code will continue to work, but you will need to migrate your modules and response functions to use the new InteractionService methods. Docs on this can be found [here](xref:Guides.IntFw.Intro) diff --git a/docs/index.md b/docs/index.md index 1d0f5aaf7..8a87963ea 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,17 +3,17 @@ uid: Root.Landing title: Home --- -# Discord.Net Documentation +# Discord.NET Documentation -[![GitHub](https://img.shields.io/github/last-commit/discord-net/discord.net?style=plastic)](https://github.com/discord-net/Discord.Net) +[![GitHub](https://img.shields.io/github/last-commit/discord-net/Discord.Net?style=plastic)](https://github.com/discord-net/Discord.Net) [![NuGet](https://img.shields.io/nuget/vpre/Discord.Net.svg?maxAge=2592000?style=plastic)](https://www.nuget.org/packages/Discord.Net) [![MyGet](https://img.shields.io/myget/discord-net/vpre/Discord.Net.svg)](https://www.myget.org/feed/Packages/discord-net) [![Build Status](https://dev.azure.com/discord-net/Discord.Net/_apis/build/status/discord-net.Discord.Net?branchName=dev)](https://dev.azure.com/discord-net/Discord.Net/_build/latest?definitionId=1&branchName=dev) -[![Discord](https://discord.com/api/guilds/81384788765712384/widget.png)](https://discord.gg/jkrBmQR) +[![Discord](https://discord.com/api/guilds/81384788765712384/widget.png)](https://discord.gg/dnet) -## What is Discord.Net? +## What is Discord.NET? Discord.Net is an asynchronous, multi-platform .NET Library used to interface with the [Discord API](https://discord.com/). @@ -26,3 +26,57 @@ If this is your first time using Discord.Net, you should refer to the More experienced users might want to refer to the [API Documentation](xref:API.Docs) for a breakdown of the individual objects in the library. + +## Nightlies + +Nightlies are builds of Discord.NET that are still in an experimental phase, and have not been released. +These are not included in the main repository, and are instead taken over by [Discord.NET Labs]. + +Discord.NET Labs is an experimental fork of Discord.NET that implements the newest discord features +for testing and development to eventually get merged into Discord.NET. + +[Installing Discord.NET Labs](xref:Guides.GettingStarted.Installation.Labs) + +[discord.net labs]: https://github.com/Discord-Net-Labs/Discord.Net-Labs + +## Questions? + +Frequently asked questions are covered in the +FAQ. Read it thoroughly because most common questions are already answered there. + +If you still have unanswered questions after reading the [FAQ](xref:FAQ.Basics.GetStarted), further support is available on +[Discord](https://discord.gg/dnet). + +## Commonly used features + +#### Interaction Framework + +A counterpart to staple command service of Discord.NET, the Interaction Framework implements the same +feature-rich structure to register & handle interactions like Slash commands & buttons. + +- Read about the Interaction Framework + [here](xref:Guides.IntFw.Intro) + +#### Slash Commands + +Slash commands are purposed to take over the normal prefixed commands in Discord and comes with good functionality to serve as a replacement. +Being interactions, they are handled as SocketInteractions. Creating and receiving slashcommands is covered below. + +- Find out more about slash commands in the + [Slash Command Guides](xref:Guides.SlashCommands.Intro) + +#### Context Message & User Ccommands + +These commands can be pointed at messages and users, in custom application tabs. +Being interactions as well, they are able to be handled just like slash commands. They do not have options however. + +- Learn how to create and handle these commands in the + [Context Command Guides](xref:Guides.ContextCommands.Creating) + +#### Message Components + +Components of a message such as buttons and dropdowns, which can be interacted with and responded to. +Message components can be set in rows and multiple can exist on a single message! + +- Explanation on how to add & respond to message components can be found in the + [Message Component Guides](xref:Guides.MessageComponents.Intro) diff --git a/docs/toc.yml b/docs/toc.yml index bea010c5a..810995383 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -8,4 +8,4 @@ href: api/ topicUid: API.Docs - name: Changelog - topicHref: ../CHANGELOG.md \ No newline at end of file + topicHref: ../CHANGELOG.md diff --git a/src/Discord.Net.Commands/ModuleBase.cs b/src/Discord.Net.Commands/ModuleBase.cs index 3eddc11d2..5008cca35 100644 --- a/src/Discord.Net.Commands/ModuleBase.cs +++ b/src/Discord.Net.Commands/ModuleBase.cs @@ -38,12 +38,12 @@ namespace Discord.Commands /// /// The request options for this request. /// The message references to be included. Used to reply to specific messages. - /// The message components to be included with this message. Used for interactions. + /// The message components to be included with this message. Used for interactions. /// A collection of stickers to send with the file. /// A array of s to send with this response. Max 10. - protected virtual async Task ReplyAsync(string message = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) + protected virtual async Task ReplyAsync(string message = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) { - return await Context.Channel.SendMessageAsync(message, isTTS, embed, options, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + return await Context.Channel.SendMessageAsync(message, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); } /// /// The method to execute before executing the command. diff --git a/src/Discord.Net.Core/DiscordErrorCode.cs b/src/Discord.Net.Core/DiscordErrorCode.cs index 5a5223b93..61516a0ff 100644 --- a/src/Discord.Net.Core/DiscordErrorCode.cs +++ b/src/Discord.Net.Core/DiscordErrorCode.cs @@ -65,6 +65,7 @@ namespace Discord OnlyOwnerAction = 20018, AnnouncementEditRatelimit = 20022, ChannelWriteRatelimit = 20028, + WriteRatelimitReached = 20029, WordsNotAllowed = 20031, GuildPremiumTooLow = 20035, #endregion @@ -139,6 +140,7 @@ namespace Discord FileUploadTooBig = 50045, InvalidFileUpload = 50046, CannotSelfRedeemGift = 50054, + InvalidGuild = 50055, PaymentSourceRequiredForGift = 50070, CannotDeleteRequiredCommunityChannel = 50074, InvalidSticker = 50081, diff --git a/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs index 87dfb3460..00ec38746 100644 --- a/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs @@ -28,14 +28,14 @@ namespace Discord /// If null, all mentioned roles and users will be notified. /// /// The message references to be included. Used to reply to specific messages. - /// The message components to be included with this message. Used for interactions. + /// The message components to be included with this message. Used for interactions. /// A collection of stickers to send with the message. /// A array of s to send with this response. Max 10. /// /// A task that represents an asynchronous send operation for delivering the message. The task result /// contains the sent message. /// - Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null); + Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null); /// /// Sends a file to this message channel with an optional caption. /// @@ -68,14 +68,14 @@ namespace Discord /// If null, all mentioned roles and users will be notified. /// /// The message references to be included. Used to reply to specific messages. - /// The message components to be included with this message. Used for interactions. + /// The message components to be included with this message. Used for interactions. /// A collection of stickers to send with the file. /// A array of s to send with this response. Max 10. /// /// A task that represents an asynchronous send operation for delivering the message. The task result /// contains the sent message. /// - Task SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null); + Task SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null); /// /// Sends a file to this message channel with an optional caption. /// @@ -105,14 +105,14 @@ namespace Discord /// If null, all mentioned roles and users will be notified. /// /// The message references to be included. Used to reply to specific messages. - /// The message components to be included with this message. Used for interactions. + /// The message components to be included with this message. Used for interactions. /// A collection of stickers to send with the file. /// A array of s to send with this response. Max 10. /// /// A task that represents an asynchronous send operation for delivering the message. The task result /// contains the sent message. /// - Task SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null); + Task SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null); /// /// Sends a file to this message channel with an optional caption. /// @@ -134,14 +134,14 @@ namespace Discord /// If null, all mentioned roles and users will be notified. /// /// The message references to be included. Used to reply to specific messages. - /// The message components to be included with this message. Used for interactions. + /// The message components to be included with this message. Used for interactions. /// A collection of stickers to send with the file. /// A array of s to send with this response. Max 10. /// /// A task that represents an asynchronous send operation for delivering the message. The task result /// contains the sent message. /// - Task SendFileAsync(FileAttachment attachment, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null); + Task SendFileAsync(FileAttachment attachment, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null); /// /// Sends a collection of files to this message channel. /// @@ -163,14 +163,14 @@ namespace Discord /// If null, all mentioned roles and users will be notified. /// /// The message references to be included. Used to reply to specific messages. - /// The message components to be included with this message. Used for interactions. + /// The message components to be included with this message. Used for interactions. /// A collection of stickers to send with the file. /// A array of s to send with this response. Max 10. /// /// A task that represents an asynchronous send operation for delivering the message. The task result /// contains the sent message. /// - Task SendFilesAsync(IEnumerable attachments, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null); + Task SendFilesAsync(IEnumerable attachments, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null); /// /// Gets a message from this message channel. diff --git a/src/Discord.Net.Core/Entities/Emotes/Emoji.cs b/src/Discord.Net.Core/Entities/Emotes/Emoji.cs index 15c20148e..2bf17d645 100644 --- a/src/Discord.Net.Core/Entities/Emotes/Emoji.cs +++ b/src/Discord.Net.Core/Entities/Emotes/Emoji.cs @@ -675,6 +675,7 @@ namespace Discord [":coconut:"] = "\uD83E\uDD65", [":coffee:"] = "☕", [":coffin:"] = "⚰️", + [":coin:"] = "\uD83E\uDE99", [":cold_face:"] = "\uD83E\uDD76", [":cold_sweat:"] = "\uD83D\uDE30", [":comet:"] = "☄️", diff --git a/src/Discord.Net.Core/Entities/Interactions/IApplicationCommandOption.cs b/src/Discord.Net.Core/Entities/Interactions/IApplicationCommandOption.cs index 440c4bd6b..72554fc98 100644 --- a/src/Discord.Net.Core/Entities/Interactions/IApplicationCommandOption.cs +++ b/src/Discord.Net.Core/Entities/Interactions/IApplicationCommandOption.cs @@ -32,6 +32,11 @@ namespace Discord /// bool? IsRequired { get; } + /// + /// Gets whether or not the option has autocomplete enabled. + /// + bool? IsAutocomplete { get; } + /// /// Gets the smallest number value the user can input. /// diff --git a/src/Discord.Net.Core/Entities/Roles/RoleProperties.cs b/src/Discord.Net.Core/Entities/Roles/RoleProperties.cs index 93cda8d5b..b6399c054 100644 --- a/src/Discord.Net.Core/Entities/Roles/RoleProperties.cs +++ b/src/Discord.Net.Core/Entities/Roles/RoleProperties.cs @@ -1,3 +1,5 @@ +using System; + namespace Discord { /// @@ -50,11 +52,24 @@ namespace Discord /// This value may not be set if the role is an @everyone role. /// public Optional Hoist { get; set; } - /// /// Gets or sets the icon of the role. /// - public Optional Icon { get; set; } + /// + /// This value cannot be set at the same time as Emoji, as they are both exclusive. + /// + /// Setting an Icon will override a currently existing Emoji if present. + /// + public Optional Icon { get; set; } + /// + /// Gets or sets the unicode emoji of the role. + /// + /// + /// This value cannot be set at the same time as Icon, as they are both exclusive. + /// + /// Setting an Emoji will override a currently existing Icon if present. + /// + public Optional Emoji { get; set; } /// /// Gets or sets whether or not this role can be mentioned. /// diff --git a/src/Discord.Net.Core/Extensions/UserExtensions.cs b/src/Discord.Net.Core/Extensions/UserExtensions.cs index e268eae84..ce914170d 100644 --- a/src/Discord.Net.Core/Extensions/UserExtensions.cs +++ b/src/Discord.Net.Core/Extensions/UserExtensions.cs @@ -32,7 +32,7 @@ namespace Discord /// Specifies if notifications are sent for mentioned users and roles in the message . /// If null, all mentioned roles and users will be notified. /// - /// The message components to be included with this message. Used for interactions. + /// The message components to be included with this message. Used for interactions. /// A array of s to send with this response. Max 10. /// /// A task that represents the asynchronous send operation. The task result contains the sent message. @@ -43,10 +43,10 @@ namespace Discord Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, - MessageComponent component = null, + MessageComponent components = null, Embed[] embeds = null) { - return await (await user.CreateDMChannelAsync().ConfigureAwait(false)).SendMessageAsync(text, isTTS, embed, options, allowedMentions, component: component, embeds: embeds).ConfigureAwait(false); + return await (await user.CreateDMChannelAsync().ConfigureAwait(false)).SendMessageAsync(text, isTTS, embed, options, allowedMentions, components: components, embeds: embeds).ConfigureAwait(false); } /// @@ -85,7 +85,7 @@ namespace Discord /// Whether the message should be read aloud by Discord or not. /// The to be sent. /// The options to be used when sending the request. - /// The message component to be included with this message. Used for interactions. + /// The message component to be included with this message. Used for interactions. /// A array of s to send with this response. Max 10. /// /// A task that represents an asynchronous send operation for delivering the message. The task result @@ -98,10 +98,10 @@ namespace Discord bool isTTS = false, Embed embed = null, RequestOptions options = null, - MessageComponent component = null, + MessageComponent components = null, Embed[] embeds = null) { - return await (await user.CreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(stream, filename, text, isTTS, embed, options, component: component, embeds: embeds).ConfigureAwait(false); + return await (await user.CreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(stream, filename, text, isTTS, embed, options, components: components, embeds: embeds).ConfigureAwait(false); } /// @@ -145,7 +145,7 @@ namespace Discord /// Whether the message should be read aloud by Discord or not. /// The to be sent. /// The options to be used when sending the request. - /// The message component to be included with this message. Used for interactions. + /// The message component to be included with this message. Used for interactions. /// A array of s to send with this response. Max 10. /// /// A task that represents an asynchronous send operation for delivering the message. The task result @@ -157,10 +157,10 @@ namespace Discord bool isTTS = false, Embed embed = null, RequestOptions options = null, - MessageComponent component = null, + MessageComponent components = null, Embed[] embeds = null) { - return await (await user.CreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(filePath, text, isTTS, embed, options, component: component, embeds: embeds).ConfigureAwait(false); + return await (await user.CreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(filePath, text, isTTS, embed, options, components: components, embeds: embeds).ConfigureAwait(false); } /// diff --git a/src/Discord.Net.Interactions/Info/Commands/CommandInfo.cs b/src/Discord.Net.Interactions/Info/Commands/CommandInfo.cs index f9fdb6e38..36cc6cbd8 100644 --- a/src/Discord.Net.Interactions/Info/Commands/CommandInfo.cs +++ b/src/Discord.Net.Interactions/Info/Commands/CommandInfo.cs @@ -241,7 +241,7 @@ namespace Discord.Interactions } builder.AppendFormat(" {0}", Name); - return builder.ToString(); + return builder.ToString().Trim(); } } } diff --git a/src/Discord.Net.Interactions/InteractionModuleBase.cs b/src/Discord.Net.Interactions/InteractionModuleBase.cs index f0e50da8d..56ef11d05 100644 --- a/src/Discord.Net.Interactions/InteractionModuleBase.cs +++ b/src/Discord.Net.Interactions/InteractionModuleBase.cs @@ -35,18 +35,18 @@ namespace Discord.Interactions /// protected virtual async Task RespondAsync (string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, - AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) => - await Context.Interaction.RespondAsync(text, embeds, isTTS, ephemeral, allowedMentions, component, embed, options).ConfigureAwait(false); + AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent components = null, Embed embed = null) => + await Context.Interaction.RespondAsync(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false); /// protected virtual async Task FollowupAsync (string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, - AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) => - await Context.Interaction.FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, component, embed, options).ConfigureAwait(false); + AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent components = null, Embed embed = null) => + await Context.Interaction.FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false); /// protected virtual async Task ReplyAsync (string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, - AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null) => - await Context.Channel.SendMessageAsync(text, false, embed, options, allowedMentions, messageReference, component).ConfigureAwait(false); + AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null) => + await Context.Channel.SendMessageAsync(text, false, embed, options, allowedMentions, messageReference, components).ConfigureAwait(false); /// protected virtual async Task DeleteOriginalResponseAsync ( ) diff --git a/src/Discord.Net.Interactions/InteractionService.cs b/src/Discord.Net.Interactions/InteractionService.cs index 6c2a70f16..d7192129d 100644 --- a/src/Discord.Net.Interactions/InteractionService.cs +++ b/src/Discord.Net.Interactions/InteractionService.cs @@ -67,7 +67,7 @@ namespace Discord.Interactions internal readonly LogManager _logManager; internal readonly Func _getRestClient; - internal readonly bool _throwOnError, _deleteUnkownSlashCommandAck, _useCompiledLambda, _enableAutocompleteHandlers; + internal readonly bool _throwOnError, _useCompiledLambda, _enableAutocompleteHandlers; internal readonly string _wildCardExp; internal readonly RunMode _runMode; internal readonly RestResponseCallback _restResponseCallback; @@ -153,7 +153,6 @@ namespace Discord.Interactions throw new InvalidOperationException($"RunMode cannot be set to {RunMode.Default}"); _throwOnError = config.ThrowOnError; - _deleteUnkownSlashCommandAck = config.DeleteUnknownSlashCommandAck; _wildCardExp = config.WildCardExpression; _useCompiledLambda = config.UseCompiledLambda; _enableAutocompleteHandlers = config.EnableAutocompleteHandlers; @@ -620,12 +619,6 @@ namespace Discord.Interactions { await _cmdLogger.DebugAsync($"Unknown slash command, skipping execution ({string.Join(" ", keywords).ToUpper()})"); - if (_deleteUnkownSlashCommandAck) - { - var response = await context.Interaction.GetOriginalResponseAsync().ConfigureAwait(false); - await response.DeleteAsync().ConfigureAwait(false); - } - await _slashCommandExecutedEvent.InvokeAsync(null, context, result).ConfigureAwait(false); return result; } diff --git a/src/Discord.Net.Interactions/InteractionServiceConfig.cs b/src/Discord.Net.Interactions/InteractionServiceConfig.cs index 8e495a5ca..e5ca5b9ec 100644 --- a/src/Discord.Net.Interactions/InteractionServiceConfig.cs +++ b/src/Discord.Net.Interactions/InteractionServiceConfig.cs @@ -33,11 +33,6 @@ namespace Discord.Interactions /// public string WildCardExpression { get; set; } - /// - /// Gets or sets the option to delete Slash Command acknowledgements if no Slash Command handler is found in the . - /// - public bool DeleteUnknownSlashCommandAck { get; set; } = true; - /// /// Gets or sets the option to use compiled lambda expressions to create module instances and execute commands. This method improves performance at the cost of memory. /// diff --git a/src/Discord.Net.Interactions/RestInteractionModuleBase.cs b/src/Discord.Net.Interactions/RestInteractionModuleBase.cs index ee1ce229e..fe184fc9a 100644 --- a/src/Discord.Net.Interactions/RestInteractionModuleBase.cs +++ b/src/Discord.Net.Interactions/RestInteractionModuleBase.cs @@ -42,18 +42,18 @@ namespace Discord.Interactions /// if the response should be hidden to everyone besides the invoker of the command, otherwise . /// The allowed mentions for this response. /// The request options for this response. - /// A to be sent with this response. + /// A to be sent with this response. /// A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored. /// /// A Task representing the operation of creating the interaction response. /// /// Thrown if the interaction isn't a type of . - protected override async Task RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) + protected override async Task RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent components = null, Embed embed = null) { if (Context.Interaction is not RestInteraction restInteraction) throw new InvalidOperationException($"Invalid interaction type. Interaction must be a type of {nameof(RestInteraction)} in order to execute this method"); - await InteractionService._restResponseCallback(Context, restInteraction.Respond(text, embeds, isTTS, ephemeral, allowedMentions, component, embed, options)).ConfigureAwait(false); + await InteractionService._restResponseCallback(Context, restInteraction.Respond(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options)).ConfigureAwait(false); } } } diff --git a/src/Discord.Net.Interactions/Results/SearchResult.cs b/src/Discord.Net.Interactions/Results/SearchResult.cs index 874e57d45..8555e9e5d 100644 --- a/src/Discord.Net.Interactions/Results/SearchResult.cs +++ b/src/Discord.Net.Interactions/Results/SearchResult.cs @@ -2,15 +2,34 @@ using System; namespace Discord.Interactions { - internal struct SearchResult : IResult where T : class, ICommandInfo + /// + /// Contains information of a command search. + /// + /// Type of the target command type. + public struct SearchResult : IResult where T : class, ICommandInfo { + /// + /// Gets the input text of the command search. + /// public string Text { get; } + + /// + /// Gets the found command, if the search was successful. + /// public T Command { get; } + + /// + /// Gets the Regex groups captured by the wild card pattern. + /// public string[] RegexCaptureGroups { get; } + + /// public InteractionCommandError? Error { get; } + /// public string ErrorReason { get; } + /// public bool IsSuccess => !Error.HasValue; private SearchResult (string text, T commandInfo, string[] captureGroups, InteractionCommandError? error, string reason) @@ -22,16 +41,53 @@ namespace Discord.Interactions ErrorReason = reason; } + /// + /// Initializes a new with no error, indicating a successful execution. + /// + /// + /// A that does not contain any errors. + /// public static SearchResult FromSuccess (string text, T commandInfo, string[] wildCardMatch = null) => new SearchResult(text, commandInfo, wildCardMatch, null, null); + /// + /// Initializes a new with a specified and its + /// reason, indicating an unsuccessful execution. + /// + /// The type of error. + /// The reason behind the error. + /// + /// A that contains a and reason. + /// public static SearchResult FromError (string text, InteractionCommandError error, string reason) => new SearchResult(text, null, null, error, reason); + + /// + /// Initializes a new with a specified exception, indicating an unsuccessful + /// execution. + /// + /// The exception that caused the command execution to fail. + /// + /// A that contains the exception that caused the unsuccessful execution, along + /// with a of type Exception as well as the exception message as the + /// reason. + /// public static SearchResult FromError (Exception ex) => new SearchResult(null, null, null, InteractionCommandError.Exception, ex.Message); + + /// + /// Initializes a new with a specified result; this may or may not be an + /// successful depending on the and + /// specified. + /// + /// The result to inherit from. + /// + /// A that inherits the error type and reason. + /// public static SearchResult FromError (IResult result) => new SearchResult(null, null, null, result.Error, result.ErrorReason); + /// public override string ToString ( ) => IsSuccess ? "Success" : $"{Error}: {ErrorReason}"; } } diff --git a/src/Discord.Net.Interactions/Utilities/InteractionUtility.cs b/src/Discord.Net.Interactions/Utilities/InteractionUtility.cs index 00062ced6..9751b613d 100644 --- a/src/Discord.Net.Interactions/Utilities/InteractionUtility.cs +++ b/src/Discord.Net.Interactions/Utilities/InteractionUtility.cs @@ -98,7 +98,7 @@ namespace Discord.Interactions .WithButton("Cancel", declineId, ButtonStyle.Danger) .Build(); - var prompt = await channel.SendMessageAsync(message, component: component).ConfigureAwait(false); + var prompt = await channel.SendMessageAsync(message, components: component).ConfigureAwait(false); var response = await WaitForMessageComponentAsync(client, prompt, timeout, cancellationToken).ConfigureAwait(false) as SocketMessageComponent; diff --git a/src/Discord.Net.Rest/API/Common/ApplicationCommandOption.cs b/src/Discord.Net.Rest/API/Common/ApplicationCommandOption.cs index 1207df282..d703bd46b 100644 --- a/src/Discord.Net.Rest/API/Common/ApplicationCommandOption.cs +++ b/src/Discord.Net.Rest/API/Common/ApplicationCommandOption.cs @@ -56,6 +56,7 @@ namespace Discord.API Default = cmd.IsDefault ?? Optional.Unspecified; MinValue = cmd.MinValue ?? Optional.Unspecified; MaxValue = cmd.MaxValue ?? Optional.Unspecified; + Autocomplete = cmd.IsAutocomplete ?? Optional.Unspecified; Name = cmd.Name; Type = cmd.Type; diff --git a/src/Discord.Net.Rest/API/Rest/ModifyGuildRoleParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyGuildRoleParams.cs index fbb9c3e48..8aa92d40d 100644 --- a/src/Discord.Net.Rest/API/Rest/ModifyGuildRoleParams.cs +++ b/src/Discord.Net.Rest/API/Rest/ModifyGuildRoleParams.cs @@ -14,7 +14,9 @@ namespace Discord.API.Rest [JsonProperty("hoist")] public Optional Hoist { get; set; } [JsonProperty("icon")] - public Optional Icon { get; set; } + public Optional Icon { get; set; } + [JsonProperty("unicode_emoji")] + public Optional Emoji { get; set; } [JsonProperty("mentionable")] public Optional Mentionable { get; set; } } diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index 67e3107ed..d905eee05 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -1353,8 +1353,7 @@ namespace Discord.API if ((!args.Embeds.IsSpecified || args.Embeds.Value == null || args.Embeds.Value.Length == 0) && !args.Files.Any()) Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); - - if (args.Content.IsSpecified && args.Content.Value.Length > DiscordConfig.MaxMessageSize) + if (args.Content.IsSpecified && args.Content.Value?.Length > DiscordConfig.MaxMessageSize) throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(args.Content)); options = RequestOptions.CreateOrClone(options); @@ -1368,9 +1367,8 @@ namespace Discord.API Preconditions.NotNull(args, nameof(args)); Preconditions.NotEqual(id, 0, nameof(id)); - if (args.Content.IsSpecified) - if (args.Content.Value.Length > DiscordConfig.MaxMessageSize) - throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(args.Content)); + if (args.Content.IsSpecified && args.Content.Value?.Length > DiscordConfig.MaxMessageSize) + throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(args.Content)); options = RequestOptions.CreateOrClone(options); diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs index 2956d6443..40328cbd6 100644 --- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs +++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs @@ -267,7 +267,7 @@ namespace Discord.Rest /// Message content is too long, length must be less or equal to . public static async Task SendMessageAsync(IMessageChannel channel, BaseDiscordClient client, - string text, bool isTTS, Embed embed, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, RequestOptions options, Embed[] embeds) + string text, bool isTTS, Embed embed, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, RequestOptions options, Embed[] embeds) { embeds ??= Array.Empty(); if (embed != null) @@ -304,7 +304,7 @@ namespace Discord.Rest Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional.Unspecified, AllowedMentions = allowedMentions?.ToModel(), MessageReference = messageReference?.ToModel(), - Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified, + Components = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified, Stickers = stickers?.Any() ?? false ? stickers.Select(x => x.Id).ToArray() : Optional.Unspecified }; var model = await client.ApiClient.CreateMessageAsync(channel.Id, args, options).ConfigureAwait(false); @@ -336,27 +336,27 @@ namespace Discord.Rest /// An I/O error occurred while opening the file. /// Message content is too long, length must be less or equal to . public static async Task SendFileAsync(IMessageChannel channel, BaseDiscordClient client, - string filePath, string text, bool isTTS, Embed embed, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, RequestOptions options, bool isSpoiler, Embed[] embeds) + string filePath, string text, bool isTTS, Embed embed, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, RequestOptions options, bool isSpoiler, Embed[] embeds) { string filename = Path.GetFileName(filePath); using (var file = File.OpenRead(filePath)) - return await SendFileAsync(channel, client, file, filename, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, isSpoiler, embeds).ConfigureAwait(false); + return await SendFileAsync(channel, client, file, filename, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, isSpoiler, embeds).ConfigureAwait(false); } /// Message content is too long, length must be less or equal to . public static Task SendFileAsync(IMessageChannel channel, BaseDiscordClient client, - Stream stream, string filename, string text, bool isTTS, Embed embed, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, RequestOptions options, bool isSpoiler, Embed[] embeds) + Stream stream, string filename, string text, bool isTTS, Embed embed, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, RequestOptions options, bool isSpoiler, Embed[] embeds) { - return SendFileAsync(channel, client, new FileAttachment(stream, filename, isSpoiler: isSpoiler), text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, embeds); + return SendFileAsync(channel, client, new FileAttachment(stream, filename, isSpoiler: isSpoiler), text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, embeds); } /// Message content is too long, length must be less or equal to . public static Task SendFileAsync(IMessageChannel channel, BaseDiscordClient client, - FileAttachment attachment, string text, bool isTTS, Embed embed, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, RequestOptions options, Embed[] embeds) - => SendFilesAsync(channel, client, new[] { attachment }, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, embeds); + FileAttachment attachment, string text, bool isTTS, Embed embed, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, RequestOptions options, Embed[] embeds) + => SendFilesAsync(channel, client, new[] { attachment }, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, embeds); public static async Task SendFilesAsync(IMessageChannel channel, BaseDiscordClient client, - IEnumerable attachments, string text, bool isTTS, Embed embed, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, RequestOptions options, Embed[] embeds) + IEnumerable attachments, string text, bool isTTS, Embed embed, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, RequestOptions options, Embed[] embeds) { embeds ??= Array.Empty(); if (embed != null) @@ -392,7 +392,7 @@ namespace Discord.Rest Preconditions.AtMost(stickers.Length, 3, nameof(stickers), "A max of 3 stickers are allowed."); } - var args = new UploadFileParams(attachments.ToArray()) { Content = text, IsTTS = isTTS, Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional.Unspecified, AllowedMentions = allowedMentions?.ToModel() ?? Optional.Unspecified, MessageReference = messageReference?.ToModel() ?? Optional.Unspecified, MessageComponent = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified, Stickers = stickers?.Any() ?? false ? stickers.Select(x => x.Id).ToArray() : Optional.Unspecified }; + var args = new UploadFileParams(attachments.ToArray()) { Content = text, IsTTS = isTTS, Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional.Unspecified, AllowedMentions = allowedMentions?.ToModel() ?? Optional.Unspecified, MessageReference = messageReference?.ToModel() ?? Optional.Unspecified, MessageComponent = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified, Stickers = stickers?.Any() ?? false ? stickers.Select(x => x.Id).ToArray() : Optional.Unspecified }; var model = await client.ApiClient.UploadFileAsync(channel.Id, args, options).ConfigureAwait(false); return RestUserMessage.Create(client, channel, client.CurrentUser, model); } diff --git a/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs b/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs index 159735798..1af936a57 100644 --- a/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs @@ -25,14 +25,14 @@ namespace Discord.Rest /// If null, all mentioned roles and users will be notified. /// /// The message references to be included. Used to reply to specific messages. - /// The message components to be included with this message. Used for interactions. + /// The message components to be included with this message. Used for interactions. /// A collection of stickers to send with the message. /// A array of s to send with this response. Max 10. /// /// A task that represents an asynchronous send operation for delivering the message. The task result /// contains the sent message. /// - new Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null); + new Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null); /// /// Sends a file to this message channel with an optional caption. /// @@ -52,14 +52,14 @@ namespace Discord.Rest /// If null, all mentioned roles and users will be notified. /// /// The message references to be included. Used to reply to specific messages. - /// The message components to be included with this message. Used for interactions. + /// The message components to be included with this message. Used for interactions. /// A collection of stickers to send with the message. /// A array of s to send with this response. Max 10. /// /// A task that represents an asynchronous send operation for delivering the message. The task result /// contains the sent message. /// - new Task SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null); + new Task SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null); /// /// Sends a file to this message channel with an optional caption. /// @@ -79,14 +79,14 @@ namespace Discord.Rest /// If null, all mentioned roles and users will be notified. /// /// The message references to be included. Used to reply to specific messages. - /// The message components to be included with this message. Used for interactions. + /// The message components to be included with this message. Used for interactions. /// A collection of stickers to send with the message. /// A array of s to send with this response. Max 10. /// /// A task that represents an asynchronous send operation for delivering the message. The task result /// contains the sent message. /// - new Task SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null); + new Task SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null); /// /// Gets a message from this message channel. diff --git a/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs index 1b91c6e62..36b190e56 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs @@ -94,8 +94,8 @@ namespace Discord.Rest /// /// Message content is too long, length must be less or equal to . - public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, embeds); + public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, embeds); /// /// @@ -122,22 +122,22 @@ namespace Discord.Rest /// is in an invalid format. /// An I/O error occurred while opening the file. /// Message content is too long, length must be less or equal to . - public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, isSpoiler, embeds); + public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, isSpoiler, embeds); /// /// Message content is too long, length must be less or equal to . - public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, isSpoiler, embeds); + public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, isSpoiler, embeds); /// /// Message content is too long, length must be less or equal to . - public Task SendFileAsync(FileAttachment attachment, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendFileAsync(this, Discord, attachment, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, embeds); + public Task SendFileAsync(FileAttachment attachment, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendFileAsync(this, Discord, attachment, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, embeds); /// /// Message content is too long, length must be less or equal to . - public Task SendFilesAsync(IEnumerable attachments, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendFilesAsync(this, Discord, attachments, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, embeds); + public Task SendFilesAsync(IEnumerable attachments, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendFilesAsync(this, Discord, attachments, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, embeds); /// public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) @@ -219,20 +219,20 @@ namespace Discord.Rest async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); /// - async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); /// - async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); /// - async Task IMessageChannel.SendFileAsync(FileAttachment attachment, string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendFileAsync(attachment, text, isTTS, embed, options, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendFileAsync(FileAttachment attachment, string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendFileAsync(attachment, text, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); /// - async Task IMessageChannel.SendFilesAsync(IEnumerable attachments, string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendFilesAsync(attachments, text, isTTS, embed, options, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendFilesAsync(IEnumerable attachments, string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendFilesAsync(attachments, text, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); /// - async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendMessageAsync(text, isTTS, embed, options, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendMessageAsync(text, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); #endregion #region IChannel diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs index 83ff3f558..267b4dc52 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs @@ -100,8 +100,8 @@ namespace Discord.Rest /// /// Message content is too long, length must be less or equal to . - public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, embeds); + public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, embeds); /// /// @@ -128,20 +128,20 @@ namespace Discord.Rest /// is in an invalid format. /// An I/O error occurred while opening the file. /// Message content is too long, length must be less or equal to . - public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, isSpoiler, embeds); + public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, isSpoiler, embeds); /// /// Message content is too long, length must be less or equal to . - public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, isSpoiler, embeds); + public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, isSpoiler, embeds); /// /// Message content is too long, length must be less or equal to . - public Task SendFileAsync(FileAttachment attachment, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendFileAsync(this, Discord, attachment, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, embeds); + public Task SendFileAsync(FileAttachment attachment, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendFileAsync(this, Discord, attachment, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, embeds); /// /// Message content is too long, length must be less or equal to . - public Task SendFilesAsync(IEnumerable attachments, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendFilesAsync(this, Discord, attachments, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, embeds); + public Task SendFilesAsync(IEnumerable attachments, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendFilesAsync(this, Discord, attachments, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, embeds); /// public Task TriggerTypingAsync(RequestOptions options = null) => ChannelHelper.TriggerTypingAsync(this, Discord, options); @@ -193,17 +193,17 @@ namespace Discord.Rest async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); - async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); - - async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); - async Task IMessageChannel.SendFileAsync(FileAttachment attachment, string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendFileAsync(attachment, text, isTTS, embed, options, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); - async Task IMessageChannel.SendFilesAsync(IEnumerable attachments, string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendFilesAsync(attachments, text, isTTS, embed, options, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); - async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendMessageAsync(text, isTTS, embed, options, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); + + async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendFileAsync(FileAttachment attachment, string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendFileAsync(attachment, text, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendFilesAsync(IEnumerable attachments, string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendFilesAsync(attachments, text, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendMessageAsync(text, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); #endregion #region IAudioChannel diff --git a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs index f97c536c8..f1bdee65c 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs @@ -103,8 +103,8 @@ namespace Discord.Rest /// /// Message content is too long, length must be less or equal to . - public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, embeds); + public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, embeds); /// /// @@ -131,23 +131,23 @@ namespace Discord.Rest /// is in an invalid format. /// An I/O error occurred while opening the file. /// Message content is too long, length must be less or equal to . - public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, isSpoiler, embeds); + public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, isSpoiler, embeds); /// /// Message content is too long, length must be less or equal to . - public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, isSpoiler, embeds); + public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, isSpoiler, embeds); /// /// Message content is too long, length must be less or equal to . - public Task SendFileAsync(FileAttachment attachment, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendFileAsync(this, Discord, attachment, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, embeds); + public Task SendFileAsync(FileAttachment attachment, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendFileAsync(this, Discord, attachment, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, embeds); /// /// Message content is too long, length must be less or equal to . - public Task SendFilesAsync(IEnumerable attachments, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendFilesAsync(this, Discord, attachments, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, embeds); + public Task SendFilesAsync(IEnumerable attachments, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendFilesAsync(this, Discord, attachments, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, embeds); /// public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) @@ -332,24 +332,24 @@ namespace Discord.Rest => await GetPinnedMessagesAsync(options).ConfigureAwait(false); /// - async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); /// - async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); /// - async Task IMessageChannel.SendFileAsync(FileAttachment attachment, string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendFileAsync(attachment, text, isTTS, embed, options, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendFileAsync(FileAttachment attachment, string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendFileAsync(attachment, text, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); /// - async Task IMessageChannel.SendFilesAsync(IEnumerable attachments, string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendFilesAsync(attachments, text, isTTS, embed, options, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendFilesAsync(IEnumerable attachments, string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendFilesAsync(attachments, text, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); /// - async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendMessageAsync(text, isTTS, embed, options, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendMessageAsync(text, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); #endregion #region IGuildChannel diff --git a/src/Discord.Net.Rest/Entities/Interactions/CommandBase/RestCommandBase.cs b/src/Discord.Net.Rest/Entities/Interactions/CommandBase/RestCommandBase.cs index 338942a30..db4ff1e0f 100644 --- a/src/Discord.Net.Rest/Entities/Interactions/CommandBase/RestCommandBase.cs +++ b/src/Discord.Net.Rest/Entities/Interactions/CommandBase/RestCommandBase.cs @@ -63,7 +63,7 @@ namespace Discord.Rest /// if the response should be hidden to everyone besides the invoker of the command, otherwise . /// The allowed mentions for this response. /// The request options for this response. - /// A to be sent with this response. + /// A to be sent with this response. /// A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored. /// Message content is too long, length must be less or equal to . /// The parameters provided were invalid or the token was invalid. @@ -76,7 +76,7 @@ namespace Discord.Rest bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, - MessageComponent component = null, + MessageComponent components = null, Embed embed = null, RequestOptions options = null) { @@ -119,7 +119,7 @@ namespace Discord.Rest AllowedMentions = allowedMentions?.ToModel() ?? Optional.Unspecified, Embeds = embeds.Select(x => x.ToModel()).ToArray(), TTS = isTTS, - Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified, + Components = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified, Flags = ephemeral ? MessageFlags.Ephemeral : Optional.Unspecified } }; @@ -152,7 +152,7 @@ namespace Discord.Rest bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, - MessageComponent component = null, + MessageComponent components = null, Embed embed = null, RequestOptions options = null) { @@ -173,7 +173,7 @@ namespace Discord.Rest AllowedMentions = allowedMentions?.ToModel() ?? Optional.Unspecified, IsTTS = isTTS, Embeds = embeds.Select(x => x.ToModel()).ToArray(), - Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified + Components = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified }; if (ephemeral) diff --git a/src/Discord.Net.Rest/Entities/Interactions/MessageComponents/RestMessageComponent.cs b/src/Discord.Net.Rest/Entities/Interactions/MessageComponents/RestMessageComponent.cs index 9660bf7b0..22d3f5b0b 100644 --- a/src/Discord.Net.Rest/Entities/Interactions/MessageComponents/RestMessageComponent.cs +++ b/src/Discord.Net.Rest/Entities/Interactions/MessageComponents/RestMessageComponent.cs @@ -13,7 +13,7 @@ namespace Discord.Rest /// /// Represents a REST-based message component. /// - internal class RestMessageComponent : RestInteraction, IComponentInteraction, IDiscordInteraction + public class RestMessageComponent : RestInteraction, IComponentInteraction, IDiscordInteraction { /// /// Gets the data received with this interaction, contains the button that was clicked. @@ -64,7 +64,7 @@ namespace Discord.Rest /// if the message should be read out by a text-to-speech reader, otherwise . /// if the response should be hidden to everyone besides the invoker of the command, otherwise . /// The allowed mentions for this response. - /// A to be sent with this response. + /// A to be sent with this response. /// A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored. /// The request options for this response. /// @@ -76,7 +76,7 @@ namespace Discord.Rest bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, - MessageComponent component = null, + MessageComponent components = null, Embed embed = null, RequestOptions options = null) { @@ -119,7 +119,7 @@ namespace Discord.Rest AllowedMentions = allowedMentions?.ToModel(), Embeds = embeds.Select(x => x.ToModel()).ToArray(), TTS = isTTS, - Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified + Components = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified } }; @@ -244,7 +244,7 @@ namespace Discord.Rest bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, - MessageComponent component = null, + MessageComponent components = null, Embed embed = null, RequestOptions options = null) { @@ -265,7 +265,7 @@ namespace Discord.Rest AllowedMentions = allowedMentions?.ToModel() ?? Optional.Unspecified, IsTTS = isTTS, Embeds = embeds.Select(x => x.ToModel()).ToArray(), - Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified + Components = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified }; if (ephemeral) diff --git a/src/Discord.Net.Rest/Entities/Interactions/RestApplicationCommandOption.cs b/src/Discord.Net.Rest/Entities/Interactions/RestApplicationCommandOption.cs index d5c261e0b..1273b9a1e 100644 --- a/src/Discord.Net.Rest/Entities/Interactions/RestApplicationCommandOption.cs +++ b/src/Discord.Net.Rest/Entities/Interactions/RestApplicationCommandOption.cs @@ -26,6 +26,9 @@ namespace Discord.Rest /// public bool? IsRequired { get; private set; } + /// + public bool? IsAutocomplete { get; private set; } + /// public double? MinValue { get; private set; } @@ -74,6 +77,9 @@ namespace Discord.Rest if (model.MaxValue.IsSpecified) MaxValue = model.MaxValue.Value; + if (model.Autocomplete.IsSpecified) + IsAutocomplete = model.Autocomplete.Value; + Options = model.Options.IsSpecified ? model.Options.Value.Select(Create).ToImmutableArray() : ImmutableArray.Create(); diff --git a/src/Discord.Net.Rest/Entities/Interactions/RestInteraction.cs b/src/Discord.Net.Rest/Entities/Interactions/RestInteraction.cs index 35b356997..5f551ba0c 100644 --- a/src/Discord.Net.Rest/Entities/Interactions/RestInteraction.cs +++ b/src/Discord.Net.Rest/Entities/Interactions/RestInteraction.cs @@ -162,7 +162,7 @@ namespace Discord.Rest return RestInteractionMessage.Create(Discord, model, Token, Channel); } /// - public abstract string Respond(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent component = null, Embed embed = null, RequestOptions options = null); + public abstract string Respond(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null); /// /// Sends a followup message for this interaction. @@ -274,8 +274,8 @@ namespace Discord.Rest => Task.FromResult(Defer(ephemeral, options)); /// async Task IDiscordInteraction.FollowupAsync(string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, - MessageComponent component, Embed embed, RequestOptions options) - => await FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, component, embed, options).ConfigureAwait(false); + MessageComponent components, Embed embed, RequestOptions options) + => await FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false); /// async Task IDiscordInteraction.GetOriginalResponseAsync(RequestOptions options) => await GetOriginalResponseAsync(options).ConfigureAwait(false); diff --git a/src/Discord.Net.Rest/Entities/Interactions/RestPingInteraction.cs b/src/Discord.Net.Rest/Entities/Interactions/RestPingInteraction.cs index 5ed5e054a..5d27b491e 100644 --- a/src/Discord.Net.Rest/Entities/Interactions/RestPingInteraction.cs +++ b/src/Discord.Net.Rest/Entities/Interactions/RestPingInteraction.cs @@ -38,7 +38,7 @@ namespace Discord.Rest } public override string Defer(bool ephemeral = false, RequestOptions options = null) => throw new NotSupportedException(); - public override string Respond(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent component = null, Embed embed = null, RequestOptions options = null) => throw new NotSupportedException(); + public override string Respond(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null) => throw new NotSupportedException(); public override Task FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null) => throw new NotSupportedException(); public override Task FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null) => throw new NotSupportedException(); public override Task FollowupWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null) => throw new NotSupportedException(); diff --git a/src/Discord.Net.Rest/Entities/Interactions/SlashCommands/RestAutocompleteInteraction.cs b/src/Discord.Net.Rest/Entities/Interactions/SlashCommands/RestAutocompleteInteraction.cs index 1abeb4b3f..12f07942f 100644 --- a/src/Discord.Net.Rest/Entities/Interactions/SlashCommands/RestAutocompleteInteraction.cs +++ b/src/Discord.Net.Rest/Entities/Interactions/SlashCommands/RestAutocompleteInteraction.cs @@ -104,7 +104,7 @@ namespace Discord.Rest => Respond(result, options); public override string Defer(bool ephemeral = false, RequestOptions options = null) => throw new NotSupportedException("Autocomplete interactions don't support this method!"); - public override string Respond(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent component = null, Embed embed = null, RequestOptions options = null) + public override string Respond(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null) => throw new NotSupportedException("Autocomplete interactions don't support this method!"); public override Task FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null) => throw new NotSupportedException("Autocomplete interactions don't support this method!"); diff --git a/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs b/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs index d8552f869..3b2946a0d 100644 --- a/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs +++ b/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs @@ -13,15 +13,20 @@ namespace Discord.Rest { await client.ApiClient.DeleteGuildRoleAsync(role.Guild.Id, role.Id, options).ConfigureAwait(false); } - public static async Task ModifyAsync(IRole role, BaseDiscordClient client, + public static async Task ModifyAsync(IRole role, BaseDiscordClient client, Action func, RequestOptions options) { var args = new RoleProperties(); func(args); - if (args.Icon.IsSpecified) + if (args.Icon.IsSpecified || args.Emoji.IsSpecified) { role.Guild.Features.EnsureFeature(GuildFeature.RoleIcons); + + if (args.Icon.IsSpecified && args.Emoji.IsSpecified) + { + throw new ArgumentException("Emoji and Icon properties cannot be present on a role at the same time."); + } } var apiArgs = new API.Rest.ModifyGuildRoleParams @@ -31,8 +36,20 @@ namespace Discord.Rest Mentionable = args.Mentionable, Name = args.Name, Permissions = args.Permissions.IsSpecified ? args.Permissions.Value.RawValue.ToString() : Optional.Create(), - Icon = args.Icon.IsSpecified ? args.Icon.Value.ToModel() : Optional.Unspecified + Icon = args.Icon.IsSpecified ? args.Icon.Value.Value.ToModel() : Optional.Unspecified, + Emoji = args.Emoji.GetValueOrDefault()?.Name ?? Optional.Unspecified }; + + if (args.Icon.IsSpecified && role.Emoji != null) + { + apiArgs.Emoji = null; + } + + if (args.Emoji.IsSpecified && !string.IsNullOrEmpty(role.Icon)) + { + apiArgs.Icon = null; + } + var model = await client.ApiClient.ModifyGuildRoleAsync(role.Guild.Id, role.Id, apiArgs, options).ConfigureAwait(false); if (args.Position.IsSpecified) diff --git a/src/Discord.Net.Rest/Extensions/EntityExtensions.cs b/src/Discord.Net.Rest/Extensions/EntityExtensions.cs index bca2e8715..4062cda3d 100644 --- a/src/Discord.Net.Rest/Extensions/EntityExtensions.cs +++ b/src/Discord.Net.Rest/Extensions/EntityExtensions.cs @@ -179,7 +179,7 @@ namespace Discord.Rest var messageModel = new API.Message { IsTextToSpeech = data.TTS, - Content = data.Content, + Content = (data.Content.IsSpecified && data.Content.Value == null) ? Optional.Unspecified : data.Content, Embeds = data.Embeds, AllowedMentions = data.AllowedMentions, Components = data.Components, diff --git a/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs b/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs index 91fb24021..0a42b403e 100644 --- a/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs +++ b/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs @@ -424,12 +424,12 @@ namespace Discord.WebSocket } internal readonly AsyncEvent> _userJoinedEvent = new AsyncEvent>(); /// Fired when a user leaves a guild. - public event Func UserLeft + public event Func UserLeft { add { _userLeftEvent.Add(value); } remove { _userLeftEvent.Remove(value); } } - internal readonly AsyncEvent> _userLeftEvent = new AsyncEvent>(); + internal readonly AsyncEvent> _userLeftEvent = new AsyncEvent>(); /// Fired when a user is banned from a guild. public event Func UserBanned { @@ -452,12 +452,12 @@ namespace Discord.WebSocket } internal readonly AsyncEvent> _userUpdatedEvent = new AsyncEvent>(); /// Fired when a guild member is updated, or a member presence is updated. - public event Func, SocketGuildUser, Task> GuildMemberUpdated + public event Func, SocketGuildUser, Task> GuildMemberUpdated { add { _guildMemberUpdatedEvent.Add(value); } remove { _guildMemberUpdatedEvent.Remove(value); } } - internal readonly AsyncEvent, SocketGuildUser, Task>> _guildMemberUpdatedEvent = new AsyncEvent, SocketGuildUser, Task>>(); + internal readonly AsyncEvent, SocketGuildUser, Task>> _guildMemberUpdatedEvent = new AsyncEvent, SocketGuildUser, Task>>(); /// Fired when a user joins, leaves, or moves voice channels. public event Func UserVoiceStateUpdated { diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index eb64b5173..7c37cb1fa 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1275,13 +1275,13 @@ namespace Discord.WebSocket var before = user.Clone(); user.Update(State, data); - var cacheableBefore = new Cacheable(before, user.Id, true, () => Task.FromResult((SocketGuildUser)null)); + var cacheableBefore = new Cacheable(null, user.Id, false, () => Rest.GetGuildUserAsync(guild.Id, user.Id)); await TimedInvokeAsync(_guildMemberUpdatedEvent, nameof(GuildMemberUpdated), cacheableBefore, user).ConfigureAwait(false); } else { user = guild.AddOrUpdateUser(data); - var cacheableBefore = new Cacheable(null, user.Id, false, () => Task.FromResult((SocketGuildUser)null)); + var cacheableBefore = new Cacheable(null, user.Id, false, () => Rest.GetGuildUserAsync(guild.Id, user.Id)); await TimedInvokeAsync(_guildMemberUpdatedEvent, nameof(GuildMemberUpdated), cacheableBefore, user).ConfigureAwait(false); } } @@ -1300,7 +1300,7 @@ namespace Discord.WebSocket var guild = State.GetGuild(data.GuildId); if (guild != null) { - var user = guild.RemoveUser(data.User.Id); + SocketUser user = guild.RemoveUser(data.User.Id); guild.MemberCount--; if (!guild.IsSynced) @@ -1309,16 +1309,10 @@ namespace Discord.WebSocket return; } - if (user != null) - await TimedInvokeAsync(_userLeftEvent, nameof(UserLeft), user).ConfigureAwait(false); - else - { - if (!guild.HasAllMembers) - await IncompleteGuildUserAsync(type, data.User.Id, data.GuildId).ConfigureAwait(false); - else - await UnknownGuildUserAsync(type, data.User.Id, data.GuildId).ConfigureAwait(false); - return; - } + if(user == null) + user = SocketGlobalUser.Create(this, State, data.User); + + await TimedInvokeAsync(_userLeftEvent, nameof(UserLeft), user).ConfigureAwait(false); } else { diff --git a/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs index 1103f8feb..3e9b635de 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs @@ -34,14 +34,14 @@ namespace Discord.WebSocket /// If null, all mentioned roles and users will be notified. /// /// The message references to be included. Used to reply to specific messages. - /// The message components to be included with this message. Used for interactions. + /// The message components to be included with this message. Used for interactions. /// A collection of stickers to send with the message. /// A array of s to send with this response. Max 10. /// /// A task that represents an asynchronous send operation for delivering the message. The task result /// contains the sent message. /// - new Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null); + new Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null); /// /// Sends a file to this message channel with an optional caption. /// @@ -60,14 +60,14 @@ namespace Discord.WebSocket /// If null, all mentioned roles and users will be notified. /// /// The message references to be included. Used to reply to specific messages. - /// The message components to be included with this message. Used for interactions. + /// The message components to be included with this message. Used for interactions. /// A collection of stickers to send with the file. /// A array of s to send with this response. Max 10. /// /// A task that represents an asynchronous send operation for delivering the message. The task result /// contains the sent message. /// - new Task SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null); + new Task SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null); /// /// Sends a file to this message channel with an optional caption. /// @@ -87,14 +87,14 @@ namespace Discord.WebSocket /// If null, all mentioned roles and users will be notified. /// /// The message references to be included. Used to reply to specific messages. - /// The message components to be included with this message. Used for interactions. + /// The message components to be included with this message. Used for interactions. /// A collection of stickers to send with the file. /// A array of s to send with this response. Max 10. /// /// A task that represents an asynchronous send operation for delivering the message. The task result /// contains the sent message. /// - new Task SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null); + new Task SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null); /// /// Gets a cached message from this channel. diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs index ea00c9e03..f4fe12755 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs @@ -139,24 +139,24 @@ namespace Discord.WebSocket /// /// Message content is too long, length must be less or equal to . - public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, embeds); + public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, embeds); /// - public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, isSpoiler, embeds); + public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, isSpoiler, embeds); /// /// Message content is too long, length must be less or equal to . - public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, isSpoiler, embeds); + public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, isSpoiler, embeds); /// /// Message content is too long, length must be less or equal to . - public Task SendFileAsync(FileAttachment attachment, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendFileAsync(this, Discord, attachment, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, embeds); + public Task SendFileAsync(FileAttachment attachment, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendFileAsync(this, Discord, attachment, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, embeds); /// /// Message content is too long, length must be less or equal to . - public Task SendFilesAsync(IEnumerable attachments, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendFilesAsync(this, Discord, attachments, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, embeds); + public Task SendFilesAsync(IEnumerable attachments, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendFilesAsync(this, Discord, attachments, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, embeds); /// public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) @@ -255,20 +255,20 @@ namespace Discord.WebSocket async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); /// - async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); /// - async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); /// - async Task IMessageChannel.SendFileAsync(FileAttachment attachment, string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendFileAsync(attachment, text, isTTS, embed, options, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendFileAsync(FileAttachment attachment, string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendFileAsync(attachment, text, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); /// - async Task IMessageChannel.SendFilesAsync(IEnumerable attachments, string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendFilesAsync(attachments, text, isTTS, embed, options, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendFilesAsync(IEnumerable attachments, string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendFilesAsync(attachments, text, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); /// - async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendMessageAsync(text, isTTS, embed, options, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendMessageAsync(text, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); #endregion #region IChannel diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs index 1bbfa6e97..f1debb864 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs @@ -173,24 +173,24 @@ namespace Discord.WebSocket /// /// Message content is too long, length must be less or equal to . - public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, embeds); + public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, embeds); /// - public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, isSpoiler, embeds); + public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, isSpoiler, embeds); /// /// Message content is too long, length must be less or equal to . - public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, isSpoiler, embeds); + public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, isSpoiler, embeds); /// /// Message content is too long, length must be less or equal to . - public Task SendFileAsync(FileAttachment attachment, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendFileAsync(this, Discord, attachment, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, embeds); + public Task SendFileAsync(FileAttachment attachment, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendFileAsync(this, Discord, attachment, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, embeds); /// /// Message content is too long, length must be less or equal to . - public Task SendFilesAsync(IEnumerable attachments, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendFilesAsync(this, Discord, attachments, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, embeds); + public Task SendFilesAsync(IEnumerable attachments, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendFilesAsync(this, Discord, attachments, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, embeds); /// public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) @@ -322,21 +322,21 @@ namespace Discord.WebSocket => await GetPinnedMessagesAsync(options).ConfigureAwait(false); /// - async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); /// - async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); /// - async Task IMessageChannel.SendFileAsync(FileAttachment attachment, string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendFileAsync(attachment, text, isTTS, embed, options, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendFileAsync(FileAttachment attachment, string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendFileAsync(attachment, text, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); /// - async Task IMessageChannel.SendFilesAsync(IEnumerable attachments, string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendFilesAsync(attachments, text, isTTS, embed, options, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendFilesAsync(IEnumerable attachments, string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendFilesAsync(attachments, text, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); /// - async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendMessageAsync(text, isTTS, embed, options, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendMessageAsync(text, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); #endregion #region IAudioChannel diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs index 8dc8e9844..55085899d 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs @@ -211,27 +211,27 @@ namespace Discord.WebSocket /// /// Message content is too long, length must be less or equal to . - public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, embeds); + public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, embeds); /// - public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, isSpoiler, embeds); + public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, isSpoiler, embeds); /// /// Message content is too long, length must be less or equal to . - public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, isSpoiler, embeds); + public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, isSpoiler, embeds); /// /// Message content is too long, length must be less or equal to . - public Task SendFileAsync(FileAttachment attachment, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendFileAsync(this, Discord, attachment, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, embeds); + public Task SendFileAsync(FileAttachment attachment, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendFileAsync(this, Discord, attachment, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, embeds); /// /// Message content is too long, length must be less or equal to . - public Task SendFilesAsync(IEnumerable attachments, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null, ISticker[] stickers = null, Embed[] embeds = null) - => ChannelHelper.SendFilesAsync(this, Discord, attachments, text, isTTS, embed, allowedMentions, messageReference, component, stickers, options, embeds); + public Task SendFilesAsync(IEnumerable attachments, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null) + => ChannelHelper.SendFilesAsync(this, Discord, attachments, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, embeds); /// public Task DeleteMessagesAsync(IEnumerable messages, RequestOptions options = null) @@ -384,20 +384,20 @@ namespace Discord.WebSocket => await GetPinnedMessagesAsync(options).ConfigureAwait(false); /// - async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); /// - async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); /// - async Task IMessageChannel.SendFileAsync(FileAttachment attachment, string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendFileAsync(attachment, text, isTTS, embed, options, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendFileAsync(FileAttachment attachment, string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendFileAsync(attachment, text, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); /// - async Task IMessageChannel.SendFilesAsync(IEnumerable attachments, string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendFilesAsync(attachments, text, isTTS, embed, options, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendFilesAsync(IEnumerable attachments, string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendFilesAsync(attachments, text, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); /// - async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent component, ISticker[] stickers, Embed[] embeds) - => await SendMessageAsync(text, isTTS, embed, options, allowedMentions, messageReference, component, stickers, embeds).ConfigureAwait(false); + async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference, MessageComponent components, ISticker[] stickers, Embed[] embeds) + => await SendMessageAsync(text, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); #endregion #region INestedChannel diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponent.cs b/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponent.cs index d5d5f959d..17038b23d 100644 --- a/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponent.cs +++ b/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponent.cs @@ -78,7 +78,7 @@ namespace Discord.WebSocket bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, - MessageComponent component = null, + MessageComponent components = null, Embed embed = null, RequestOptions options = null) { @@ -121,7 +121,7 @@ namespace Discord.WebSocket AllowedMentions = allowedMentions?.ToModel(), Embeds = embeds.Select(x => x.ToModel()).ToArray(), TTS = isTTS, - Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified + Components = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified } }; @@ -155,7 +155,7 @@ namespace Discord.WebSocket /// A delegate containing the properties to modify the message with. /// The request options for this request. /// A task that represents the asynchronous operation of updating the message. - public async Task UpdateAsync(Action func, RequestOptions options = null) + public async Task UpdateAsync(Action func, RequestOptions options = null) { var args = new MessageProperties(); func(args); @@ -236,7 +236,7 @@ namespace Discord.WebSocket } } - await InteractionHelper.SendInteractionResponseAsync(Discord, response, this, Channel, options).ConfigureAwait(false); + return await InteractionHelper.SendInteractionResponseAsync(Discord, response, this, Channel, options).ConfigureAwait(false); lock (_lock) { @@ -251,7 +251,7 @@ namespace Discord.WebSocket bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, - MessageComponent component = null, + MessageComponent components = null, Embed embed = null, RequestOptions options = null) { @@ -272,7 +272,7 @@ namespace Discord.WebSocket AllowedMentions = allowedMentions?.ToModel() ?? Optional.Unspecified, IsTTS = isTTS, Embeds = embeds.Select(x => x.ToModel()).ToArray(), - Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified + Components = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified }; if (ephemeral) diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketApplicationCommandOption.cs b/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketApplicationCommandOption.cs index a19068d48..bdb03c8b0 100644 --- a/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketApplicationCommandOption.cs +++ b/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketApplicationCommandOption.cs @@ -25,6 +25,8 @@ namespace Discord.WebSocket /// public bool? IsRequired { get; private set; } + public bool? IsAutocomplete { get; private set; } + /// public double? MinValue { get; private set; } @@ -68,6 +70,8 @@ namespace Discord.WebSocket MaxValue = model.MaxValue.ToNullable(); + IsAutocomplete = model.Autocomplete.ToNullable(); + Choices = model.Choices.IsSpecified ? model.Choices.Value.Select(SocketApplicationCommandChoice.Create).ToImmutableArray() : ImmutableArray.Create(); diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBase.cs b/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBase.cs index 7c90878be..0a97bcd48 100644 --- a/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBase.cs +++ b/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBase.cs @@ -75,7 +75,7 @@ namespace Discord.WebSocket bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, - MessageComponent component = null, + MessageComponent components = null, Embed embed = null, RequestOptions options = null) { @@ -118,7 +118,7 @@ namespace Discord.WebSocket AllowedMentions = allowedMentions?.ToModel() ?? Optional.Unspecified, Embeds = embeds.Select(x => x.ToModel()).ToArray(), TTS = isTTS, - Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified, + Components = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified, Flags = ephemeral ? MessageFlags.Ephemeral : Optional.Unspecified } }; @@ -151,7 +151,7 @@ namespace Discord.WebSocket bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, - MessageComponent component = null, + MessageComponent components = null, Embed embed = null, RequestOptions options = null) { @@ -172,7 +172,7 @@ namespace Discord.WebSocket AllowedMentions = allowedMentions?.ToModel() ?? Optional.Unspecified, IsTTS = isTTS, Embeds = embeds.Select(x => x.ToModel()).ToArray(), - Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified + Components = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional.Unspecified }; if (ephemeral) diff --git a/src/Discord.Net.Webhook/DiscordWebhookClient.cs b/src/Discord.Net.Webhook/DiscordWebhookClient.cs index f7ad7301c..cb24f9ea1 100644 --- a/src/Discord.Net.Webhook/DiscordWebhookClient.cs +++ b/src/Discord.Net.Webhook/DiscordWebhookClient.cs @@ -87,8 +87,8 @@ namespace Discord.Webhook /// Sends a message to the channel for this webhook. /// Returns the ID of the created message. public Task SendMessageAsync(string text = null, bool isTTS = false, IEnumerable embeds = null, - string username = null, string avatarUrl = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageComponent component = null) - => WebhookClientHelper.SendMessageAsync(this, text, isTTS, embeds, username, avatarUrl, allowedMentions, options, component); + string username = null, string avatarUrl = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageComponent components = null) + => WebhookClientHelper.SendMessageAsync(this, text, isTTS, embeds, username, avatarUrl, allowedMentions, options, components); /// /// Modifies a message posted using this webhook. diff --git a/src/Discord.Net.Webhook/WebhookClientHelper.cs b/src/Discord.Net.Webhook/WebhookClientHelper.cs index 8b4bb5d2a..a9d5a25da 100644 --- a/src/Discord.Net.Webhook/WebhookClientHelper.cs +++ b/src/Discord.Net.Webhook/WebhookClientHelper.cs @@ -21,7 +21,7 @@ namespace Discord.Webhook return RestInternalWebhook.Create(client, model); } public static async Task SendMessageAsync(DiscordWebhookClient client, - string text, bool isTTS, IEnumerable embeds, string username, string avatarUrl, AllowedMentions allowedMentions, RequestOptions options, MessageComponent component) + string text, bool isTTS, IEnumerable embeds, string username, string avatarUrl, AllowedMentions allowedMentions, RequestOptions options, MessageComponent components) { var args = new CreateWebhookMessageParams { @@ -37,8 +37,8 @@ namespace Discord.Webhook args.AvatarUrl = avatarUrl; if (allowedMentions != null) args.AllowedMentions = allowedMentions.ToModel(); - if (component != null) - args.Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray(); + if (components != null) + args.Components = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray(); var model = await client.ApiClient.CreateWebhookMessageAsync(client.Webhook.Id, args, options: options).ConfigureAwait(false); return model.Id; diff --git a/src/Discord.Net/Discord.Net.nuspec b/src/Discord.Net/Discord.Net.nuspec index cb773a379..a2efc880b 100644 --- a/src/Discord.Net/Discord.Net.nuspec +++ b/src/Discord.Net/Discord.Net.nuspec @@ -13,26 +13,45 @@ false https://github.com/RogueException/Discord.Net/raw/dev/docs/marketing/logo/PackageLogo.png + + + + + + + + + + + + + + + + - - - - - - + + + + + + + - - - - - + + + + + + - - - - - - + + + + + + +