Browse Source

Implemented Context Menus (#106)

* Update README.md

* Update README.md

* Fix SocketSlashCommandDataOption to use long for Number instead of int (#89)

* Application webhooks (#86)

* Added webhook components for hooks having an application ID.

* resolved #88

* resolved #85

* Update device for gateway

* Fix MessageProperties.Embed being ignored in some methods that modifies a message (#92)

* Update label/description lengths for selects (ref: https://github.com/discord/discord-api-docs/pull/3598/files) (#91)

https://github.com/discord/discord-api-docs/pull/3598/files

* Fix tests (#90)

* Fix gateway serialization to include nulls (#96)

* Add missing guild permissions (#93)

* Update GuildPermissions.cs

* Update GuildPermissionsTests.cs

* Add banner and accent color to user and some fixes/improvements (#81)

* Add banner and accent color to user and some fixes

* Fix

* Fix!

* increase size of user banners to 256

* Some changes and mini refactor of color class

* add constant maxDecimalValue to color and checks with exceptions

* add `NotSupportedException` for `BannerId` and `AccentColor` in `SocketWebhookUser`

* Update ComponentBuilder.cs

- `MaxLabelLength` from `ComponentBuilder` moved to `ButtonBuilder`
- Added `MaxLabelLength` for `SelectMenuOptionBuilder`
- Changed `MaxDescriptionLength` to 100

* Interface Method Declarations for Interaction Methods (#99)

* added interface method declarations

* inline docs

* Fix serialization error

* meta: bump versions

* Fix debug pragma

* meta: bump version

* Remove rich presence button

* Assign CurrentUserId in Sharded Client (#100)

* added interface method declarations

* inline docs

* current user id assignment in sharded client

* Allow EmbedBuilder.ImageUrl to use attachment scheme syntax (#104)

* Make Webhook ApplicationId nullable instead of optional + fix IDiscordInteraction DeferAsync method (#110)

* Make Webhook ApplicationId nullable instead of optional

* Fix IDiscordInteraction DeferAsync to account for ephemeral defer

* Fix application command and thread starter messages being created as SocketSystemMessage

* Added description of ApplicationCommandType Enums

* Requested Fixes

renamed SocketApplicationUserCommand to SocketUserCommand
renamed SocketApplicationMessageCommand to SocketMessageCommand
using ContextMenuCreationProperties for both User and Message commands

* Added Summary to public members

removed whitespace from DiscordRestApiClient.cs

* Fixing guide to use switch statement

* implemented TrySendApplicationCommandAsync

* implemented ephemeral in SocketCommandBase Defer, and RespondAsync.

assigning int 64 was error. changed to "MessageFlags.Ephemeral", built and tested to work.

* removed ApplicationCommandType from SocketUser and SocketMessageCommandData

Co-authored-by: Quin Lynch <49576606+quinchs@users.noreply.github.com>
Co-authored-by: František Boháček <fandabohacek@gmail.com>
Co-authored-by: quin lynch <lynchquin@gmail.com>
Co-authored-by: d4n3436 <dan3436@hotmail.com>
Co-authored-by: MrCakeSlayer <13650699+MrCakeSlayer@users.noreply.github.com>
Co-authored-by: Nikon <47792796+INikonI@users.noreply.github.com>
Co-authored-by: Cenk Ergen <57065323+Cenngo@users.noreply.github.com>
pull/1923/head
drobbins329 GitHub 3 years ago
parent
commit
8993911d68
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
71 changed files with 1067 additions and 517 deletions
  1. +0
    -2
      Discord.Net.sln
  2. +12
    -3
      docs/guides/application-commands/context-menu-commands/creating-context-menu-commands.md
  3. +14
    -17
      docs/guides/application-commands/context-menu-commands/receiving-context-menu-command-events.md
  4. +18
    -0
      src/Discord.Net.Core/CDN.cs
  5. +4
    -4
      src/Discord.Net.Core/Discord.Net.Core.csproj
  6. +201
    -85
      src/Discord.Net.Core/Discord.Net.Core.xml
  7. +10
    -0
      src/Discord.Net.Core/Entities/Guilds/IGuild.cs
  8. +12
    -0
      src/Discord.Net.Core/Entities/Interactions/ApplicationCommandTypes.cs
  9. +1
    -7
      src/Discord.Net.Core/Entities/Interactions/ContextMenuCommandCreationProperties.cs
  10. +1
    -8
      src/Discord.Net.Core/Entities/Interactions/IApplicationCommand.cs
  11. +54
    -0
      src/Discord.Net.Core/Entities/Interactions/IDiscordInteraction.cs
  12. +25
    -10
      src/Discord.Net.Core/Entities/Interactions/Message Components/ComponentBuilder.cs
  13. +7
    -41
      src/Discord.Net.Core/Entities/Interactions/MessageCommandBuilder.cs
  14. +7
    -41
      src/Discord.Net.Core/Entities/Interactions/UserCommandBuilder.cs
  15. +1
    -1
      src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs
  16. +1
    -1
      src/Discord.Net.Core/Entities/Messages/MessageProperties.cs
  17. +49
    -6
      src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs
  18. +59
    -42
      src/Discord.Net.Core/Entities/Roles/Color.cs
  19. +27
    -5
      src/Discord.Net.Core/Entities/Users/IUser.cs
  20. +6
    -1
      src/Discord.Net.Core/Entities/Webhooks/IWebhook.cs
  21. +2
    -2
      src/Discord.Net.Rest/API/Common/Game.cs
  22. +1
    -1
      src/Discord.Net.Rest/API/Common/InteractionCallbackData.cs
  23. +0
    -18
      src/Discord.Net.Rest/API/Common/RichPresenceButton.cs
  24. +4
    -0
      src/Discord.Net.Rest/API/Common/User.cs
  25. +2
    -0
      src/Discord.Net.Rest/API/Common/Webhook.cs
  26. +1
    -1
      src/Discord.Net.Rest/API/Rest/CreateWebhookMessageParams.cs
  27. +2
    -0
      src/Discord.Net.Rest/API/Rest/ModifyWebhookMessageParams.cs
  28. +3
    -3
      src/Discord.Net.Rest/Discord.Net.Rest.csproj
  29. +27
    -2
      src/Discord.Net.Rest/Discord.Net.Rest.xml
  30. +6
    -56
      src/Discord.Net.Rest/DiscordRestApiClient.cs
  31. +8
    -8
      src/Discord.Net.Rest/DiscordRestClient.cs
  32. +15
    -1
      src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
  33. +61
    -36
      src/Discord.Net.Rest/Entities/Interactions/InteractionHelper.cs
  34. +30
    -15
      src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs
  35. +2
    -2
      src/Discord.Net.Rest/Entities/Users/RestThreadUser.cs
  36. +12
    -0
      src/Discord.Net.Rest/Entities/Users/RestUser.cs
  37. +4
    -0
      src/Discord.Net.Rest/Entities/Webhooks/RestWebhook.cs
  38. +1
    -0
      src/Discord.Net.Rest/Extensions/EntityExtensions.cs
  39. +3
    -1
      src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj
  40. +106
    -15
      src/Discord.Net.WebSocket/Discord.Net.WebSocket.xml
  41. +10
    -1
      src/Discord.Net.WebSocket/DiscordShardedClient.cs
  42. +14
    -3
      src/Discord.Net.WebSocket/DiscordSocketApiClient.cs
  43. +3
    -0
      src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
  44. +5
    -5
      src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/Message Commands/SocketMessageCommand.cs
  45. +8
    -9
      src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/Message Commands/SocketMessageCommandData.cs
  46. +5
    -5
      src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/User Commands/SocketUserCommand.cs
  47. +8
    -10
      src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/User Commands/SocketUserCommandData.cs
  48. +49
    -12
      src/Discord.Net.WebSocket/Entities/Interaction/Message Components/SocketMessageComponent.cs
  49. +1
    -1
      src/Discord.Net.WebSocket/Entities/Interaction/Slash Commands/SocketSlashCommand.cs
  50. +3
    -3
      src/Discord.Net.WebSocket/Entities/Interaction/Slash Commands/SocketSlashCommandDataOption.cs
  51. +6
    -3
      src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBase.cs
  52. +7
    -3
      src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBaseData.cs
  53. +4
    -0
      src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBaseDataOption.cs
  54. +21
    -4
      src/Discord.Net.WebSocket/Entities/Interaction/SocketInteraction.cs
  55. +4
    -1
      src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
  56. +3
    -1
      src/Discord.Net.WebSocket/Entities/Users/SocketGlobalUser.cs
  57. +4
    -0
      src/Discord.Net.WebSocket/Entities/Users/SocketGroupUser.cs
  58. +6
    -1
      src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs
  59. +4
    -0
      src/Discord.Net.WebSocket/Entities/Users/SocketSelfUser.cs
  60. +15
    -1
      src/Discord.Net.WebSocket/Entities/Users/SocketThreadUser.cs
  61. +8
    -1
      src/Discord.Net.WebSocket/Entities/Users/SocketUnknownUser.cs
  62. +18
    -0
      src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs
  63. +17
    -0
      src/Discord.Net.WebSocket/Entities/Users/SocketWebhookUser.cs
  64. +1
    -1
      src/Discord.Net.Webhook/Discord.Net.Webhook.csproj
  65. +6
    -1
      src/Discord.Net.Webhook/Discord.Net.Webhook.xml
  66. +2
    -2
      src/Discord.Net.Webhook/DiscordWebhookClient.cs
  67. +4
    -0
      src/Discord.Net.Webhook/Entities/Messages/WebhookMessageProperties.cs
  68. +3
    -0
      src/Discord.Net.Webhook/Entities/Webhooks/RestInternalWebhook.cs
  69. +5
    -2
      src/Discord.Net.Webhook/WebhookClientHelper.cs
  70. +10
    -10
      src/Discord.Net/Discord.Net.nuspec
  71. +14
    -2
      test/Discord.Net.Tests.Unit/GuildPermissionsTests.cs

+ 0
- 2
Discord.Net.sln View File

@@ -42,8 +42,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Examples", "src
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "idn", "samples\idn\idn.csproj", "{4A03840B-9EBE-47E3-89AB-E0914DF21AFB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FeatureTesting", "..\FeatureTesting\FeatureTesting\FeatureTesting.csproj", "{0CC57A32-3AC7-489D-8DF5-C431925E4675}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU


+ 12
- 3
docs/guides/application-commands/context-menu-commands/creating-context-menu-commands.md View File

@@ -15,14 +15,23 @@ Guild commands are specific to the guild you specify when making them. Guild com

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).

## SlashCommandBuilder
## UserCommandBuilder

The slash command builder will help you create slash commands. The builder has these available fields and methods:
The context menu user command builder will help you create user commands. The builder has these available fields and methods:

| Name | Type | Description |
| --------------------- | -------------------------------- | -------------------------------------------------------------------------------------------- |
| Name | string | The name of this context menu command. |
| WithName | Function | Sets the field name. |
| Build | Function | Builds the builder into the appropriate `CommandCreationProperties` class used to make Menu commands |

## MessageCommandBuilder

The context menu message command builder will help you create message commands. The builder has these available fields and methods:

| Name | Type | Description |
| --------------------- | -------------------------------- | -------------------------------------------------------------------------------------------- |
| Name | string | The name of this context menu command. |
| Description | string | A 0 length string. Left in place for possible future use. |
| WithName | Function | Sets the field name. |
| Build | Function | Builds the builder into the appropriate `CommandCreationProperties` class used to make Menu commands |



+ 14
- 17
docs/guides/application-commands/context-menu-commands/receiving-context-menu-command-events.md View File

@@ -15,24 +15,21 @@ public async Task InteractionCreatedHandler(SocketInteraction arg)

public async Task ApplicationCommandHandler(SocketInteraction arg)
{
var slashCommand = arg as SocketSlashCommand;
if(slashCommand != null)
Console.Writeline("Slash command received!")
var userCommand = arg as SocketApplicationUserCommand;
if(userCommand != null)
switch (arg)
{
Console.Writeline("User command received!")
// userCommand.User = User who ran command.
// userCommand.Data.Member = User who was clicked.
}
var messageCommand = arg as SocketApplicationMessageCommand;
if(messageCommand != null)
{
Console.Writeline("Message command received!")
// messageCommand.User = User who ran command.
// messageCommand.Data.Message = Message that was clicked.
case SocketSlashCommand slashCommand:
Console.Writeline("Slash command received!");
break;
case SocketUserCommand userCommand:
Console.Writeline("User command received!")
// userCommand.User = User who ran command.
// userCommand.Data.Member = User who was clicked.
break;
case SocketMessageCommand messageCommand:
Console.Writeline("Message command received!")
// messageCommand.User = User who ran command.
// messageCommand.Data.Message = Message that was clicked.
break;
}
}
```


+ 18
- 0
src/Discord.Net.Core/CDN.cs View File

@@ -46,6 +46,24 @@ namespace Discord
string extension = FormatToExtension(format, avatarId);
return $"{DiscordConfig.CDNUrl}avatars/{userId}/{avatarId}.{extension}?size={size}";
}

/// <summary>
/// Returns a user banner URL.
/// </summary>
/// <param name="userId">The user snowflake identifier.</param>
/// <param name="bannerId">The banner identifier.</param>
/// <param name="size">The size of the image to return in horizontal pixels. This can be any power of two between 16 and 2048.</param>
/// <param name="format">The format to return.</param>
/// <returns>
/// A URL pointing to the user's banner in the specified size.
/// </returns>
public static string GetUserBannerUrl(ulong userId, string bannerId, ushort size, ImageFormat format)
{
if (bannerId == null)
return null;
string extension = FormatToExtension(format, bannerId);
return $"{DiscordConfig.CDNUrl}banners/{userId}/{bannerId}.{extension}?size={size}";
}
/// <summary>
/// Returns the default user avatar URL.
/// </summary>


+ 4
- 4
src/Discord.Net.Core/Discord.Net.Core.csproj View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../../Discord.Net.targets" />
<Import Project="../../StyleAnalyzer.targets" />
<PropertyGroup>
@@ -8,12 +8,12 @@
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net461;netstandard2.0;netstandard2.1</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard2.0;netstandard2.1</TargetFrameworks>
<PackageId>Discord.Net.Labs.Core</PackageId>
<Version>3.0.0-pre</Version>
<Version>3.0.1-pre</Version>
<Product>Discord.Net.Labs.Core</Product>
<RepositoryUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</RepositoryUrl>
<PackageIcon>Temporary.png</PackageIcon>
<AssemblyVersion>2.3.8</AssemblyVersion>
<FileVersion>2.3.8</FileVersion>
<AssemblyVersion>3.3.1</AssemblyVersion>
<FileVersion>3.0.1</FileVersion>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
</PropertyGroup>
<PropertyGroup>


+ 201
- 85
src/Discord.Net.Core/Discord.Net.Core.xml View File

@@ -100,6 +100,18 @@
A URL pointing to the user's avatar in the specified size.
</returns>
</member>
<member name="M:Discord.CDN.GetUserBannerUrl(System.UInt64,System.String,System.UInt16,Discord.ImageFormat)">
<summary>
Returns a user banner URL.
</summary>
<param name="userId">The user snowflake identifier.</param>
<param name="bannerId">The banner identifier.</param>
<param name="size">The size of the image to return in horizontal pixels. This can be any power of two between 16 and 2048.</param>
<param name="format">The format to return.</param>
<returns>
A URL pointing to the user's banner in the specified size.
</returns>
</member>
<member name="M:Discord.CDN.GetDefaultUserAvatarUrl(System.UInt16)">
<summary>
Returns the default user avatar URL.
@@ -3902,6 +3914,16 @@
A task that represents the asynchronous removal operation.
</returns>
</member>
<member name="M:Discord.IGuild.GetApplicationCommandsAsync(Discord.RequestOptions)">
<summary>
Gets this guilds slash commands commands
</summary>
<param name="options">The options to be used when sending the request.</param>
<returns>
A task that represents the asynchronous get operation. The task result contains a read-only collection
of application commands found within the guild.
</returns>
</member>
<member name="T:Discord.IGuildIntegration">
<summary>
Holds information for a guild integration feature.
@@ -4467,6 +4489,41 @@
Whether the command is enabled by default when the app is added to a guild. Default is <see langword="true"/>
</summary>
</member>
<member name="T:Discord.ApplicationCommandType">
<summary>
ApplicationCommandType is enum of current valid Application Command Types: Slash, User, Message
</summary>
</member>
<member name="F:Discord.ApplicationCommandType.Slash">
<summary>
ApplicationCommandType.Slash is Slash command type
</summary>
</member>
<member name="F:Discord.ApplicationCommandType.User">
<summary>
ApplicationCommandType.User is Context Menu User command type
</summary>
</member>
<member name="F:Discord.ApplicationCommandType.Message">
<summary>
ApplicationCommandType.Message is Context Menu Message command type
</summary>
</member>
<member name="T:Discord.ContextMenuCommandCreationProperties">
<summary>
A class used to create Message commands.
</summary>
</member>
<member name="P:Discord.ContextMenuCommandCreationProperties.Name">
<summary>
The name of this command.
</summary>
</member>
<member name="P:Discord.ContextMenuCommandCreationProperties.Type">
<summary>
Gets or sets the type for this command.
</summary>
</member>
<member name="T:Discord.IApplicationCommand">
<summary>
The base command model that belongs to an application. see <see href="https://discord.com/developers/docs/interactions/slash-commands#applicationcommand"/>
@@ -4502,13 +4559,6 @@
If the option is a subcommand or subcommand group type, this nested options will be the parameters.
</summary>
</member>
<member name="M:Discord.IApplicationCommand.DeleteAsync(Discord.RequestOptions)">
<summary>
Deletes this command
</summary>
<param name="options">The options to be used when sending the request.</param>
<returns>A task that represents the asynchronous delete operation.</returns>
</member>
<member name="T:Discord.IApplicationCommandInteractionData">
<summary>
Represents data of an Interaction Command, see <see href="https://discord.com/developers/docs/interactions/slash-commands#interaction-applicationcommandinteractiondata"/>.
@@ -4642,6 +4692,58 @@
read-only property, always 1.
</summary>
</member>
<member name="M:Discord.IDiscordInteraction.RespondAsync(System.String,Discord.Embed[],System.Boolean,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent,Discord.Embed)">
<summary>
Responds to an Interaction with type <see cref="F:Discord.InteractionResponseType.ChannelMessageWithSource"/>.
</summary>
<param name="text">The text of the message to be sent.</param>
<param name="embeds">A array of embeds to send with this response. Max 10</param>
<param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param>
<param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param>
<param name="allowedMentions">The allowed mentions for this response.</param>
<param name="options">The request options for this response.</param>
<param name="component">A <see cref="T:Discord.MessageComponent"/> to be sent with this response</param>
<param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param>
</member>
<member name="M:Discord.IDiscordInteraction.FollowupAsync(System.String,Discord.Embed[],System.Boolean,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent,Discord.Embed)">
<summary>
Sends a followup message for this interaction.
</summary>
<param name="text">The text of the message to be sent</param>
<param name="embeds">A array of embeds to send with this response. Max 10</param>
<param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param>
<param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param>
<param name="allowedMentions">The allowed mentions for this response.</param>
<param name="options">The request options for this response.</param>
<param name="component">A <see cref="T:Discord.MessageComponent"/> to be sent with this response</param>
<param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param>
<returns>
The sent message.
</returns>
</member>
<member name="M:Discord.IDiscordInteraction.GetOriginalResponseAsync(Discord.RequestOptions)">
<summary>
Gets the original response for this interaction.
</summary>
<param name="options">The request options for this async request.</param>
<returns>A <see cref="T:Discord.IUserMessage"/> that represents the initial response.</returns>
</member>
<member name="M:Discord.IDiscordInteraction.ModifyOriginalResponseAsync(System.Action{Discord.MessageProperties},Discord.RequestOptions)">
<summary>
Edits original response for this interaction.
</summary>
<param name="func">A delegate containing the properties to modify the message with.</param>
<param name="options">The request options for this async request.</param>
<returns>A <see cref="T:Discord.IUserMessage"/> that represents the initial response.</returns>
</member>
<member name="M:Discord.IDiscordInteraction.DeferAsync(System.Boolean,Discord.RequestOptions)">
<summary>
Acknowledges this interaction.
</summary>
<returns>
A task that represents the asynchronous operation of acknowledging the interaction.
</returns>
</member>
<member name="T:Discord.IDiscordInteractionData">
<summary>
Represents an interface used to specify classes that they are a vaild dataype of a <see cref="T:Discord.IDiscordInteraction"/> class.
@@ -4792,7 +4894,7 @@
Represents a builder for creating a <see cref="T:Discord.MessageComponent"/>.
</summary>
</member>
<member name="F:Discord.ComponentBuilder.MaxLabelLength">
<member name="F:Discord.ComponentBuilder.MaxButtonLabelLength">
<summary>
The max length of a <see cref="P:Discord.ButtonComponent.Label"/>.
</summary>
@@ -4915,11 +5017,16 @@
Represents a class used to build <see cref="T:Discord.ButtonComponent"/>'s.
</summary>
</member>
<member name="F:Discord.ButtonBuilder.MaxLabelLength">
<summary>
The max length of a <see cref="P:Discord.ButtonComponent.Label"/>.
</summary>
</member>
<member name="P:Discord.ButtonBuilder.Label">
<summary>
Gets or sets the label of the current button.
</summary>
<exception cref="T:System.ArgumentException" accessor="set"><see cref="P:Discord.ButtonBuilder.Label"/> length exceeds <see cref="F:Discord.ComponentBuilder.MaxLabelLength"/>.</exception>
<exception cref="T:System.ArgumentException" accessor="set"><see cref="P:Discord.ButtonBuilder.Label"/> length exceeds <see cref="F:Discord.ComponentBuilder.MaxButtonLabelLength"/>.</exception>
</member>
<member name="P:Discord.ButtonBuilder.CustomId">
<summary>
@@ -5239,16 +5346,26 @@
Represents a class used to build <see cref="T:Discord.SelectMenuOption"/>'s.
</summary>
</member>
<member name="F:Discord.SelectMenuOptionBuilder.MaxLabelLength">
<summary>
The maximum length of a <see cref="P:Discord.SelectMenuOption.Label"/>.
</summary>
</member>
<member name="F:Discord.SelectMenuOptionBuilder.MaxDescriptionLength">
<summary>
The maximum length of a <see cref="P:Discord.SelectMenuOption.Description"/>.
</summary>
</member>
<member name="F:Discord.SelectMenuOptionBuilder.MaxSelectLabelLength">
<summary>
The maximum length of a <see cref="P:Discord.SelectMenuOption.Label"/>.
</summary>
</member>
<member name="P:Discord.SelectMenuOptionBuilder.Label">
<summary>
Gets or sets the label of the current select menu.
</summary>
<exception cref="T:System.ArgumentException" accessor="set"><see cref="P:Discord.SelectMenuOptionBuilder.Label"/> length exceeds <see cref="F:Discord.ComponentBuilder.MaxLabelLength"/></exception>
<exception cref="T:System.ArgumentException" accessor="set"><see cref="P:Discord.SelectMenuOptionBuilder.Label"/> length exceeds <see cref="F:Discord.SelectMenuOptionBuilder.MaxSelectLabelLength"/></exception>
</member>
<member name="P:Discord.SelectMenuOptionBuilder.Value">
<summary>
@@ -5456,7 +5573,7 @@
</member>
<member name="T:Discord.MessageCommandBuilder">
<summary>
A class used to build slash commands.
A class used to build Message commands.
</summary>
</member>
<member name="F:Discord.MessageCommandBuilder.MaxNameLength">
@@ -5464,26 +5581,16 @@
Returns the maximun length a commands name allowed by Discord
</summary>
</member>
<member name="F:Discord.MessageCommandBuilder.MaxDescriptionLength">
<summary>
Returns the maximum length of a commands description allowed by Discord.
</summary>
</member>
<member name="P:Discord.MessageCommandBuilder.Name">
<summary>
The name of this slash command.
</summary>
</member>
<member name="P:Discord.MessageCommandBuilder.Description">
<summary>
A 1-100 length description of this slash command
The name of this Message command.
</summary>
</member>
<member name="M:Discord.MessageCommandBuilder.Build">
<summary>
Build the current builder into a <see cref="T:Discord.MessageCommandCreationProperties"/> class.
Build the current builder into a <see cref="T:Discord.ContextMenuCommandCreationProperties"/> class.
</summary>
<returns>A <see cref="T:Discord.MessageCommandCreationProperties"/> that can be used to create user commands over rest.</returns>
<returns>A <see cref="T:Discord.ContextMenuCommandCreationProperties"/> that can be used to create message commands over rest.</returns>
</member>
<member name="M:Discord.MessageCommandBuilder.WithName(System.String)">
<summary>
@@ -5494,33 +5601,6 @@
The current builder.
</returns>
</member>
<member name="M:Discord.MessageCommandBuilder.WithDescription(System.String)">
<summary>
Sets the description of the current command.
</summary>
<param name="description">The description of this command.</param>
<returns>The current builder.</returns>
</member>
<member name="T:Discord.MessageCommandCreationProperties">
<summary>
A class used to create Message commands.
</summary>
</member>
<member name="P:Discord.MessageCommandCreationProperties.Name">
<summary>
The name of this command.
</summary>
</member>
<member name="P:Discord.MessageCommandCreationProperties.Description">
<summary>
The discription of this command.
</summary>
</member>
<member name="P:Discord.MessageCommandCreationProperties.Type">
<summary>
Gets or sets the type for this command.
</summary>
</member>
<member name="T:Discord.SlashCommandBuilder">
<summary>
A class used to build slash commands.
@@ -5785,7 +5865,7 @@
</member>
<member name="T:Discord.UserCommandBuilder">
<summary>
A class used to build slash commands.
A class used to build user commands.
</summary>
</member>
<member name="F:Discord.UserCommandBuilder.MaxNameLength">
@@ -5793,26 +5873,16 @@
Returns the maximun length a commands name allowed by Discord
</summary>
</member>
<member name="F:Discord.UserCommandBuilder.MaxDescriptionLength">
<summary>
Returns the maximum length of a commands description allowed by Discord.
</summary>
</member>
<member name="P:Discord.UserCommandBuilder.Name">
<summary>
The name of this slash command.
</summary>
</member>
<member name="P:Discord.UserCommandBuilder.Description">
<summary>
A 1-100 length description of this slash command
The name of this User command.
</summary>
</member>
<member name="M:Discord.UserCommandBuilder.Build">
<summary>
Build the current builder into a <see cref="T:Discord.UserCommandCreationProperties"/> class.
Build the current builder into a <see cref="T:Discord.ContextMenuCommandCreationProperties"/> class.
</summary>
<returns>A <see cref="T:Discord.UserCommandCreationProperties"/> that can be used to create user commands over rest.</returns>
<returns>A <see cref="T:Discord.ContextMenuCommandCreationProperties"/> that can be used to create user commands over rest.</returns>
</member>
<member name="M:Discord.UserCommandBuilder.WithName(System.String)">
<summary>
@@ -5823,13 +5893,6 @@
The current builder.
</returns>
</member>
<member name="M:Discord.UserCommandBuilder.WithDescription(System.String)">
<summary>
Sets the description of the current command.
</summary>
<param name="description">The description of this command.</param>
<returns>The current builder.</returns>
</member>
<member name="T:Discord.UserCommandCreationProperties">
<summary>
A class used to create User commands.
@@ -7751,7 +7814,7 @@
Gets or sets a single embed for this message.
</summary>
<remarks>
This property will be added to the <see cref="P:Discord.MessageProperties.Embed"/> array, in the future please use the array rather then this property.
This property will be added to the <see cref="P:Discord.MessageProperties.Embeds"/> array, in the future please use the array rather than this property.
</remarks>
</member>
<member name="P:Discord.MessageProperties.Embeds">
@@ -8794,7 +8857,25 @@
<summary> If <c>true</c>, a user may edit the webhooks for this guild. </summary>
</member>
<member name="P:Discord.GuildPermissions.ManageEmojisAndStickers">
<summary> If <c>true</c>, a user may edit the emojis for this guild. </summary>
<summary> If <c>true</c>, a user may edit the emojis and stickers for this guild. </summary>
</member>
<member name="P:Discord.GuildPermissions.UseSlashCommands">
<summary> If <c>true</c>, a user may use slash commands in this guild. </summary>
</member>
<member name="P:Discord.GuildPermissions.RequestToSpeak">
<summary> If <c>true</c>, a user may request to speak in stage channels. </summary>
</member>
<member name="P:Discord.GuildPermissions.ManageThreads">
<summary> If <c>true</c>, a user may manage threads in this guild. </summary>
</member>
<member name="P:Discord.GuildPermissions.UsePublicThreads">
<summary> If <c>true</c>, a user may create public threads in this guild. </summary>
</member>
<member name="P:Discord.GuildPermissions.UsePrivateThreads">
<summary> If <c>true</c>, a user may create private threads in this guild. </summary>
</member>
<member name="P:Discord.GuildPermissions.UseExternalStickers">
<summary> If <c>true</c>, a user may use external stickers in this guild. </summary>
</member>
<member name="M:Discord.GuildPermissions.#ctor(System.UInt64)">
<summary> Creates a new <see cref="T:Discord.GuildPermissions"/> with the provided packed value. </summary>
@@ -8802,10 +8883,10 @@
<member name="M:Discord.GuildPermissions.#ctor(System.String)">
<summary> Creates a new <see cref="T:Discord.GuildPermissions"/> with the provided packed value after converting to ulong. </summary>
</member>
<member name="M:Discord.GuildPermissions.#ctor(System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean)">
<member name="M:Discord.GuildPermissions.#ctor(System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean)">
<summary> Creates a new <see cref="T:Discord.GuildPermissions"/> structure with the provided permissions. </summary>
</member>
<member name="M:Discord.GuildPermissions.Modify(System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean})">
<member name="M:Discord.GuildPermissions.Modify(System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean})">
<summary> Creates a new <see cref="T:Discord.GuildPermissions"/> from this one, changing the provided non-null permissions. </summary>
</member>
<member name="M:Discord.GuildPermissions.Has(Discord.GuildPermission)">
@@ -8992,6 +9073,9 @@
Represents a color used in Discord.
</summary>
</member>
<member name="F:Discord.Color.MaxDecimalValue">
<summary> Gets the max decimal value of color. </summary>
</member>
<member name="F:Discord.Color.Default">
<summary> Gets the default user color value. </summary>
</member>
@@ -9096,20 +9180,21 @@
Initializes a <see cref="T:Discord.Color"/> struct with the given raw value.
</summary>
<example>
The following will create a color that has a hex value of
The following will create a color that has a hex value of
<see href="http://www.color-hex.com/color/607d8b">#607D8B</see>.
<code language="cs">
Color darkGrey = new Color(0x607D8B);
</code>
</example>
<param name="rawValue">The raw value of the color (e.g. <c>0x607D8B</c>).</param>
<exception cref="T:System.ArgumentException">Value exceeds <see cref="F:Discord.Color.MaxDecimalValue"/>.</exception>
</member>
<member name="M:Discord.Color.#ctor(System.Byte,System.Byte,System.Byte)">
<summary>
Initializes a <see cref="T:Discord.Color" /> struct with the given RGB bytes.
</summary>
<example>
The following will create a color that has a value of
The following will create a color that has a value of
<see href="http://www.color-hex.com/color/607d8b">#607D8B</see>.
<code language="cs">
Color darkGrey = new Color((byte)0b_01100000, (byte)0b_01111101, (byte)0b_10001011);
@@ -9118,13 +9203,14 @@
<param name="r">The byte that represents the red color.</param>
<param name="g">The byte that represents the green color.</param>
<param name="b">The byte that represents the blue color.</param>
<exception cref="T:System.ArgumentException">Value exceeds <see cref="F:Discord.Color.MaxDecimalValue"/>.</exception>
</member>
<member name="M:Discord.Color.#ctor(System.Int32,System.Int32,System.Int32)">
<summary>
Initializes a <see cref="T:Discord.Color"/> struct with the given RGB value.
</summary>
<example>
The following will create a color that has a value of
The following will create a color that has a value of
<see href="http://www.color-hex.com/color/607d8b">#607D8B</see>.
<code language="cs">
Color darkGrey = new Color(96, 125, 139);
@@ -9140,7 +9226,7 @@
Initializes a <see cref="T:Discord.Color"/> struct with the given RGB float value.
</summary>
<example>
The following will create a color that has a value of
The following will create a color that has a value of
<see href="http://www.color-hex.com/color/607c8c">#607c8c</see>.
<code language="cs">
Color darkGrey = new Color(0.38f, 0.49f, 0.55f);
@@ -9908,19 +9994,33 @@
Gets the identifier of this user's avatar.
</summary>
</member>
<member name="P:Discord.IUser.BannerId">
<summary>
Gets the identifier of this user's banner.
</summary>
</member>
<member name="P:Discord.IUser.AccentColor">
<summary>
Gets the user's banner color.
</summary>
<returns>
A <see cref="T:Discord.Color"/> struct representing the accent color of this user's banner.
</returns>
</member>
<member name="M:Discord.IUser.GetAvatarUrl(Discord.ImageFormat,System.UInt16)">
<summary>
Gets the avatar URL for this user.
</summary>
<remarks>
This property retrieves a URL for this user's avatar. In event that the user does not have a valid avatar
(i.e. their avatar identifier is not set), this property will return <c>null</c>. If you wish to
(i.e. their avatar identifier is not set), this method will return <c>null</c>. If you wish to
retrieve the default avatar for this user, consider using <see cref="M:Discord.IUser.GetDefaultAvatarUrl"/> (see
example).
</remarks>
<example>
<para>The following example attempts to retrieve the user's current avatar and send it to a channel; if one is
not set, a default avatar for this user will be returned instead.</para>
<para
>The following example attempts to retrieve the user's current avatar and send it to a channel; if one is
not set, a default avatar for this user will be returned instead.</para>
<code language="cs" region="GetAvatarUrl"
source="..\..\..\Discord.Net.Examples\Core\Entities\Users\IUser.Examples.cs"/>
</example>
@@ -9931,6 +10031,17 @@
A string representing the user's avatar URL; <c>null</c> if the user does not have an avatar in place.
</returns>
</member>
<member name="M:Discord.IUser.GetBannerUrl(Discord.ImageFormat,System.UInt16)">
<summary>
Gets the banner URL for this user.
</summary>
<param name="format">The format to return.</param>
<param name="size">The size of the image to return in. This can be any power of two between 16 and 2048.
</param>
<returns>
A string representing the user's avatar URL; <c>null</c> if the user does not have an banner in place.
</returns>
</member>
<member name="M:Discord.IUser.GetDefaultAvatarUrl">
<summary>
Gets the default avatar URL for this user.
@@ -9998,8 +10109,8 @@
This method is used to obtain or create a channel used to send a direct message.
<note type="warning">
In event that the current user cannot send a message to the target user, a channel can and will
still be created by Discord. However, attempting to send a message will yield a
<see cref="T:Discord.Net.HttpException"/> with a 403 as its
still be created by Discord. However, attempting to send a message will yield a
<see cref="T:Discord.Net.HttpException"/> with a 403 as its
<see cref="P:Discord.Net.HttpException.HttpCode"/>. There are currently no official workarounds by
Discord.
</note>
@@ -10293,6 +10404,11 @@
Gets the user that created this webhook.
</summary>
</member>
<member name="P:Discord.IWebhook.ApplicationId">
<summary>
Gets the ID of the application owning this webhook.
</summary>
</member>
<member name="M:Discord.IWebhook.ModifyAsync(System.Action{Discord.WebhookProperties},Discord.RequestOptions)">
<summary>
Modifies this webhook.


+ 10
- 0
src/Discord.Net.Core/Entities/Guilds/IGuild.cs View File

@@ -941,5 +941,15 @@ namespace Discord
/// A task that represents the asynchronous removal operation.
/// </returns>
Task DeleteEmoteAsync(GuildEmote emote, RequestOptions options = null);

/// <summary>
/// Gets this guilds slash commands commands
/// </summary>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous get operation. The task result contains a read-only collection
/// of application commands found within the guild.
/// </returns>
Task<IReadOnlyCollection<IApplicationCommand>> GetApplicationCommandsAsync (RequestOptions options = null);
}
}

+ 12
- 0
src/Discord.Net.Core/Entities/Interactions/ApplicationCommandTypes.cs View File

@@ -6,10 +6,22 @@ using System.Threading.Tasks;

namespace Discord
{
/// <summary>
/// ApplicationCommandType is enum of current valid Application Command Types: Slash, User, Message
/// </summary>
public enum ApplicationCommandType : byte
{
/// <summary>
/// ApplicationCommandType.Slash is Slash command type
/// </summary>
Slash = 1,
/// <summary>
/// ApplicationCommandType.User is Context Menu User command type
/// </summary>
User = 2,
/// <summary>
/// ApplicationCommandType.Message is Context Menu Message command type
/// </summary>
Message = 3
}
}

src/Discord.Net.Core/Entities/Interactions/MessageCommandCreationProperties.cs → src/Discord.Net.Core/Entities/Interactions/ContextMenuCommandCreationProperties.cs View File

@@ -9,19 +9,13 @@ namespace Discord
/// <summary>
/// A class used to create Message commands.
/// </summary>
public class MessageCommandCreationProperties
public class ContextMenuCommandCreationProperties
{
/// <summary>
/// The name of this command.
/// </summary>
public string Name { get; set; }

/// <summary>
/// The discription of this command.
/// </summary>
public string Description { get; set; }


/// <summary>
/// Gets or sets the type for this command.
/// </summary>

+ 1
- 8
src/Discord.Net.Core/Entities/Interactions/IApplicationCommand.cs View File

@@ -9,7 +9,7 @@ namespace Discord
/// <summary>
/// The base command model that belongs to an application. see <see href="https://discord.com/developers/docs/interactions/slash-commands#applicationcommand"/>
/// </summary>
public interface IApplicationCommand : ISnowflakeEntity
public interface IApplicationCommand : ISnowflakeEntity, IDeletable
{
/// <summary>
/// Gets the unique id of the parent application.
@@ -40,12 +40,5 @@ namespace Discord
/// If the option is a subcommand or subcommand group type, this nested options will be the parameters.
/// </summary>
IReadOnlyCollection<IApplicationCommandOption> Options { get; }

/// <summary>
/// Deletes this command
/// </summary>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>A task that represents the asynchronous delete operation.</returns>
Task DeleteAsync(RequestOptions options = null);
}
}

+ 54
- 0
src/Discord.Net.Core/Entities/Interactions/IDiscordInteraction.cs View File

@@ -39,5 +39,59 @@ namespace Discord
/// read-only property, always 1.
/// </summary>
int Version { get; }

/// <summary>
/// Responds to an Interaction with type <see cref="InteractionResponseType.ChannelMessageWithSource"/>.
/// </summary>
/// <param name="text">The text of the message to be sent.</param>
/// <param name="embeds">A array of embeds to send with this response. Max 10</param>
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param>
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param>
/// <param name="allowedMentions">The allowed mentions for this response.</param>
/// <param name="options">The request options for this response.</param>
/// <param name="component">A <see cref="MessageComponent"/> to be sent with this response</param>
/// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param>
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);

/// <summary>
/// Sends a followup message for this interaction.
/// </summary>
/// <param name="text">The text of the message to be sent</param>
/// <param name="embeds">A array of embeds to send with this response. Max 10</param>
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param>
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param>
/// <param name="allowedMentions">The allowed mentions for this response.</param>
/// <param name="options">The request options for this response.</param>
/// <param name="component">A <see cref="MessageComponent"/> to be sent with this response</param>
/// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param>
/// <returns>
/// The sent message.
/// </returns>
Task<IUserMessage> 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);

/// <summary>
/// Gets the original response for this interaction.
/// </summary>
/// <param name="options">The request options for this async request.</param>
/// <returns>A <see cref="IUserMessage"/> that represents the initial response.</returns>
Task<IUserMessage> GetOriginalResponseAsync (RequestOptions options = null);

/// <summary>
/// Edits original response for this interaction.
/// </summary>
/// <param name="func">A delegate containing the properties to modify the message with.</param>
/// <param name="options">The request options for this async request.</param>
/// <returns>A <see cref="IUserMessage"/> that represents the initial response.</returns>
Task<IUserMessage> ModifyOriginalResponseAsync (Action<MessageProperties> func, RequestOptions options = null);

/// <summary>
/// Acknowledges this interaction.
/// </summary>
/// <returns>
/// A task that represents the asynchronous operation of acknowledging the interaction.
/// </returns>
Task DeferAsync (bool ephemeral = false, RequestOptions options = null);
}
}

+ 25
- 10
src/Discord.Net.Core/Entities/Interactions/Message Components/ComponentBuilder.cs View File

@@ -13,7 +13,7 @@ namespace Discord
/// <summary>
/// The max length of a <see cref="ButtonComponent.Label"/>.
/// </summary>
public const int MaxLabelLength = 80;
public const int MaxButtonLabelLength = 80;

/// <summary>
/// The max length of a <see cref="ButtonComponent.CustomId"/>.
@@ -307,17 +307,22 @@ namespace Discord
/// </summary>
public class ButtonBuilder
{
/// <summary>
/// The max length of a <see cref="ButtonComponent.Label"/>.
/// </summary>
public const int MaxLabelLength = 80;

/// <summary>
/// Gets or sets the label of the current button.
/// </summary>
/// <exception cref="ArgumentException" accessor="set"><see cref="Label"/> length exceeds <see cref="ComponentBuilder.MaxLabelLength"/>.</exception>
/// <exception cref="ArgumentException" accessor="set"><see cref="Label"/> length exceeds <see cref="ComponentBuilder.MaxButtonLabelLength"/>.</exception>
public string Label
{
get => _label;
set
{
if (value != null && value.Length > ComponentBuilder.MaxLabelLength)
throw new ArgumentException(message: $"Button label must be {ComponentBuilder.MaxLabelLength} characters or less!", paramName: nameof(Label));
if (value != null && value.Length > ComponentBuilder.MaxButtonLabelLength)
throw new ArgumentException(message: $"Button label must be {ComponentBuilder.MaxButtonLabelLength} characters or less!", paramName: nameof(Label));

_label = value;
}
@@ -539,8 +544,8 @@ namespace Discord
if (string.IsNullOrEmpty(this.Url))
throw new InvalidOperationException("Link buttons must have a link associated with them");
else
UrlValidation.Validate(this.Url);
}
UrlValidation.Validate(this.Url);
}
else if (string.IsNullOrEmpty(this.CustomId))
throw new InvalidOperationException("Non-link buttons must have a custom id associated with them");

@@ -831,23 +836,33 @@ namespace Discord
/// </summary>
public class SelectMenuOptionBuilder
{
/// <summary>
/// The maximum length of a <see cref="SelectMenuOption.Label"/>.
/// </summary>
public const int MaxLabelLength = 100;

/// <summary>
/// The maximum length of a <see cref="SelectMenuOption.Description"/>.
/// </summary>
public const int MaxDescriptionLength = 50;
public const int MaxDescriptionLength = 100;
/// <summary>
/// The maximum length of a <see cref="SelectMenuOption.Label"/>.
/// </summary>
public const int MaxSelectLabelLength = 100;

/// <summary>
/// Gets or sets the label of the current select menu.
/// </summary>
/// <exception cref="ArgumentException" accessor="set"><see cref="Label"/> length exceeds <see cref="ComponentBuilder.MaxLabelLength"/></exception>
/// <exception cref="ArgumentException" accessor="set"><see cref="Label"/> length exceeds <see cref="MaxSelectLabelLength"/></exception>
public string Label
{
get => _label;
set
{
if (value != null)
if (value.Length > ComponentBuilder.MaxLabelLength)
throw new ArgumentException(message: $"Button label must be {ComponentBuilder.MaxLabelLength} characters or less!", paramName: nameof(Label));
if (value.Length > MaxSelectLabelLength)
throw new ArgumentException(message: $"Button label must be {MaxSelectLabelLength} characters or less!", paramName: nameof(Label));

_label = value;
}


+ 7
- 41
src/Discord.Net.Core/Entities/Interactions/MessageCommandBuilder.cs View File

@@ -8,7 +8,7 @@ using System.Threading.Tasks;
namespace Discord
{
/// <summary>
/// A class used to build slash commands.
/// A class used to build Message commands.
/// </summary>
public class MessageCommandBuilder
{
@@ -16,13 +16,9 @@ namespace Discord
/// Returns the maximun length a commands name allowed by Discord
/// </summary>
public const int MaxNameLength = 32;
/// <summary>
/// Returns the maximum length of a commands description allowed by Discord.
/// </summary>
public const int MaxDescriptionLength = 0;

/// <summary>
/// The name of this slash command.
/// The name of this Message command.
/// </summary>
public string Name
{
@@ -45,36 +41,17 @@ namespace Discord
}
}

/// <summary>
/// A 1-100 length description of this slash command
/// </summary>
public string Description
{
get
{
return _description;
}
set
{
Preconditions.Equals(value, "");

_description = value;
}
}

private string _name { get; set; }
private string _description { get; set; }

/// <summary>
/// Build the current builder into a <see cref="MessageCommandCreationProperties"/> class.
/// Build the current builder into a <see cref="ContextMenuCommandCreationProperties"/> class.
/// </summary>
/// <returns>A <see cref="MessageCommandCreationProperties"/> that can be used to create user commands over rest.</returns>
public MessageCommandCreationProperties Build()
/// <returns>A <see cref="ContextMenuCommandCreationProperties"/> that can be used to create message commands over rest.</returns>
public ContextMenuCommandCreationProperties Build()
{
MessageCommandCreationProperties props = new MessageCommandCreationProperties()
ContextMenuCommandCreationProperties props = new ContextMenuCommandCreationProperties()
{
Name = this.Name,
Description = this.Description,
Type=ApplicationCommandType.Message
};

@@ -93,17 +70,6 @@ namespace Discord
{
this.Name = name;
return this;
}

/// <summary>
/// Sets the description of the current command.
/// </summary>
/// <param name="description">The description of this command.</param>
/// <returns>The current builder.</returns>
public MessageCommandBuilder WithDescription(string description)
{
this.Description = description;
return this;
}
}
}
}

+ 7
- 41
src/Discord.Net.Core/Entities/Interactions/UserCommandBuilder.cs View File

@@ -8,7 +8,7 @@ using System.Threading.Tasks;
namespace Discord
{
/// <summary>
/// A class used to build slash commands.
/// A class used to build user commands.
/// </summary>
public class UserCommandBuilder
{
@@ -16,13 +16,9 @@ namespace Discord
/// Returns the maximun length a commands name allowed by Discord
/// </summary>
public const int MaxNameLength = 32;
/// <summary>
/// Returns the maximum length of a commands description allowed by Discord.
/// </summary>
public const int MaxDescriptionLength = 0;

/// <summary>
/// The name of this slash command.
/// The name of this User command.
/// </summary>
public string Name
{
@@ -45,36 +41,17 @@ namespace Discord
}
}

/// <summary>
/// A 1-100 length description of this slash command
/// </summary>
public string Description
{
get
{
return _description;
}
set
{
Preconditions.Equals(value, "");

_description = value;
}
}

private string _name { get; set; }
private string _description { get; set; }

/// <summary>
/// Build the current builder into a <see cref="UserCommandCreationProperties"/> class.
/// Build the current builder into a <see cref="ContextMenuCommandCreationProperties"/> class.
/// </summary>
/// <returns>A <see cref="UserCommandCreationProperties"/> that can be used to create user commands over rest.</returns>
public UserCommandCreationProperties Build()
/// <returns>A <see cref="ContextMenuCommandCreationProperties"/> that can be used to create user commands over rest.</returns>
public ContextMenuCommandCreationProperties Build()
{
UserCommandCreationProperties props = new UserCommandCreationProperties()
ContextMenuCommandCreationProperties props = new ContextMenuCommandCreationProperties()
{
Name = this.Name,
Description = this.Description,
Type=ApplicationCommandType.User
};

@@ -93,17 +70,6 @@ namespace Discord
{
this.Name = name;
return this;
}

/// <summary>
/// Sets the description of the current command.
/// </summary>
/// <param name="description">The description of this command.</param>
/// <returns>The current builder.</returns>
public UserCommandBuilder WithDescription(string description)
{
this.Description = description;
return this;
}
}
}
}

+ 1
- 1
src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs View File

@@ -411,7 +411,7 @@ namespace Discord
UrlValidation.Validate(Url);
if (!string.IsNullOrEmpty(ThumbnailUrl))
UrlValidation.Validate(ThumbnailUrl);
if (!string.IsNullOrEmpty(ImageUrl))
if (!string.IsNullOrEmpty(ImageUrl) && !ImageUrl.StartsWith("attachment://", StringComparison.Ordinal))
UrlValidation.Validate(ImageUrl);
if (Author != null)
{


+ 1
- 1
src/Discord.Net.Core/Entities/Messages/MessageProperties.cs View File

@@ -22,7 +22,7 @@ namespace Discord
/// Gets or sets a single embed for this message.
/// </summary>
/// <remarks>
/// This property will be added to the <see cref="Embed"/> array, in the future please use the array rather then this property.
/// This property will be added to the <see cref="Embeds"/> array, in the future please use the array rather than this property.
/// </remarks>
public Optional<Embed> Embed { get; set; }



+ 49
- 6
src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs View File

@@ -81,8 +81,20 @@ namespace Discord
public bool ManageRoles => Permissions.GetValue(RawValue, GuildPermission.ManageRoles);
/// <summary> If <c>true</c>, a user may edit the webhooks for this guild. </summary>
public bool ManageWebhooks => Permissions.GetValue(RawValue, GuildPermission.ManageWebhooks);
/// <summary> If <c>true</c>, a user may edit the emojis for this guild. </summary>
/// <summary> If <c>true</c>, a user may edit the emojis and stickers for this guild. </summary>
public bool ManageEmojisAndStickers => Permissions.GetValue(RawValue, GuildPermission.ManageEmojisAndStickers);
/// <summary> If <c>true</c>, a user may use slash commands in this guild. </summary>
public bool UseSlashCommands => Permissions.GetValue(RawValue, GuildPermission.UseSlashCommands);
/// <summary> If <c>true</c>, a user may request to speak in stage channels. </summary>
public bool RequestToSpeak => Permissions.GetValue(RawValue, GuildPermission.RequestToSpeak);
/// <summary> If <c>true</c>, a user may manage threads in this guild. </summary>
public bool ManageThreads => Permissions.GetValue(RawValue, GuildPermission.ManageThreads);
/// <summary> If <c>true</c>, a user may create public threads in this guild. </summary>
public bool UsePublicThreads => Permissions.GetValue(RawValue, GuildPermission.UsePublicThreads);
/// <summary> If <c>true</c>, a user may create private threads in this guild. </summary>
public bool UsePrivateThreads => Permissions.GetValue(RawValue, GuildPermission.UsePrivateThreads);
/// <summary> If <c>true</c>, a user may use external stickers in this guild. </summary>
public bool UseExternalStickers => Permissions.GetValue(RawValue, GuildPermission.UseExternalStickers);

/// <summary> Creates a new <see cref="GuildPermissions"/> with the provided packed value. </summary>
public GuildPermissions(ulong rawValue) { RawValue = rawValue; }
@@ -121,7 +133,13 @@ namespace Discord
bool? manageNicknames = null,
bool? manageRoles = null,
bool? manageWebhooks = null,
bool? manageEmojisAndStickers = null)
bool? manageEmojisAndStickers = null,
bool? useSlashCommands = null,
bool? requestToSpeak = null,
bool? manageThreads = null,
bool? usePublicThreads = null,
bool? usePrivateThreads = null,
bool? useExternalStickers = null)
{
ulong value = initialValue;

@@ -156,6 +174,12 @@ namespace Discord
Permissions.SetValue(ref value, manageRoles, GuildPermission.ManageRoles);
Permissions.SetValue(ref value, manageWebhooks, GuildPermission.ManageWebhooks);
Permissions.SetValue(ref value, manageEmojisAndStickers, GuildPermission.ManageEmojisAndStickers);
Permissions.SetValue(ref value, useSlashCommands, GuildPermission.UseSlashCommands);
Permissions.SetValue(ref value, requestToSpeak, GuildPermission.RequestToSpeak);
Permissions.SetValue(ref value, manageThreads, GuildPermission.ManageThreads);
Permissions.SetValue(ref value, usePublicThreads, GuildPermission.UsePublicThreads);
Permissions.SetValue(ref value, usePrivateThreads, GuildPermission.UseExternalStickers);
Permissions.SetValue(ref value, useExternalStickers, GuildPermission.UseExternalStickers);

RawValue = value;
}
@@ -192,7 +216,13 @@ namespace Discord
bool manageNicknames = false,
bool manageRoles = false,
bool manageWebhooks = false,
bool manageEmojis = false)
bool manageEmojisAndStickers = false,
bool useSlashCommands = false,
bool requestToSpeak = false,
bool manageThreads = false,
bool usePublicThreads = false,
bool usePrivateThreads = false,
bool useExternalStickers = false)
: this(0,
createInstantInvite: createInstantInvite,
manageRoles: manageRoles,
@@ -224,7 +254,13 @@ namespace Discord
changeNickname: changeNickname,
manageNicknames: manageNicknames,
manageWebhooks: manageWebhooks,
manageEmojisAndStickers: manageEmojis)
manageEmojisAndStickers: manageEmojisAndStickers,
useSlashCommands: useSlashCommands,
requestToSpeak: requestToSpeak,
manageThreads: manageThreads,
usePublicThreads: usePublicThreads,
usePrivateThreads: usePrivateThreads,
useExternalStickers: useExternalStickers)
{ }

/// <summary> Creates a new <see cref="GuildPermissions"/> from this one, changing the provided non-null permissions. </summary>
@@ -259,11 +295,18 @@ namespace Discord
bool? manageNicknames = null,
bool? manageRoles = null,
bool? manageWebhooks = null,
bool? manageEmojis = null)
bool? manageEmojisAndStickers = null,
bool? useSlashCommands = null,
bool? requestToSpeak = null,
bool? manageThreads = null,
bool? usePublicThreads = null,
bool? usePrivateThreads = null,
bool? useExternalStickers = null)
=> new GuildPermissions(RawValue, createInstantInvite, kickMembers, banMembers, administrator, manageChannels, manageGuild, addReactions,
viewAuditLog, viewGuildInsights, viewChannel, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles,
readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, moveMembers,
useVoiceActivation, prioritySpeaker, stream, changeNickname, manageNicknames, manageRoles, manageWebhooks, manageEmojis);
useVoiceActivation, prioritySpeaker, stream, changeNickname, manageNicknames, manageRoles, manageWebhooks, manageEmojisAndStickers,
useSlashCommands, requestToSpeak, manageThreads, usePublicThreads, usePrivateThreads, useExternalStickers);

/// <summary>
/// Returns a value that indicates if a specific <see cref="GuildPermission"/> is enabled


+ 59
- 42
src/Discord.Net.Core/Entities/Roles/Color.cs View File

@@ -10,68 +10,70 @@ namespace Discord
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public struct Color
{
/// <summary> Gets the max decimal value of color. </summary>
public const uint MaxDecimalValue = 0xFFFFFF;
/// <summary> Gets the default user color value. </summary>
public static readonly Color Default = new Color(0);
public static readonly Color Default = new(0);
/// <summary> Gets the teal color value. </summary>
/// <returns> A color struct with the hex value of <see href="http://www.color-hex.com/color/1ABC9C">1ABC9C</see>.</returns>
public static readonly Color Teal = new Color(0x1ABC9C);
public static readonly Color Teal = new(0x1ABC9C);
/// <summary> Gets the dark teal color value. </summary>
/// <returns> A color struct with the hex value of <see href="http://www.color-hex.com/color/11806A">11806A</see>.</returns>
public static readonly Color DarkTeal = new Color(0x11806A);
public static readonly Color DarkTeal = new(0x11806A);
/// <summary> Gets the green color value. </summary>
/// <returns> A color struct with the hex value of <see href="http://www.color-hex.com/color/2ECC71">2ECC71</see>.</returns>
public static readonly Color Green = new Color(0x2ECC71);
public static readonly Color Green = new(0x2ECC71);
/// <summary> Gets the dark green color value. </summary>
/// <returns> A color struct with the hex value of <see href="http://www.color-hex.com/color/1F8B4C">1F8B4C</see>.</returns>
public static readonly Color DarkGreen = new Color(0x1F8B4C);
public static readonly Color DarkGreen = new(0x1F8B4C);
/// <summary> Gets the blue color value. </summary>
/// <returns> A color struct with the hex value of <see href="http://www.color-hex.com/color/3498DB">3498DB</see>.</returns>
public static readonly Color Blue = new Color(0x3498DB);
public static readonly Color Blue = new(0x3498DB);
/// <summary> Gets the dark blue color value. </summary>
/// <returns> A color struct with the hex value of <see href="http://www.color-hex.com/color/206694">206694</see>.</returns>
public static readonly Color DarkBlue = new Color(0x206694);
public static readonly Color DarkBlue = new(0x206694);
/// <summary> Gets the purple color value. </summary>
/// <returns> A color struct with the hex value of <see href="http://www.color-hex.com/color/9B59B6">9B59B6</see>.</returns>
public static readonly Color Purple = new Color(0x9B59B6);
public static readonly Color Purple = new(0x9B59B6);
/// <summary> Gets the dark purple color value. </summary>
/// <returns> A color struct with the hex value of <see href="http://www.color-hex.com/color/71368A">71368A</see>.</returns>
public static readonly Color DarkPurple = new Color(0x71368A);
public static readonly Color DarkPurple = new(0x71368A);
/// <summary> Gets the magenta color value. </summary>
/// <returns> A color struct with the hex value of <see href="http://www.color-hex.com/color/E91E63">E91E63</see>.</returns>
public static readonly Color Magenta = new Color(0xE91E63);
public static readonly Color Magenta = new(0xE91E63);
/// <summary> Gets the dark magenta color value. </summary>
/// <returns> A color struct with the hex value of <see href="http://www.color-hex.com/color/AD1457">AD1457</see>.</returns>
public static readonly Color DarkMagenta = new Color(0xAD1457);
public static readonly Color DarkMagenta = new(0xAD1457);
/// <summary> Gets the gold color value. </summary>
/// <returns> A color struct with the hex value of <see href="http://www.color-hex.com/color/F1C40F">F1C40F</see>.</returns>
public static readonly Color Gold = new Color(0xF1C40F);
public static readonly Color Gold = new(0xF1C40F);
/// <summary> Gets the light orange color value. </summary>
/// <returns> A color struct with the hex value of <see href="http://www.color-hex.com/color/C27C0E">C27C0E</see>.</returns>
public static readonly Color LightOrange = new Color(0xC27C0E);
public static readonly Color LightOrange = new(0xC27C0E);
/// <summary> Gets the orange color value. </summary>
/// <returns> A color struct with the hex value of <see href="http://www.color-hex.com/color/E67E22">E67E22</see>.</returns>
public static readonly Color Orange = new Color(0xE67E22);
public static readonly Color Orange = new(0xE67E22);
/// <summary> Gets the dark orange color value. </summary>
/// <returns> A color struct with the hex value of <see href="http://www.color-hex.com/color/A84300">A84300</see>.</returns>
public static readonly Color DarkOrange = new Color(0xA84300);
public static readonly Color DarkOrange = new(0xA84300);
/// <summary> Gets the red color value. </summary>
/// <returns> A color struct with the hex value of <see href="http://www.color-hex.com/color/E74C3C">E74C3C</see>.</returns>
public static readonly Color Red = new Color(0xE74C3C);
public static readonly Color Red = new(0xE74C3C);
/// <summary> Gets the dark red color value. </summary>
/// <returns> A color struct with the hex value of <see href="http://www.color-hex.com/color/992D22">992D22</see>.</returns>
public static readonly Color DarkRed = new Color(0x992D22);
public static readonly Color DarkRed = new(0x992D22);
/// <summary> Gets the light grey color value. </summary>
/// <returns> A color struct with the hex value of <see href="http://www.color-hex.com/color/979C9F">979C9F</see>.</returns>
public static readonly Color LightGrey = new Color(0x979C9F);
public static readonly Color LightGrey = new(0x979C9F);
/// <summary> Gets the lighter grey color value. </summary>
/// <returns> A color struct with the hex value of <see href="http://www.color-hex.com/color/95A5A6">95A5A6</see>.</returns>
public static readonly Color LighterGrey = new Color(0x95A5A6);
public static readonly Color LighterGrey = new(0x95A5A6);
/// <summary> Gets the dark grey color value. </summary>
/// <returns> A color struct with the hex value of <see href="http://www.color-hex.com/color/607D8B">607D8B</see>.</returns>
public static readonly Color DarkGrey = new Color(0x607D8B);
public static readonly Color DarkGrey = new(0x607D8B);
/// <summary> Gets the darker grey color value. </summary>
/// <returns> A color struct with the hex value of <see href="http://www.color-hex.com/color/546E7A">546E7A</see>.</returns>
public static readonly Color DarkerGrey = new Color(0x546E7A);
public static readonly Color DarkerGrey = new(0x546E7A);

/// <summary> Gets the encoded value for this color. </summary>
/// <remarks>
@@ -91,22 +93,27 @@ namespace Discord
/// Initializes a <see cref="Color"/> struct with the given raw value.
/// </summary>
/// <example>
/// The following will create a color that has a hex value of
/// The following will create a color that has a hex value of
/// <see href="http://www.color-hex.com/color/607d8b">#607D8B</see>.
/// <code language="cs">
/// Color darkGrey = new Color(0x607D8B);
/// </code>
/// </example>
/// <param name="rawValue">The raw value of the color (e.g. <c>0x607D8B</c>).</param>
/// <exception cref="ArgumentException">Value exceeds <see cref="MaxDecimalValue"/>.</exception>
public Color(uint rawValue)
{
if (rawValue > MaxDecimalValue)
throw new ArgumentException($"{nameof(RawValue)} of color cannot be greater than {MaxDecimalValue}!", nameof(rawValue));

RawValue = rawValue;
}

/// <summary>
/// Initializes a <see cref="Color" /> struct with the given RGB bytes.
/// </summary>
/// <example>
/// The following will create a color that has a value of
/// The following will create a color that has a value of
/// <see href="http://www.color-hex.com/color/607d8b">#607D8B</see>.
/// <code language="cs">
/// Color darkGrey = new Color((byte)0b_01100000, (byte)0b_01111101, (byte)0b_10001011);
@@ -115,19 +122,24 @@ namespace Discord
/// <param name="r">The byte that represents the red color.</param>
/// <param name="g">The byte that represents the green color.</param>
/// <param name="b">The byte that represents the blue color.</param>
/// <exception cref="ArgumentException">Value exceeds <see cref="MaxDecimalValue"/>.</exception>
public Color(byte r, byte g, byte b)
{
RawValue =
((uint)r << 16) |
((uint)g << 8) |
(uint)b;
uint value = ((uint)r << 16)
| ((uint)g << 8)
| (uint)b;

if (value > MaxDecimalValue)
throw new ArgumentException($"{nameof(RawValue)} of color cannot be greater than {MaxDecimalValue}!");

RawValue = value;
}

/// <summary>
/// Initializes a <see cref="Color"/> struct with the given RGB value.
/// </summary>
/// <example>
/// The following will create a color that has a value of
/// The following will create a color that has a value of
/// <see href="http://www.color-hex.com/color/607d8b">#607D8B</see>.
/// <code language="cs">
/// Color darkGrey = new Color(96, 125, 139);
@@ -145,16 +157,15 @@ namespace Discord
throw new ArgumentOutOfRangeException(nameof(g), "Value must be within [0,255].");
if (b < 0 || b > 255)
throw new ArgumentOutOfRangeException(nameof(b), "Value must be within [0,255].");
RawValue =
((uint)r << 16) |
((uint)g << 8) |
(uint)b;
RawValue = ((uint)r << 16)
| ((uint)g << 8)
| (uint)b;
}
/// <summary>
/// Initializes a <see cref="Color"/> struct with the given RGB float value.
/// </summary>
/// <example>
/// The following will create a color that has a value of
/// The following will create a color that has a value of
/// <see href="http://www.color-hex.com/color/607c8c">#607c8c</see>.
/// <code language="cs">
/// Color darkGrey = new Color(0.38f, 0.49f, 0.55f);
@@ -172,10 +183,9 @@ namespace Discord
throw new ArgumentOutOfRangeException(nameof(g), "Value must be within [0,1].");
if (b < 0.0f || b > 1.0f)
throw new ArgumentOutOfRangeException(nameof(b), "Value must be within [0,1].");
RawValue =
((uint)(r * 255.0f) << 16) |
((uint)(g * 255.0f) << 8) |
(uint)(b * 255.0f);
RawValue = ((uint)(r * 255.0f) << 16)
| ((uint)(g * 255.0f) << 8)
| (uint)(b * 255.0f);
}

public static bool operator ==(Color lhs, Color rhs)
@@ -184,15 +194,22 @@ namespace Discord
public static bool operator !=(Color lhs, Color rhs)
=> lhs.RawValue != rhs.RawValue;

public static implicit operator Color(uint rawValue)
=> new(rawValue);

public static implicit operator uint(Color color)
=> color.RawValue;

public override bool Equals(object obj)
=> (obj is Color c && RawValue == c.RawValue);
=> obj is Color c && RawValue == c.RawValue;

public override int GetHashCode() => RawValue.GetHashCode();

public static implicit operator StandardColor(Color color) =>
StandardColor.FromArgb((int)color.RawValue);
public static explicit operator Color(StandardColor color) =>
new Color((uint)color.ToArgb() << 8 >> 8);
public static implicit operator StandardColor(Color color)
=> StandardColor.FromArgb((int)color.RawValue);

public static explicit operator Color(StandardColor color)
=> new((uint)color.ToArgb() << 8 >> 8);

/// <summary>
/// Gets the hexadecimal representation of the color (e.g. <c>#000ccc</c>).


+ 27
- 5
src/Discord.Net.Core/Entities/Users/IUser.cs View File

@@ -12,17 +12,29 @@ namespace Discord
/// </summary>
string AvatarId { get; }
/// <summary>
/// Gets the identifier of this user's banner.
/// </summary>
string BannerId { get; }
/// <summary>
/// Gets the user's banner color.
/// </summary>
/// <returns>
/// A <see cref="Color"/> struct representing the accent color of this user's banner.
/// </returns>
Color? AccentColor { get; }
/// <summary>
/// Gets the avatar URL for this user.
/// </summary>
/// <remarks>
/// This property retrieves a URL for this user's avatar. In event that the user does not have a valid avatar
/// (i.e. their avatar identifier is not set), this property will return <c>null</c>. If you wish to
/// (i.e. their avatar identifier is not set), this method will return <c>null</c>. If you wish to
/// retrieve the default avatar for this user, consider using <see cref="IUser.GetDefaultAvatarUrl"/> (see
/// example).
/// </remarks>
/// <example>
/// <para>The following example attempts to retrieve the user's current avatar and send it to a channel; if one is
/// not set, a default avatar for this user will be returned instead.</para>
/// <para
/// >The following example attempts to retrieve the user's current avatar and send it to a channel; if one is
/// not set, a default avatar for this user will be returned instead.</para>
/// <code language="cs" region="GetAvatarUrl"
/// source="..\..\..\Discord.Net.Examples\Core\Entities\Users\IUser.Examples.cs"/>
/// </example>
@@ -34,6 +46,16 @@ namespace Discord
/// </returns>
string GetAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128);
/// <summary>
/// Gets the banner URL for this user.
/// </summary>
/// <param name="format">The format to return.</param>
/// <param name="size">The size of the image to return in. This can be any power of two between 16 and 2048.
/// </param>
/// <returns>
/// A string representing the user's avatar URL; <c>null</c> if the user does not have an banner in place.
/// </returns>
string GetBannerUrl(ImageFormat format = ImageFormat.Auto, ushort size = 256);
/// <summary>
/// Gets the default avatar URL for this user.
/// </summary>
/// <remarks>
@@ -93,8 +115,8 @@ namespace Discord
/// This method is used to obtain or create a channel used to send a direct message.
/// <note type="warning">
/// In event that the current user cannot send a message to the target user, a channel can and will
/// still be created by Discord. However, attempting to send a message will yield a
/// <see cref="Discord.Net.HttpException"/> with a 403 as its
/// still be created by Discord. However, attempting to send a message will yield a
/// <see cref="Discord.Net.HttpException"/> with a 403 as its
/// <see cref="Discord.Net.HttpException.HttpCode"/>. There are currently no official workarounds by
/// Discord.
/// </note>


+ 6
- 1
src/Discord.Net.Core/Entities/Webhooks/IWebhook.cs View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Threading.Tasks;

namespace Discord
@@ -49,6 +49,11 @@ namespace Discord
/// </summary>
IUser Creator { get; }

/// <summary>
/// Gets the ID of the application owning this webhook.
/// </summary>
ulong? ApplicationId { get; }

/// <summary>
/// Modifies this webhook.
/// </summary>


+ 2
- 2
src/Discord.Net.Rest/API/Common/Game.cs View File

@@ -41,8 +41,8 @@ namespace Discord.API
public Optional<Emoji> Emoji { get; set; }
[JsonProperty("created_at")]
public Optional<long> CreatedAt { get; set; }
[JsonProperty("buttons")]
public Optional<RichPresenceButton[]> Buttons { get; set; }
//[JsonProperty("buttons")]
//public Optional<RichPresenceButton[]> Buttons { get; set; }

[OnError]
internal void OnError(StreamingContext context, ErrorContext errorContext)


+ 1
- 1
src/Discord.Net.Rest/API/Common/InteractionCallbackData.cs View File

@@ -18,7 +18,7 @@ namespace Discord.API

// New flags prop. this make the response "ephemeral". see https://discord.com/developers/docs/interactions/slash-commands#interaction-response-interactionapplicationcommandcallbackdata
[JsonProperty("flags")]
public Optional<int> Flags { get; set; }
public Optional<MessageFlags> Flags { get; set; }

[JsonProperty("components")]
public Optional<API.ActionRowComponent[]> Components { get; set; }


+ 0
- 18
src/Discord.Net.Rest/API/Common/RichPresenceButton.cs View File

@@ -1,18 +0,0 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Discord.API
{
internal class RichPresenceButton
{
[JsonProperty("label")]
public string Label { get; set; }

[JsonProperty("url")]
public string Url { get; set; }
}
}

+ 4
- 0
src/Discord.Net.Rest/API/Common/User.cs View File

@@ -15,6 +15,10 @@ namespace Discord.API
public Optional<bool> Bot { get; set; }
[JsonProperty("avatar")]
public Optional<string> Avatar { get; set; }
[JsonProperty("banner")]
public Optional<string> Banner { get; set; }
[JsonProperty("accent_color")]
public Optional<uint?> AccentColor { get; set; }

//CurrentUser
[JsonProperty("verified")]


+ 2
- 0
src/Discord.Net.Rest/API/Common/Webhook.cs View File

@@ -21,5 +21,7 @@ namespace Discord.API

[JsonProperty("user")]
public Optional<User> Creator { get; set; }
[JsonProperty("application_id")]
public ulong? ApplicationId { get; set; }
}
}

+ 1
- 1
src/Discord.Net.Rest/API/Rest/CreateWebhookMessageParams.cs View File

@@ -28,7 +28,7 @@ namespace Discord.API.Rest
public Optional<AllowedMentions> AllowedMentions { get; set; }

[JsonProperty("flags")]
public Optional<int> Flags { get; set; }
public Optional<MessageFlags> Flags { get; set; }

[JsonProperty("components")]
public Optional<API.ActionRowComponent[]> Components { get; set; }


+ 2
- 0
src/Discord.Net.Rest/API/Rest/ModifyWebhookMessageParams.cs View File

@@ -12,5 +12,7 @@ namespace Discord.API.Rest
public Optional<Embed[]> Embeds { get; set; }
[JsonProperty("allowed_mentions")]
public Optional<AllowedMentions> AllowedMentions { get; set; }
[JsonProperty("components")]
public Optional<API.ActionRowComponent[]> Components { get; set; }
}
}

+ 3
- 3
src/Discord.Net.Rest/Discord.Net.Rest.csproj View File

@@ -9,11 +9,11 @@
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard2.0;netstandard2.1</TargetFrameworks>
<PackageIcon>Temporary.png</PackageIcon>
<PackageProjectUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</PackageProjectUrl>
<Version>3.0.0-pre</Version>
<Version>3.0.1-pre</Version>
<PackageId>Discord.Net.Labs.Rest</PackageId>
<RepositoryUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</RepositoryUrl>
<AssemblyVersion>2.3.4</AssemblyVersion>
<FileVersion>2.3.4</FileVersion>
<AssemblyVersion>3.0.1</AssemblyVersion>
<FileVersion>3.0.1</FileVersion>
</PropertyGroup>
<PropertyGroup>
<DocumentationFile>..\Discord.Net.Rest\Discord.Net.Rest.xml</DocumentationFile>


+ 27
- 2
src/Discord.Net.Rest/Discord.Net.Rest.xml View File

@@ -3493,6 +3493,16 @@
of webhooks found within the guild.
</returns>
</member>
<member name="M:Discord.Rest.RestGuild.GetApplicationCommandsAsync(Discord.RequestOptions)">
<summary>
Gets this guilds slash commands commands
</summary>
<param name="options">The options to be used when sending the request.</param>
<returns>
A task that represents the asynchronous get operation. The task result contains a read-only collection
of application commands found within the guild.
</returns>
</member>
<member name="M:Discord.Rest.RestGuild.ToString">
<summary>
Returns the name of the guild.
@@ -3650,6 +3660,9 @@
<member name="M:Discord.Rest.RestGuild.Discord#IGuild#GetWebhooksAsync(Discord.RequestOptions)">
<inheritdoc />
</member>
<member name="M:Discord.Rest.RestGuild.Discord#IGuild#GetApplicationCommandsAsync(Discord.RequestOptions)">
<inheritdoc />
</member>
<member name="P:Discord.Rest.RestGuildIntegration.Name">
<inheritdoc />
</member>
@@ -4667,7 +4680,7 @@
</member>
<member name="T:Discord.Rest.RestThreadUser">
<summary>
Represents a thread user recieved over the REST api.
Represents a thread user received over the REST api.
</summary>
</member>
<member name="P:Discord.Rest.RestThreadUser.Thread">
@@ -4690,7 +4703,7 @@
Gets the guild user for this thread user.
</summary>
<returns>
A task representing the asyncronous get operation. The task returns a
A task representing the asynchronous get operation. The task returns a
<see cref="T:Discord.IGuildUser"/> that represents the current thread user.
</returns>
</member>
@@ -4711,6 +4724,12 @@
<member name="P:Discord.Rest.RestUser.AvatarId">
<inheritdoc />
</member>
<member name="P:Discord.Rest.RestUser.BannerId">
<inheritdoc />
</member>
<member name="P:Discord.Rest.RestUser.AccentColor">
<inheritdoc />
</member>
<member name="P:Discord.Rest.RestUser.PublicFlags">
<inheritdoc />
</member>
@@ -4753,6 +4772,9 @@
<member name="M:Discord.Rest.RestUser.GetAvatarUrl(Discord.ImageFormat,System.UInt16)">
<inheritdoc />
</member>
<member name="M:Discord.Rest.RestUser.GetBannerUrl(Discord.ImageFormat,System.UInt16)">
<inheritdoc />
</member>
<member name="M:Discord.Rest.RestUser.GetDefaultAvatarUrl">
<inheritdoc />
</member>
@@ -4875,6 +4897,9 @@
<member name="P:Discord.Rest.RestWebhook.Creator">
<inheritdoc />
</member>
<member name="P:Discord.Rest.RestWebhook.ApplicationId">
<inheritdoc />
</member>
<member name="P:Discord.Rest.RestWebhook.CreatedAt">
<inheritdoc />
</member>


+ 6
- 56
src/Discord.Net.Rest/DiscordRestApiClient.cs View File

@@ -55,7 +55,7 @@ namespace Discord.API
_restClientProvider = restClientProvider;
UserAgent = userAgent;
DefaultRetryMode = defaultRetryMode;
_serializer = serializer ?? new JsonSerializer { ContractResolver = new DiscordContractResolver(), NullValueHandling = NullValueHandling.Ignore };
_serializer = serializer ?? new JsonSerializer { ContractResolver = new DiscordContractResolver(), NullValueHandling = NullValueHandling.Include };
UseSystemClock = useSystemClock;

RequestQueue = new RequestQueue();
@@ -1113,11 +1113,8 @@ namespace Discord.API
Preconditions.NotNull(command, nameof(command));
Preconditions.AtMost(command.Name.Length, 32, nameof(command.Name));
Preconditions.AtLeast(command.Name.Length, 3, nameof(command.Name));
Preconditions.Equals(command.Description, "");

options = RequestOptions.CreateOrClone(options);

options = RequestOptions.CreateOrClone(options);

return await TrySendApplicationCommand(SendJsonAsync<ApplicationCommand>("POST", () => $"applications/{this.CurrentUserId}/commands", command, new BucketIds(), options: options)).ConfigureAwait(false);
}
@@ -1126,12 +1123,9 @@ namespace Discord.API
Preconditions.NotNull(command, nameof(command));
Preconditions.AtMost(command.Name.Length, 32, nameof(command.Name));
Preconditions.AtLeast(command.Name.Length, 3, nameof(command.Name));
Preconditions.Equals(command.Description, "");

options = RequestOptions.CreateOrClone(options);



return await TrySendApplicationCommand(SendJsonAsync<ApplicationCommand>("POST", () => $"applications/{this.CurrentUserId}/commands", command, new BucketIds(), options: options)).ConfigureAwait(false);
}

@@ -1174,7 +1168,6 @@ namespace Discord.API
var bucket = new BucketIds(guildId: guildId);

return await TrySendApplicationCommand(SendJsonAsync<ApplicationCommand>("POST", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands", command, bucket, options: options)).ConfigureAwait(false);

}
public async Task<ApplicationCommand> ModifyGuildApplicationCommandAsync(ModifyApplicationCommandParams command, ulong guildId, ulong commandId, RequestOptions options = null)
{
@@ -1182,21 +1175,7 @@ namespace Discord.API

var bucket = new BucketIds(guildId: guildId);

try
{
return await SendJsonAsync<ApplicationCommand>("PATCH", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands/{commandId}", command, bucket, options: options).ConfigureAwait(false);
}
catch (HttpException x)
{
if (x.HttpCode == HttpStatusCode.BadRequest)
{
var json = (x.Request as JsonRestRequest).Json;
throw new ApplicationCommandException(json, x);
}

// Re-throw the http exception
throw;
}
return await TrySendApplicationCommand(SendJsonAsync<ApplicationCommand>("PATCH", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands/{commandId}", command, bucket, options: options)).ConfigureAwait(false);
}
public async Task DeleteGuildApplicationCommandAsync(ulong guildId, ulong commandId, RequestOptions options = null)
{
@@ -1223,29 +1202,15 @@ namespace Discord.API
var bucket = new BucketIds(guildId: guildId);

return await TrySendApplicationCommand(SendJsonAsync<ApplicationCommand>("POST", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands", command, bucket, options: options)).ConfigureAwait(false);

}

public async Task<ApplicationCommand> ModifyGuildApplicationUserCommandAsync(ModifyApplicationCommandParams command, ulong guildId, ulong commandId, RequestOptions options = null)
{
options = RequestOptions.CreateOrClone(options);

var bucket = new BucketIds(guildId: guildId);

try
{
return await SendJsonAsync<ApplicationCommand>("PATCH", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands/{commandId}", command, bucket, options: options).ConfigureAwait(false);
}
catch (HttpException x)
{
if (x.HttpCode == HttpStatusCode.BadRequest)
{
var json = (x.Request as JsonRestRequest).Json;
throw new ApplicationCommandException(json, x);
}

// Re-throw the http exception
throw;
}
return await TrySendApplicationCommand(SendJsonAsync<ApplicationCommand>("PATCH", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands/{commandId}", command, bucket, options: options)).ConfigureAwait(false);
}
public async Task<ApplicationCommand[]> BulkOverwriteGuildApplicationUserCommands(ulong guildId, CreateApplicationCommandParams[] commands, RequestOptions options = null)
{
@@ -1263,7 +1228,6 @@ namespace Discord.API
var bucket = new BucketIds(guildId: guildId);

return await TrySendApplicationCommand(SendJsonAsync<ApplicationCommand>("POST", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands", command, bucket, options: options)).ConfigureAwait(false);

}
public async Task<ApplicationCommand> ModifyGuildApplicationMessageCommandAsync(ModifyApplicationCommandParams command, ulong guildId, ulong commandId, RequestOptions options = null)
{
@@ -1271,21 +1235,7 @@ namespace Discord.API

var bucket = new BucketIds(guildId: guildId);

try
{
return await SendJsonAsync<ApplicationCommand>("PATCH", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands/{commandId}", command, bucket, options: options).ConfigureAwait(false);
}
catch (HttpException x)
{
if (x.HttpCode == HttpStatusCode.BadRequest)
{
var json = (x.Request as JsonRestRequest).Json;
throw new ApplicationCommandException(json, x);
}

// Re-throw the http exception
throw;
}
return await TrySendApplicationCommand(SendJsonAsync<ApplicationCommand>("PATCH", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands/{commandId}", command, bucket, options: options)).ConfigureAwait(false);
}

public async Task<ApplicationCommand[]> BulkOverwriteGuildApplicationMessageCommands(ulong guildId, CreateApplicationCommandParams[] commands, RequestOptions options = null)


+ 8
- 8
src/Discord.Net.Rest/DiscordRestClient.cs View File

@@ -112,25 +112,25 @@ namespace Discord.Rest
=> InteractionHelper.CreateGlobalCommand(this, properties, options);
public Task<RestGlobalCommand> CreateGlobalCommand(Action<SlashCommandCreationProperties> func, RequestOptions options = null)
=> InteractionHelper.CreateGlobalCommand(this, func, options);
public Task<RestGlobalUserCommand> CreateGlobalUserCommand(UserCommandCreationProperties properties, RequestOptions options = null)
public Task<RestGlobalUserCommand> CreateGlobalUserCommand(ContextMenuCommandCreationProperties properties, RequestOptions options = null)
=> InteractionHelper.CreateGlobalUserCommand(this, properties, options);
public Task<RestGlobalUserCommand> CreateGlobalUserCommand(Action<UserCommandCreationProperties> func, RequestOptions options = null)
public Task<RestGlobalUserCommand> CreateGlobalUserCommand(Action<ContextMenuCommandCreationProperties> func, RequestOptions options = null)
=> InteractionHelper.CreateGlobalUserCommand(this, func, options);
public Task<RestGlobalMessageCommand> CreateGlobalMessageCommand(MessageCommandCreationProperties properties, RequestOptions options = null)
public Task<RestGlobalMessageCommand> CreateGlobalMessageCommand(ContextMenuCommandCreationProperties properties, RequestOptions options = null)
=> InteractionHelper.CreateGlobalMessageCommand(this, properties, options);
public Task<RestGlobalMessageCommand> CreateGlobalMessageCommand(Action<MessageCommandCreationProperties> func, RequestOptions options = null)
public Task<RestGlobalMessageCommand> CreateGlobalMessageCommand(Action<ContextMenuCommandCreationProperties> func, RequestOptions options = null)
=> InteractionHelper.CreateGlobalMessageCommand(this, func, options);
public Task<RestGuildCommand> CreateGuildCommand(SlashCommandCreationProperties properties, ulong guildId, RequestOptions options = null)
=> InteractionHelper.CreateGuildCommand(this, guildId, properties, options);
public Task<RestGuildCommand> CreateGuildCommand(Action<SlashCommandCreationProperties> func, ulong guildId, RequestOptions options = null)
=> InteractionHelper.CreateGuildCommand(this, guildId, func, options);
public Task<RestGuildUserCommand> CreateGuildUserCommand(UserCommandCreationProperties properties, ulong guildId, RequestOptions options = null)
public Task<RestGuildUserCommand> CreateGuildUserCommand(ContextMenuCommandCreationProperties properties, ulong guildId, RequestOptions options = null)
=> InteractionHelper.CreateGuildUserCommand(this, guildId, properties, options);
public Task<RestGuildUserCommand> CreateGuildUserCommand(Action<UserCommandCreationProperties> func, ulong guildId, RequestOptions options = null)
public Task<RestGuildUserCommand> CreateGuildUserCommand(Action<ContextMenuCommandCreationProperties> func, ulong guildId, RequestOptions options = null)
=> InteractionHelper.CreateGuildUserCommand(this, guildId, func, options);
public Task<RestGuildMessageCommand> CreateGuildMessageCommand(MessageCommandCreationProperties properties, ulong guildId, RequestOptions options = null)
public Task<RestGuildMessageCommand> CreateGuildMessageCommand(ContextMenuCommandCreationProperties properties, ulong guildId, RequestOptions options = null)
=> InteractionHelper.CreateGuildMessageCommand(this, guildId, properties, options);
public Task<RestGuildMessageCommand> CreateGuildMessageCommand(Action<MessageCommandCreationProperties> func, ulong guildId, RequestOptions options = null)
public Task<RestGuildMessageCommand> CreateGuildMessageCommand(Action<ContextMenuCommandCreationProperties> func, ulong guildId, RequestOptions options = null)
=> InteractionHelper.CreateGuildMessageCommand(this, guildId, func, options);
public Task<IReadOnlyCollection<RestGlobalCommand>> GetGlobalApplicationCommands(RequestOptions options = null)
=> ClientHelper.GetGlobalApplicationCommands(this, options);


+ 15
- 1
src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs View File

@@ -869,6 +869,18 @@ namespace Discord.Rest
public Task<IReadOnlyCollection<RestWebhook>> GetWebhooksAsync(RequestOptions options = null)
=> GuildHelper.GetWebhooksAsync(this, Discord, options);

//Interactions
/// <summary>
/// Gets this guilds slash commands commands
/// </summary>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous get operation. The task result contains a read-only collection
/// of application commands found within the guild.
/// </returns>
public async Task<IReadOnlyCollection<RestApplicationCommand>> GetApplicationCommandsAsync (RequestOptions options = null)
=> await ClientHelper.GetGuildApplicationCommands(Discord, Id, options).ConfigureAwait(false);

/// <summary>
/// Returns the name of the guild.
/// </summary>
@@ -1154,6 +1166,8 @@ namespace Discord.Rest
/// <inheritdoc />
async Task<IReadOnlyCollection<IWebhook>> IGuild.GetWebhooksAsync(RequestOptions options)
=> await GetWebhooksAsync(options).ConfigureAwait(false);
/// <inheritdoc />
async Task<IReadOnlyCollection<IApplicationCommand>> IGuild.GetApplicationCommandsAsync (RequestOptions options)
=> await GetApplicationCommandsAsync(options).ConfigureAwait(false);
}
}

+ 61
- 36
src/Discord.Net.Rest/Entities/Interactions/InteractionHelper.cs View File

@@ -200,22 +200,20 @@ namespace Discord.Rest
await client.ApiClient.DeleteGlobalApplicationCommandAsync(command.Id, options).ConfigureAwait(false);
}

public static async Task<RestGlobalUserCommand> CreateGlobalUserCommand(BaseDiscordClient client, Action<UserCommandCreationProperties> func, RequestOptions options = null)
public static async Task<RestGlobalUserCommand> CreateGlobalUserCommand(BaseDiscordClient client, Action<ContextMenuCommandCreationProperties> func, RequestOptions options = null)
{
var args = new UserCommandCreationProperties();
var args = new ContextMenuCommandCreationProperties();
func(args);
return await CreateGlobalUserCommand(client, args, options).ConfigureAwait(false);
}

public static async Task<RestGlobalUserCommand> CreateGlobalUserCommand(BaseDiscordClient client, UserCommandCreationProperties arg, RequestOptions options = null)
public static async Task<RestGlobalUserCommand> CreateGlobalUserCommand(BaseDiscordClient client, ContextMenuCommandCreationProperties arg, RequestOptions options = null)
{
Preconditions.NotNullOrEmpty(arg.Name, nameof(arg.Name));
Preconditions.Equals(arg.Description, "");

var model = new CreateApplicationCommandParams()
{
Name = arg.Name,
Description = arg.Description,
Type = arg.Type
};

@@ -223,22 +221,20 @@ namespace Discord.Rest
return RestGlobalUserCommand.Create(client, cmd);
}

public static async Task<RestGlobalMessageCommand> CreateGlobalMessageCommand(BaseDiscordClient client, Action<MessageCommandCreationProperties> func, RequestOptions options = null)
public static async Task<RestGlobalMessageCommand> CreateGlobalMessageCommand(BaseDiscordClient client, Action<ContextMenuCommandCreationProperties> func, RequestOptions options = null)
{
var args = new MessageCommandCreationProperties();
var args = new ContextMenuCommandCreationProperties();
func(args);
return await CreateGlobalMessageCommand(client, args, options).ConfigureAwait(false);
}

public static async Task<RestGlobalMessageCommand> CreateGlobalMessageCommand(BaseDiscordClient client, MessageCommandCreationProperties arg, RequestOptions options = null)
public static async Task<RestGlobalMessageCommand> CreateGlobalMessageCommand(BaseDiscordClient client, ContextMenuCommandCreationProperties arg, RequestOptions options = null)
{
Preconditions.NotNullOrEmpty(arg.Name, nameof(arg.Name));
Preconditions.Equals(arg.Description, "");

var model = new CreateApplicationCommandParams()
{
Name = arg.Name,
Description = arg.Description,
Type = arg.Type
};

@@ -246,7 +242,7 @@ namespace Discord.Rest
return RestGlobalMessageCommand.Create(client, cmd);
}

public static async Task<IReadOnlyCollection<RestGlobalUserCommand>> BulkOverwriteGlobalUserCommands(BaseDiscordClient client, UserCommandCreationProperties[] args, RequestOptions options = null)
public static async Task<IReadOnlyCollection<RestGlobalUserCommand>> BulkOverwriteGlobalUserCommands(BaseDiscordClient client, ContextMenuCommandCreationProperties[] args, RequestOptions options = null)
{
Preconditions.NotNull(args, nameof(args));

@@ -255,13 +251,11 @@ namespace Discord.Rest
foreach (var arg in args)
{
Preconditions.NotNullOrEmpty(arg.Name, nameof(arg.Name));
Preconditions.NotNullOrEmpty(arg.Description, nameof(arg.Description));
Preconditions.Equals(arg.Type, ApplicationCommandType.User);

var model = new CreateApplicationCommandParams()
{
Name = arg.Name,
Description = arg.Description,
Type = arg.Type
};

@@ -307,7 +301,7 @@ namespace Discord.Rest
await client.ApiClient.DeleteGlobalApplicationCommandAsync(command.Id, options).ConfigureAwait(false);
}

public static async Task<IReadOnlyCollection<RestGlobalMessageCommand>> BulkOverwriteGlobalMessageCommands(BaseDiscordClient client, MessageCommandCreationProperties[] args, RequestOptions options = null)
public static async Task<IReadOnlyCollection<RestGlobalMessageCommand>> BulkOverwriteGlobalMessageCommands(BaseDiscordClient client, ContextMenuCommandCreationProperties[] args, RequestOptions options = null)
{
Preconditions.NotNull(args, nameof(args));

@@ -316,13 +310,11 @@ namespace Discord.Rest
foreach (var arg in args)
{
Preconditions.NotNullOrEmpty(arg.Name, nameof(arg.Name));
Preconditions.NotNullOrEmpty(arg.Description, nameof(arg.Description));
Preconditions.Equals(arg.Type, ApplicationCommandType.Message);

var model = new CreateApplicationCommandParams()
{
Name = arg.Name,
Description = arg.Description,
Type = arg.Type
};

@@ -465,22 +457,20 @@ namespace Discord.Rest
await client.ApiClient.DeleteGuildApplicationCommandAsync(guildId, command.Id, options).ConfigureAwait(false);
}

public static async Task<RestGuildUserCommand> CreateGuildUserCommand(BaseDiscordClient client, ulong guildId, Action<UserCommandCreationProperties> func, RequestOptions options = null)
public static async Task<RestGuildUserCommand> CreateGuildUserCommand(BaseDiscordClient client, ulong guildId, Action<ContextMenuCommandCreationProperties> func, RequestOptions options = null)
{
var args = new UserCommandCreationProperties();
var args = new ContextMenuCommandCreationProperties();
func(args);
return await CreateGuildUserCommand(client, guildId, args, options).ConfigureAwait(false);
}

public static async Task<RestGuildUserCommand> CreateGuildUserCommand(BaseDiscordClient client, ulong guildId, UserCommandCreationProperties arg, RequestOptions options = null)
public static async Task<RestGuildUserCommand> CreateGuildUserCommand(BaseDiscordClient client, ulong guildId, ContextMenuCommandCreationProperties arg, RequestOptions options = null)
{
Preconditions.NotNullOrEmpty(arg.Name, nameof(arg.Name));
Preconditions.Equals(arg.Description, "");

var model = new CreateApplicationCommandParams()
{
Name = arg.Name,
Description = arg.Description,
Type = arg.Type
};

@@ -488,22 +478,20 @@ namespace Discord.Rest
return RestGuildUserCommand.Create(client, cmd, guildId);
}

public static async Task<RestGuildMessageCommand> CreateGuildMessageCommand(BaseDiscordClient client, ulong guildId, Action<MessageCommandCreationProperties> func, RequestOptions options = null)
public static async Task<RestGuildMessageCommand> CreateGuildMessageCommand(BaseDiscordClient client, ulong guildId, Action<ContextMenuCommandCreationProperties> func, RequestOptions options = null)
{
var args = new MessageCommandCreationProperties();
var args = new ContextMenuCommandCreationProperties();
func(args);
return await CreateGuildMessageCommand(client, guildId, args, options).ConfigureAwait(false);
}

public static async Task<RestGuildMessageCommand> CreateGuildMessageCommand(BaseDiscordClient client, ulong guildId, MessageCommandCreationProperties arg, RequestOptions options = null)
public static async Task<RestGuildMessageCommand> CreateGuildMessageCommand(BaseDiscordClient client, ulong guildId, ContextMenuCommandCreationProperties arg, RequestOptions options = null)
{
Preconditions.NotNullOrEmpty(arg.Name, nameof(arg.Name));
Preconditions.Equals(arg.Description, "");

var model = new CreateApplicationCommandParams()
{
Name = arg.Name,
Description = arg.Description,
Type = arg.Type
};

@@ -511,7 +499,7 @@ namespace Discord.Rest
return RestGuildMessageCommand.Create(client, cmd, guildId);
}

public static async Task<IReadOnlyCollection<RestGuildUserCommand>> BulkOverwriteGuildUserCommands(BaseDiscordClient client, ulong guildId, UserCommandCreationProperties[] args, RequestOptions options = null)
public static async Task<IReadOnlyCollection<RestGuildUserCommand>> BulkOverwriteGuildUserCommands(BaseDiscordClient client, ulong guildId, ContextMenuCommandCreationProperties[] args, RequestOptions options = null)
{
Preconditions.NotNull(args, nameof(args));

@@ -520,13 +508,11 @@ namespace Discord.Rest
foreach (var arg in args)
{
Preconditions.NotNullOrEmpty(arg.Name, nameof(arg.Name));
Preconditions.NotNullOrEmpty(arg.Description, nameof(arg.Description));
Preconditions.Equals(arg.Type, ApplicationCommandType.User);

var model = new CreateApplicationCommandParams()
{
Name = arg.Name,
Description = arg.Description,
Type = arg.Type
};

@@ -573,7 +559,7 @@ namespace Discord.Rest
await client.ApiClient.DeleteGuildApplicationCommandAsync(guildId, command.Id, options).ConfigureAwait(false);
}

public static async Task<IReadOnlyCollection<RestGuildMessageCommand>> BulkOverwriteGuildMessageCommands(BaseDiscordClient client, ulong guildId, MessageCommandCreationProperties[] args, RequestOptions options = null)
public static async Task<IReadOnlyCollection<RestGuildMessageCommand>> BulkOverwriteGuildMessageCommands(BaseDiscordClient client, ulong guildId, ContextMenuCommandCreationProperties[] args, RequestOptions options = null)
{
Preconditions.NotNull(args, nameof(args));

@@ -582,13 +568,11 @@ namespace Discord.Rest
foreach (var arg in args)
{
Preconditions.NotNullOrEmpty(arg.Name, nameof(arg.Name));
Preconditions.NotNullOrEmpty(arg.Description, nameof(arg.Description));
Preconditions.Equals(arg.Type, ApplicationCommandType.Message);

var model = new CreateApplicationCommandParams()
{
Name = arg.Name,
Description = arg.Description,
Type = arg.Type
};

@@ -642,15 +626,33 @@ namespace Discord.Rest
var args = new MessageProperties();
func(args);

var embed = args.Embed;
var embeds = args.Embeds;

bool hasText = args.Content.IsSpecified ? !string.IsNullOrEmpty(args.Content.Value) : !string.IsNullOrEmpty(message.Content);
bool hasEmbed = args.Embeds.IsSpecified ? args.Embeds.Value != null : message.Embeds.Any();
if (!hasText && !hasEmbed)
bool hasEmbeds = (embed.IsSpecified && embed.Value != null) || (embeds.IsSpecified && embeds.Value?.Length > 0) || message.Embeds.Any();

if (!hasText && !hasEmbeds)
Preconditions.NotNullOrEmpty(args.Content.IsSpecified ? args.Content.Value : string.Empty, nameof(args.Content));

var apiEmbeds = embed.IsSpecified || embeds.IsSpecified ? new List<API.Embed>() : null;

if (embed.IsSpecified && embed.Value != null)
{
apiEmbeds.Add(embed.Value.ToModel());
}

if (embeds.IsSpecified && embeds.Value != null)
{
apiEmbeds.AddRange(embeds.Value.Select(x => x.ToModel()));
}

Preconditions.AtMost(apiEmbeds?.Count ?? 0, 10, nameof(args.Embeds), "A max of 10 embeds are allowed.");

var apiArgs = new API.Rest.ModifyInteractionResponseParams
{
Content = args.Content,
Embeds = args.Embeds.IsSpecified ? args.Embeds.Value.Select(x => x.ToModel()).ToArray() : Optional.Create<API.Embed[]>(),
Embeds = apiEmbeds?.ToArray() ?? Optional<API.Embed[]>.Unspecified,
AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value.ToModel() : Optional<API.AllowedMentions>.Unspecified,
Components = args.Components.IsSpecified ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() : Optional<API.ActionRowComponent[]>.Unspecified,
};
@@ -667,10 +669,33 @@ namespace Discord.Rest
var args = new MessageProperties();
func(args);

var embed = args.Embed;
var embeds = args.Embeds;

bool hasText = !string.IsNullOrEmpty(args.Content.GetValueOrDefault());
bool hasEmbeds = (embed.IsSpecified && embed.Value != null) || (embeds.IsSpecified && embeds.Value?.Length > 0);

if (!hasText && !hasEmbeds)
Preconditions.NotNullOrEmpty(args.Content.IsSpecified ? args.Content.Value : string.Empty, nameof(args.Content));

var apiEmbeds = embed.IsSpecified || embeds.IsSpecified ? new List<API.Embed>() : null;

if (embed.IsSpecified && embed.Value != null)
{
apiEmbeds.Add(embed.Value.ToModel());
}

if (embeds.IsSpecified && embeds.Value != null)
{
apiEmbeds.AddRange(embeds.Value.Select(x => x.ToModel()));
}

Preconditions.AtMost(apiEmbeds?.Count ?? 0, 10, nameof(args.Embeds), "A max of 10 embeds are allowed.");

var apiArgs = new ModifyInteractionResponseParams
{
Content = args.Content,
Embeds = args.Embeds.IsSpecified ? args.Embeds.Value?.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
Embeds = apiEmbeds?.ToArray() ?? Optional<API.Embed[]>.Unspecified,
AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value?.ToModel() : Optional<API.AllowedMentions>.Unspecified,
Components = args.Components.IsSpecified ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() : Optional<API.ActionRowComponent[]>.Unspecified,
Flags = args.Flags


+ 30
- 15
src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs View File

@@ -1,3 +1,4 @@
using Discord.API;
using Discord.API.Rest;
using System;
using System.Collections.Generic;
@@ -33,9 +34,13 @@ namespace Discord.Rest
if (msg.Author.Id != client.CurrentUser.Id && (args.Content.IsSpecified || args.Embeds.IsSpecified || args.AllowedMentions.IsSpecified))
throw new InvalidOperationException("Only the author of a message may modify the message content, embed, or allowed mentions.");

var embed = args.Embed;
var embeds = args.Embeds;

bool hasText = args.Content.IsSpecified ? !string.IsNullOrEmpty(args.Content.Value) : !string.IsNullOrEmpty(msg.Content);
bool hasEmbed = args.Embeds.IsSpecified ? args.Embeds.Value != null : msg.Embeds.Any();
if (!hasText && !hasEmbed)
bool hasEmbeds = (embed.IsSpecified && embed.Value != null) || (embeds.IsSpecified && embeds.Value?.Length > 0) || msg.Embeds.Any();

if (!hasText && !hasEmbeds)
Preconditions.NotNullOrEmpty(args.Content.IsSpecified ? args.Content.Value : string.Empty, nameof(args.Content));

if (args.AllowedMentions.IsSpecified)
@@ -61,22 +66,24 @@ namespace Discord.Rest
}
}

var embeds = new List<API.Embed>();
var apiEmbeds = embed.IsSpecified || embeds.IsSpecified ? new List<API.Embed>() : null;

if (args.Embed.IsSpecified)
if (embed.IsSpecified && embed.Value != null)
{
embeds.Add(args.Embed.Value.ToModel());
apiEmbeds.Add(embed.Value.ToModel());
}

if (args.Embeds.IsSpecified)
if (embeds.IsSpecified && embeds.Value != null)
{
embeds.AddRange(args.Embeds.Value.Select(x => x.ToModel()));
apiEmbeds.AddRange(embeds.Value.Select(x => x.ToModel()));
}

Preconditions.AtMost(apiEmbeds?.Count ?? 0, 10, nameof(args.Embeds), "A max of 10 embeds are allowed.");

var apiArgs = new ModifyMessageParams
{
Content = args.Content,
Embeds = embeds.ToArray(),
Embeds = apiEmbeds?.ToArray() ?? Optional<API.Embed[]>.Unspecified,
Components = args.Components.IsSpecified ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() : Optional<API.ActionRowComponent[]>.Unspecified,
Flags = args.Flags,
AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value.ToModel() : Optional<API.AllowedMentions>.Unspecified,
@@ -90,7 +97,13 @@ namespace Discord.Rest
var args = new MessageProperties();
func(args);

if (args.Content.IsSpecified && string.IsNullOrEmpty(args.Content.Value) && args.Embeds.IsSpecified && args.Embeds.Value == null)
var embed = args.Embed;
var embeds = args.Embeds;

bool hasText = args.Content.IsSpecified && string.IsNullOrEmpty(args.Content.Value);
bool hasEmbeds = (embed.IsSpecified && embed.Value != null) || (embeds.IsSpecified && embeds.Value?.Length > 0);

if (!hasText && !hasEmbeds)
Preconditions.NotNullOrEmpty(args.Content.IsSpecified ? args.Content.Value : string.Empty, nameof(args.Content));

if (args.AllowedMentions.IsSpecified)
@@ -117,22 +130,24 @@ namespace Discord.Rest
}
}

var embeds = new List<API.Embed>();
var apiEmbeds = embed.IsSpecified || embeds.IsSpecified ? new List<API.Embed>() : null;

if (args.Embed.IsSpecified)
if (embed.IsSpecified && embed.Value != null)
{
embeds.Add(args.Embed.Value.ToModel());
apiEmbeds.Add(embed.Value.ToModel());
}

if (args.Embeds.IsSpecified)
if (embeds.IsSpecified && embeds.Value != null)
{
embeds.AddRange(args.Embeds.Value.Select(x => x.ToModel()));
apiEmbeds.AddRange(embeds.Value.Select(x => x.ToModel()));
}

Preconditions.AtMost(apiEmbeds?.Count ?? 0, 10, nameof(args.Embeds), "A max of 10 embeds are allowed.");

var apiArgs = new API.Rest.ModifyMessageParams
{
Content = args.Content,
Embeds = embeds.ToArray(),
Embeds = apiEmbeds?.ToArray() ?? Optional<API.Embed[]>.Unspecified,
Flags = args.Flags.IsSpecified ? args.Flags.Value : Optional.Create<MessageFlags?>(),
AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value.ToModel() : Optional.Create<API.AllowedMentions>(),
Components = args.Components.IsSpecified ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() : Optional<API.ActionRowComponent[]>.Unspecified,


+ 2
- 2
src/Discord.Net.Rest/Entities/Users/RestThreadUser.cs View File

@@ -9,7 +9,7 @@ using Model = Discord.API.ThreadMember;
namespace Discord.Rest
{
/// <summary>
/// Represents a thread user recieved over the REST api.
/// Represents a thread user received over the REST api.
/// </summary>
public class RestThreadUser : RestEntity<ulong>
{
@@ -51,7 +51,7 @@ namespace Discord.Rest
/// Gets the guild user for this thread user.
/// </summary>
/// <returns>
/// A task representing the asyncronous get operation. The task returns a
/// A task representing the asynchronous get operation. The task returns a
/// <see cref="IGuildUser"/> that represents the current thread user.
/// </returns>
public Task<IGuildUser> GetGuildUser()


+ 12
- 0
src/Discord.Net.Rest/Entities/Users/RestUser.cs View File

@@ -22,6 +22,10 @@ namespace Discord.Rest
/// <inheritdoc />
public string AvatarId { get; private set; }
/// <inheritdoc />
public string BannerId { get; private set; }
/// <inheritdoc />
public Color? AccentColor { get; private set; }
/// <inheritdoc />
public UserProperties? PublicFlags { get; private set; }

/// <inheritdoc />
@@ -61,6 +65,10 @@ namespace Discord.Rest
{
if (model.Avatar.IsSpecified)
AvatarId = model.Avatar.Value;
if (model.Banner.IsSpecified)
BannerId = model.Banner.Value;
if (model.AccentColor.IsSpecified)
AccentColor = model.AccentColor.Value;
if (model.Discriminator.IsSpecified)
DiscriminatorValue = ushort.Parse(model.Discriminator.Value, NumberStyles.None, CultureInfo.InvariantCulture);
if (model.Bot.IsSpecified)
@@ -92,6 +100,10 @@ namespace Discord.Rest
public string GetAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128)
=> CDN.GetUserAvatarUrl(Id, AvatarId, size, format);

/// <inheritdoc />
public string GetBannerUrl(ImageFormat format = ImageFormat.Auto, ushort size = 256)
=> CDN.GetUserBannerUrl(Id, BannerId, size, format);

/// <inheritdoc />
public string GetDefaultAvatarUrl()
=> CDN.GetDefaultUserAvatarUrl(DiscriminatorValue);


+ 4
- 0
src/Discord.Net.Rest/Entities/Webhooks/RestWebhook.cs View File

@@ -24,6 +24,8 @@ namespace Discord.Rest
public ulong? GuildId { get; private set; }
/// <inheritdoc />
public IUser Creator { get; private set; }
/// <inheritdoc />
public ulong? ApplicationId { get; private set; }

/// <inheritdoc />
public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id);
@@ -66,6 +68,8 @@ namespace Discord.Rest
GuildId = model.GuildId.Value;
if (model.Name.IsSpecified)
Name = model.Name.Value;

ApplicationId = model.ApplicationId;
}

/// <inheritdoc />


+ 1
- 0
src/Discord.Net.Rest/Extensions/EntityExtensions.cs View File

@@ -68,6 +68,7 @@ namespace Discord.Rest
model.Video = entity.Video.Value.ToModel();
return model;
}

public static API.AllowedMentions ToModel(this AllowedMentions entity)
{
return new API.AllowedMentions()


+ 3
- 1
src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj View File

@@ -8,7 +8,7 @@
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net461;netstandard2.0;netstandard2.1</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard2.0;netstandard2.1</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Version>3.0.0-pre</Version>
<Version>3.0.1-pre</Version>
<RepositoryUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</RepositoryUrl>
<PackageProjectUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</PackageProjectUrl>
<PackageIcon>Temporary.png</PackageIcon>
@@ -16,6 +16,8 @@
</PropertyGroup>
<PropertyGroup>
<DocumentationFile>..\Discord.Net.WebSocket\Discord.Net.WebSocket.xml</DocumentationFile>
<AssemblyVersion>3.0.1</AssemblyVersion>
<FileVersion>3.0.1</FileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>TRACE;</DefineConstants>


+ 106
- 15
src/Discord.Net.WebSocket/Discord.Net.WebSocket.xml View File

@@ -3599,40 +3599,43 @@
<member name="M:Discord.WebSocket.SocketGuild.Discord#IGuild#GetWebhooksAsync(Discord.RequestOptions)">
<inheritdoc />
</member>
<member name="T:Discord.WebSocket.SocketApplicationMessageCommand">
<member name="M:Discord.WebSocket.SocketGuild.Discord#IGuild#GetApplicationCommandsAsync(Discord.RequestOptions)">
<inheritdoc />
</member>
<member name="T:Discord.WebSocket.SocketMessageCommand">
<summary>
Represents a Websocket-based slash command received over the gateway.
</summary>
</member>
<member name="P:Discord.WebSocket.SocketApplicationMessageCommand.Data">
<member name="P:Discord.WebSocket.SocketMessageCommand.Data">
<summary>
The data associated with this interaction.
</summary>
</member>
<member name="T:Discord.WebSocket.SocketApplicationMessageCommandData">
<member name="T:Discord.WebSocket.SocketMessageCommandData">
<summary>
Represents the data tied with the <see cref="T:Discord.WebSocket.SocketApplicationMessageCommand"/> interaction.
Represents the data tied with the <see cref="T:Discord.WebSocket.SocketMessageCommand"/> interaction.
</summary>
</member>
<member name="P:Discord.WebSocket.SocketApplicationMessageCommandData.Name">
<member name="P:Discord.WebSocket.SocketMessageCommandData.Name">
<inheritdoc/>
</member>
<member name="T:Discord.WebSocket.SocketApplicationUserCommand">
<member name="T:Discord.WebSocket.SocketUserCommand">
<summary>
Represents a Websocket-based slash command received over the gateway.
</summary>
</member>
<member name="P:Discord.WebSocket.SocketApplicationUserCommand.Data">
<member name="P:Discord.WebSocket.SocketUserCommand.Data">
<summary>
The data associated with this interaction.
</summary>
</member>
<member name="T:Discord.WebSocket.SocketApplicationUserCommandData">
<member name="T:Discord.WebSocket.SocketUserCommandData">
<summary>
Represents the data tied with the <see cref="T:Discord.WebSocket.SocketApplicationUserCommand"/> interaction.
Represents the data tied with the <see cref="T:Discord.WebSocket.SocketUserCommand"/> interaction.
</summary>
</member>
<member name="P:Discord.WebSocket.SocketApplicationUserCommandData.Name">
<member name="P:Discord.WebSocket.SocketUserCommandData.Name">
<inheritdoc/>
</member>
<member name="T:Discord.WebSocket.SocketMessageComponent">
@@ -3664,15 +3667,19 @@
<member name="M:Discord.WebSocket.SocketMessageComponent.FollowupAsync(System.String,Discord.Embed[],System.Boolean,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent,Discord.Embed)">
<inheritdoc/>
</member>
<member name="M:Discord.WebSocket.SocketMessageComponent.DeferAsync(Discord.RequestOptions)">
<member name="M:Discord.WebSocket.SocketMessageComponent.DeferLoadingAsync(System.Boolean,Discord.RequestOptions)">
<summary>
Acknowledges this interaction with the <see cref="F:Discord.InteractionResponseType.DeferredUpdateMessage"/>.
Defers an interaction and responds with type 5 (<see cref="F:Discord.InteractionResponseType.DeferredChannelMessageWithSource"/>)
</summary>
<param name="ephemeral"><see langword="true"/> to send this message ephemerally, otherwise <see langword="false"/>.</param>
<param name="options">The request options for this async request.</param>
<returns>
A task that represents the asynchronous operation of acknowledging the interaction.
</returns>
</member>
<member name="M:Discord.WebSocket.SocketMessageComponent.DeferAsync(System.Boolean,Discord.RequestOptions)">
<inheritdoc/>
</member>
<member name="T:Discord.WebSocket.SocketMessageComponentData">
<summary>
Represents the data sent with a <see cref="F:Discord.InteractionType.MessageComponent"/>.
@@ -3812,6 +3819,11 @@
If the option is a subcommand or subcommand group type, this nested options will be the parameters.
</summary>
</member>
<member name="T:Discord.WebSocket.SocketCommandBase">
<summary>
Base class for User, Message, and Slash command interactions
</summary>
</member>
<member name="P:Discord.WebSocket.SocketCommandBase.Data">
<summary>
The data associated with this interaction.
@@ -3823,7 +3835,7 @@
<member name="M:Discord.WebSocket.SocketCommandBase.FollowupAsync(System.String,Discord.Embed[],System.Boolean,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent,Discord.Embed)">
<inheritdoc/>
</member>
<member name="M:Discord.WebSocket.SocketCommandBase.DeferAsync(Discord.RequestOptions)">
<member name="M:Discord.WebSocket.SocketCommandBase.DeferAsync(System.Boolean,Discord.RequestOptions)">
<summary>
Acknowledges this interaction with the <see cref="F:Discord.InteractionResponseType.DeferredChannelMessageWithSource"/>.
</summary>
@@ -3831,6 +3843,27 @@
A task that represents the asynchronous operation of acknowledging the interaction.
</returns>
</member>
<member name="T:Discord.WebSocket.SocketCommandBaseData">
<summary>
Represents the base data tied with the <see cref="T:Discord.WebSocket.SocketCommandBase"/> interaction.
</summary>
</member>
<member name="P:Discord.WebSocket.SocketCommandBaseData.Name">
<inheritdoc/>
</member>
<member name="P:Discord.WebSocket.SocketCommandBaseData.Options">
<summary>
The <see cref="T:Discord.WebSocket.SocketCommandBaseDataOption"/>'s received with this interaction.
</summary>
</member>
<member name="T:Discord.WebSocket.SocketCommandBaseDataOption">
<summary>
Represents the base Websocket-based <see cref="T:Discord.IApplicationCommandInteractionDataOption"/> recieved by the gateway
</summary>
</member>
<member name="P:Discord.WebSocket.SocketCommandBaseDataOption.Name">
<inheritdoc/>
</member>
<member name="P:Discord.WebSocket.SocketCommandBaseDataOption.Value">
<inheritdoc/>
</member>
@@ -3943,14 +3976,25 @@
A task that represents the asynchronous operation of acknowledging the interaction.
</returns>
</member>
<member name="M:Discord.WebSocket.SocketInteraction.DeferAsync(Discord.RequestOptions)">
<member name="M:Discord.WebSocket.SocketInteraction.DeferAsync(System.Boolean,Discord.RequestOptions)">
<summary>
Acknowledges this interaction.
</summary>
<param name="ephemeral"><see langword="true"/> to send this message ephemerally, otherwise <see langword="false"/>.</param>
<param name="options">The request options for this async request.</param>
<returns>
A task that represents the asynchronous operation of acknowledging the interaction.
</returns>
</member>
<member name="M:Discord.WebSocket.SocketInteraction.Discord#IDiscordInteraction#FollowupAsync(System.String,Discord.Embed[],System.Boolean,System.Boolean,Discord.AllowedMentions,Discord.RequestOptions,Discord.MessageComponent,Discord.Embed)">
<inheritdoc/>
</member>
<member name="M:Discord.WebSocket.SocketInteraction.Discord#IDiscordInteraction#GetOriginalResponseAsync(Discord.RequestOptions)">
<inheritdoc/>
</member>
<member name="M:Discord.WebSocket.SocketInteraction.Discord#IDiscordInteraction#ModifyOriginalResponseAsync(System.Action{Discord.MessageProperties},Discord.RequestOptions)">
<inheritdoc/>
</member>
<member name="T:Discord.WebSocket.SocketInvite">
<summary>
Represents a WebSocket-based invite to a guild.
@@ -4487,6 +4531,12 @@
<member name="P:Discord.WebSocket.SocketGroupUser.AvatarId">
<inheritdoc />
</member>
<member name="P:Discord.WebSocket.SocketGroupUser.BannerId">
<inheritdoc />
</member>
<member name="P:Discord.WebSocket.SocketGroupUser.AccentColor">
<inheritdoc />
</member>
<member name="P:Discord.WebSocket.SocketGroupUser.Presence">
<inheritdoc />
</member>
@@ -4545,6 +4595,12 @@
<member name="P:Discord.WebSocket.SocketGuildUser.AvatarId">
<inheritdoc />
</member>
<member name="P:Discord.WebSocket.SocketGuildUser.BannerId">
<inheritdoc />
</member>
<member name="P:Discord.WebSocket.SocketGuildUser.AccentColor">
<inheritdoc />
</member>
<member name="P:Discord.WebSocket.SocketGuildUser.GuildPermissions">
<inheritdoc />
</member>
@@ -4608,7 +4664,7 @@
Returns the position of the user within the role hierarchy.
</summary>
<remarks>
The returned value equal to the position of the highest role the user has, or
The returned value equal to the position of the highest role the user has, or
<see cref="F:System.Int32.MaxValue"/> if user is the server owner.
</remarks>
</member>
@@ -4730,6 +4786,12 @@
<member name="P:Discord.WebSocket.SocketSelfUser.AvatarId">
<inheritdoc />
</member>
<member name="P:Discord.WebSocket.SocketSelfUser.BannerId">
<inheritdoc />
</member>
<member name="P:Discord.WebSocket.SocketSelfUser.AccentColor">
<inheritdoc />
</member>
<member name="P:Discord.WebSocket.SocketSelfUser.Presence">
<inheritdoc />
</member>
@@ -4783,6 +4845,12 @@
<member name="P:Discord.WebSocket.SocketThreadUser.AvatarId">
<inheritdoc/>
</member>
<member name="P:Discord.WebSocket.SocketThreadUser.BannerId">
<inheritdoc/>
</member>
<member name="P:Discord.WebSocket.SocketThreadUser.AccentColor">
<inheritdoc/>
</member>
<member name="P:Discord.WebSocket.SocketThreadUser.DiscriminatorValue">
<inheritdoc/>
</member>
@@ -4890,6 +4958,12 @@
<member name="P:Discord.WebSocket.SocketUnknownUser.AvatarId">
<inheritdoc />
</member>
<member name="P:Discord.WebSocket.SocketUnknownUser.BannerId">
<inheritdoc />
</member>
<member name="P:Discord.WebSocket.SocketUnknownUser.AccentColor">
<inheritdoc />
</member>
<member name="P:Discord.WebSocket.SocketUnknownUser.IsBot">
<inheritdoc />
</member>
@@ -4920,6 +4994,12 @@
<member name="P:Discord.WebSocket.SocketUser.AvatarId">
<inheritdoc />
</member>
<member name="P:Discord.WebSocket.SocketUser.BannerId">
<inheritdoc />
</member>
<member name="P:Discord.WebSocket.SocketUser.AccentColor">
<inheritdoc />
</member>
<member name="P:Discord.WebSocket.SocketUser.IsWebhook">
<inheritdoc />
</member>
@@ -4958,6 +5038,9 @@
<member name="M:Discord.WebSocket.SocketUser.GetAvatarUrl(Discord.ImageFormat,System.UInt16)">
<inheritdoc />
</member>
<member name="M:Discord.WebSocket.SocketUser.GetBannerUrl(Discord.ImageFormat,System.UInt16)">
<inheritdoc />
</member>
<member name="M:Discord.WebSocket.SocketUser.GetDefaultAvatarUrl">
<inheritdoc />
</member>
@@ -5039,6 +5122,14 @@
<member name="P:Discord.WebSocket.SocketWebhookUser.AvatarId">
<inheritdoc />
</member>
<member name="P:Discord.WebSocket.SocketWebhookUser.BannerId">
<inheritdoc />
<exception cref="T:System.NotSupportedException">Webhook users does not support banners.</exception>
</member>
<member name="P:Discord.WebSocket.SocketWebhookUser.AccentColor">
<inheritdoc />
<exception cref="T:System.NotSupportedException">Webhook users does not support accent colors.</exception>
</member>
<member name="P:Discord.WebSocket.SocketWebhookUser.IsBot">
<inheritdoc />
</member>


+ 10
- 1
src/Discord.Net.WebSocket/DiscordShardedClient.cs View File

@@ -30,7 +30,16 @@ namespace Discord.WebSocket
/// <inheritdoc />
public override IActivity Activity { get => _shards[0].Activity; protected set { } }

internal new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient;
internal new DiscordSocketApiClient ApiClient
{
get
{
if (base.ApiClient.CurrentUserId == null)
base.ApiClient.CurrentUserId = CurrentUser?.Id;

return base.ApiClient;
}
}
/// <inheritdoc />
public override IReadOnlyCollection<SocketGuild> Guilds => GetGuilds().ToReadOnlyCollection(GetGuildCount);
/// <inheritdoc />


+ 14
- 3
src/Discord.Net.WebSocket/DiscordSocketApiClient.cs View File

@@ -79,7 +79,7 @@ namespace Discord.API
if (msg != null)
{
#if DEBUG_PACKETS
Console.WriteLine($"<- {msg.Operation} [{msg.Type ?? "none"}] : {(msg.Payload as Newtonsoft.Json.Linq.JToken)?.ToString().Length}");
Console.WriteLine($"<- {(GatewayOpCode)msg.Operation} [{msg.Type ?? "none"}] : {(msg.Payload as Newtonsoft.Json.Linq.JToken)?.ToString().Length}");
#endif

await _receivedGatewayEvent.InvokeAsync((GatewayOpCode)msg.Operation, msg.Sequence, msg.Type, msg.Payload).ConfigureAwait(false);
@@ -96,7 +96,7 @@ namespace Discord.API
if (msg != null)
{
#if DEBUG_PACKETS
Console.WriteLine($"<- {msg.Operation} [{msg.Type ?? "none"}] : {(msg.Payload as Newtonsoft.Json.Linq.JToken)?.ToString().Length}");
Console.WriteLine($"<- {(GatewayOpCode)msg.Operation} [{msg.Type ?? "none"}] : {(msg.Payload as Newtonsoft.Json.Linq.JToken)?.ToString().Length}");
#endif

await _receivedGatewayEvent.InvokeAsync((GatewayOpCode)msg.Operation, msg.Sequence, msg.Type, msg.Payload).ConfigureAwait(false);
@@ -105,6 +105,10 @@ namespace Discord.API
};
WebSocketClient.Closed += async ex =>
{
#if DEBUG_PACKETS
Console.WriteLine(ex);
#endif

await DisconnectAsync().ConfigureAwait(false);
await _disconnectedEvent.InvokeAsync(ex).ConfigureAwait(false);
};
@@ -166,6 +170,11 @@ namespace Discord.API
var gatewayResponse = await GetGatewayAsync().ConfigureAwait(false);
_gatewayUrl = $"{gatewayResponse.Url}?v={DiscordConfig.APIVersion}&encoding={DiscordSocketConfig.GatewayEncoding}&compress=zlib-stream";
}

#if DEBUG_PACKETS
Console.WriteLine("Connecting to gateway: " + _gatewayUrl);
#endif

await WebSocketClient.ConnectAsync(_gatewayUrl).ConfigureAwait(false);

ConnectionState = ConnectionState.Connected;
@@ -237,7 +246,9 @@ namespace Discord.API
options = RequestOptions.CreateOrClone(options);
var props = new Dictionary<string, string>
{
["$device"] = "Discord.Net"
["$device"] = "Discord.Net Labs",
["$os"] = Environment.OSVersion.Platform.ToString(),
[$"browser"] = "Discord.Net Labs"
};
var msg = new IdentifyParams()
{


+ 3
- 0
src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs View File

@@ -1478,6 +1478,9 @@ namespace Discord.WebSocket
/// <inheritdoc />
async Task<IReadOnlyCollection<IWebhook>> IGuild.GetWebhooksAsync(RequestOptions options)
=> await GetWebhooksAsync(options).ConfigureAwait(false);
/// <inheritdoc />
async Task<IReadOnlyCollection<IApplicationCommand>> IGuild.GetApplicationCommandsAsync (RequestOptions options)
=> await GetApplicationCommandsAsync(options).ConfigureAwait(false);

void IDisposable.Dispose()
{


src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/Message Commands/SocketApplicationMessageCommand.cs → src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/Message Commands/SocketMessageCommand.cs View File

@@ -10,14 +10,14 @@ namespace Discord.WebSocket
/// <summary>
/// Represents a Websocket-based slash command received over the gateway.
/// </summary>
public class SocketApplicationMessageCommand : SocketCommandBase
public class SocketMessageCommand : SocketCommandBase
{
/// <summary>
/// The data associated with this interaction.
/// </summary>
new public SocketApplicationMessageCommandData Data { get; }
new public SocketMessageCommandData Data { get; }

internal SocketApplicationMessageCommand(DiscordSocketClient client, Model model, ISocketMessageChannel channel)
internal SocketMessageCommand(DiscordSocketClient client, Model model, ISocketMessageChannel channel)
: base(client, model, channel)
{
var dataModel = model.Data.IsSpecified ?
@@ -28,12 +28,12 @@ namespace Discord.WebSocket
if (this.Channel is SocketGuildChannel guildChannel)
guildId = guildChannel.Guild.Id;

Data = SocketApplicationMessageCommandData.Create(client, dataModel, model.Id, guildId);
Data = SocketMessageCommandData.Create(client, dataModel, model.Id, guildId);
}

new internal static SocketInteraction Create(DiscordSocketClient client, Model model, ISocketMessageChannel channel)
{
var entity = new SocketApplicationMessageCommand(client, model, channel);
var entity = new SocketMessageCommand(client, model, channel);
entity.Update(model);
return entity;
}

src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/Message Commands/SocketApplicationMessageCommandData.cs → src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/Message Commands/SocketMessageCommandData.cs View File

@@ -6,12 +6,15 @@ using Model = Discord.API.ApplicationCommandInteractionData;
namespace Discord.WebSocket
{
/// <summary>
/// Represents the data tied with the <see cref="SocketApplicationMessageCommand"/> interaction.
/// Represents the data tied with the <see cref="SocketMessageCommand"/> interaction.
/// </summary>
public class SocketApplicationMessageCommandData : SocketEntity<ulong>, IApplicationCommandInteractionData
public class SocketMessageCommandData : SocketEntity<ulong>, IApplicationCommandInteractionData
{
/// <inheritdoc/>
public string Name { get; private set; }
/// <summary>
/// The message selected to run the command
/// </summary>
public SocketMessage Message { get; private set; }

internal Dictionary<ulong, SocketGuildUser> guildMembers { get; private set; }
@@ -27,15 +30,11 @@ namespace Discord.WebSocket

private ulong? guildId;

private ApplicationCommandType Type;

internal SocketApplicationMessageCommandData(DiscordSocketClient client, Model model, ulong? guildId)
internal SocketMessageCommandData(DiscordSocketClient client, Model model, ulong? guildId)
: base(client, model.Id)
{
this.guildId = guildId;

this.Type = (ApplicationCommandType)model.Type;

if (model.Resolved.IsSpecified)
{
var guild = this.guildId.HasValue ? Discord.GetGuild(this.guildId.Value) : null;
@@ -126,9 +125,9 @@ namespace Discord.WebSocket
}
}

internal static SocketApplicationMessageCommandData Create(DiscordSocketClient client, Model model, ulong id, ulong? guildId)
internal static SocketMessageCommandData Create(DiscordSocketClient client, Model model, ulong id, ulong? guildId)
{
var entity = new SocketApplicationMessageCommandData(client, model, guildId);
var entity = new SocketMessageCommandData(client, model, guildId);
entity.Update(model);
return entity;
}

src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/User Commands/SocketApplicationUserCommand.cs → src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/User Commands/SocketUserCommand.cs View File

@@ -10,14 +10,14 @@ namespace Discord.WebSocket
/// <summary>
/// Represents a Websocket-based slash command received over the gateway.
/// </summary>
public class SocketApplicationUserCommand : SocketCommandBase
public class SocketUserCommand : SocketCommandBase
{
/// <summary>
/// The data associated with this interaction.
/// </summary>
new public SocketApplicationUserCommandData Data { get; }
new public SocketUserCommandData Data { get; }

internal SocketApplicationUserCommand(DiscordSocketClient client, Model model, ISocketMessageChannel channel)
internal SocketUserCommand(DiscordSocketClient client, Model model, ISocketMessageChannel channel)
: base(client, model, channel)
{
var dataModel = model.Data.IsSpecified ?
@@ -28,12 +28,12 @@ namespace Discord.WebSocket
if (this.Channel is SocketGuildChannel guildChannel)
guildId = guildChannel.Guild.Id;

Data = SocketApplicationUserCommandData.Create(client, dataModel, model.Id, guildId);
Data = SocketUserCommandData.Create(client, dataModel, model.Id, guildId);
}

new internal static SocketInteraction Create(DiscordSocketClient client, Model model, ISocketMessageChannel channel)
{
var entity = new SocketApplicationUserCommand(client, model, channel);
var entity = new SocketUserCommand(client, model, channel);
entity.Update(model);
return entity;
}

src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/User Commands/SocketApplicationUserCommandData.cs → src/Discord.Net.WebSocket/Entities/Interaction/Context Menu Commands/User Commands/SocketUserCommandData.cs View File

@@ -6,13 +6,15 @@ using Model = Discord.API.ApplicationCommandInteractionData;
namespace Discord.WebSocket
{
/// <summary>
/// Represents the data tied with the <see cref="SocketApplicationUserCommand"/> interaction.
/// Represents the data tied with the <see cref="SocketUserCommand"/> interaction.
/// </summary>
public class SocketApplicationUserCommandData : SocketEntity<ulong>, IApplicationCommandInteractionData
public class SocketUserCommandData : SocketEntity<ulong>, IApplicationCommandInteractionData
{
/// <inheritdoc/>
public string Name { get; private set; }

/// <summary>
/// The user used to run the command
/// </summary>
public SocketUser Member { get; private set; }

internal Dictionary<ulong, SocketGuildUser> guildMembers { get; private set; }
@@ -28,15 +30,11 @@ namespace Discord.WebSocket

private ulong? guildId;

private ApplicationCommandType Type;

internal SocketApplicationUserCommandData(DiscordSocketClient client, Model model, ulong? guildId)
internal SocketUserCommandData(DiscordSocketClient client, Model model, ulong? guildId)
: base(client, model.Id)
{
this.guildId = guildId;

this.Type = (ApplicationCommandType)model.Type;

if (model.Resolved.IsSpecified)
{
var guild = this.guildId.HasValue ? Discord.GetGuild(this.guildId.Value) : null;
@@ -99,9 +97,9 @@ namespace Discord.WebSocket
}
}

internal static SocketApplicationUserCommandData Create(DiscordSocketClient client, Model model, ulong id, ulong? guildId)
internal static SocketUserCommandData Create(DiscordSocketClient client, Model model, ulong id, ulong? guildId)
{
var entity = new SocketApplicationUserCommandData(client, model, guildId);
var entity = new SocketUserCommandData(client, model, guildId);
entity.Update(model);
return entity;
}

+ 49
- 12
src/Discord.Net.WebSocket/Entities/Interaction/Message Components/SocketMessageComponent.cs View File

@@ -4,12 +4,13 @@ using System.Threading.Tasks;
using Model = Discord.API.Interaction;
using DataModel = Discord.API.MessageComponentInteractionData;
using Discord.Rest;
using System.Collections.Generic;

namespace Discord.WebSocket
{
/// <summary>
/// Represents a Websocket-based interaction type for Message Components.
/// </summary>
/// <summary>
/// Represents a Websocket-based interaction type for Message Components.
/// </summary>
public class SocketMessageComponent : SocketInteraction
{
/// <summary>
@@ -123,7 +124,7 @@ namespace Discord.WebSocket
};

if (ephemeral)
response.Data.Value.Flags = 64;
response.Data.Value.Flags = MessageFlags.Ephemeral;

await InteractionHelper.SendInteractionResponse(this.Discord, response, this.Id, Token, options);
}
@@ -149,8 +150,28 @@ namespace Discord.WebSocket
Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions), "A max of 100 user Ids are allowed.");
}

if (args.Embeds.IsSpecified)
Preconditions.AtMost(args.Embeds.Value?.Length ?? 0, 10, nameof(args.Embeds), "A max of 10 embeds are allowed.");
var embed = args.Embed;
var embeds = args.Embeds;

bool hasText = args.Content.IsSpecified ? !string.IsNullOrEmpty(args.Content.Value) : !string.IsNullOrEmpty(Message.Content);
bool hasEmbeds = (embed.IsSpecified && embed.Value != null) || (embeds.IsSpecified && embeds.Value?.Length > 0) || Message.Embeds.Any();

if (!hasText && !hasEmbeds)
Preconditions.NotNullOrEmpty(args.Content.IsSpecified ? args.Content.Value : string.Empty, nameof(args.Content));

var apiEmbeds = embed.IsSpecified || embeds.IsSpecified ? new List<API.Embed>() : null;

if (embed.IsSpecified && embed.Value != null)
{
apiEmbeds.Add(embed.Value.ToModel());
}

if (embeds.IsSpecified && embeds.Value != null)
{
apiEmbeds.AddRange(embeds.Value.Select(x => x.ToModel()));
}

Preconditions.AtMost(apiEmbeds?.Count ?? 0, 10, nameof(args.Embeds), "A max of 10 embeds are allowed.");

// check that user flag and user Id list are exclusive, same with role flag and role Id list
if (args.AllowedMentions.IsSpecified && args.AllowedMentions.Value != null && args.AllowedMentions.Value.AllowedTypes.HasValue)
@@ -176,11 +197,11 @@ namespace Discord.WebSocket
{
Content = args.Content,
AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value?.ToModel() : Optional<API.AllowedMentions>.Unspecified,
Embeds = args.Embeds.IsSpecified ? args.Embeds.Value?.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
Embeds = apiEmbeds?.ToArray() ?? Optional<API.Embed[]>.Unspecified,
Components = args.Components.IsSpecified
? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray()
: Optional<API.ActionRowComponent[]>.Unspecified,
Flags = args.Flags.IsSpecified ? (int?)args.Flags.Value ?? Optional<int>.Unspecified : Optional<int>.Unspecified
Flags = args.Flags.IsSpecified ? args.Flags.Value ?? Optional<MessageFlags>.Unspecified : Optional<MessageFlags>.Unspecified
}
};

@@ -213,27 +234,43 @@ namespace Discord.WebSocket
AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
IsTTS = isTTS,
Embeds = embeds?.Select(x => x.ToModel()).ToArray() ?? Optional<API.Embed[]>.Unspecified,
Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified
Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
};

if (ephemeral)
args.Flags = 64;
args.Flags = MessageFlags.Ephemeral;

return await InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options);
}

/// <summary>
/// Acknowledges this interaction with the <see cref="InteractionResponseType.DeferredUpdateMessage"/>.
/// Defers an interaction and responds with type 5 (<see cref="InteractionResponseType.DeferredChannelMessageWithSource"/>)
/// </summary>
/// <param name="ephemeral"><see langword="true"/> to send this message ephemerally, otherwise <see langword="false"/>.</param>
/// <param name="options">The request options for this async request.</param>
/// <returns>
/// A task that represents the asynchronous operation of acknowledging the interaction.
/// </returns>
public override Task DeferAsync(RequestOptions options = null)
public Task DeferLoadingAsync(bool ephemeral = false, RequestOptions options = null)
{
var response = new API.InteractionResponse()
{
Type = InteractionResponseType.DeferredChannelMessageWithSource,
Data = ephemeral ? new API.InteractionCallbackData() { Flags = MessageFlags.Ephemeral } : Optional<API.InteractionCallbackData>.Unspecified

};

return Discord.Rest.ApiClient.CreateInteractionResponse(response, this.Id, this.Token, options);
}

/// <inheritdoc/>
public override Task DeferAsync(bool ephemeral = false, RequestOptions options = null)
{
var response = new API.InteractionResponse()
{
Type = InteractionResponseType.DeferredUpdateMessage,
Data = ephemeral ? new API.InteractionCallbackData() { Flags = MessageFlags.Ephemeral } : Optional<API.InteractionCallbackData>.Unspecified

};

return Discord.Rest.ApiClient.CreateInteractionResponse(response, this.Id, this.Token, options);


+ 1
- 1
src/Discord.Net.WebSocket/Entities/Interaction/Slash Commands/SocketSlashCommand.cs View File

@@ -36,6 +36,6 @@ namespace Discord.WebSocket
var entity = new SocketSlashCommand(client, model, channel);
entity.Update(model);
return entity;
}
}
}
}

+ 3
- 3
src/Discord.Net.WebSocket/Entities/Interaction/Slash Commands/SocketSlashCommandDataOption.cs View File

@@ -86,9 +86,9 @@ namespace Discord.WebSocket
break;
case ApplicationCommandOptionType.Integer:
{
if (model.Value.Value is int val)
if (model.Value.Value is long val)
this.Value = val;
else if (int.TryParse(model.Value.Value.ToString(), out int res))
else if (long.TryParse(model.Value.Value.ToString(), out long res))
this.Value = res;
}
break;
@@ -109,7 +109,7 @@ namespace Discord.WebSocket
}
break;
}
}

this.Options = model.Options.IsSpecified


+ 6
- 3
src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBase.cs View File

@@ -9,6 +9,9 @@ using Model = Discord.API.Interaction;

namespace Discord.WebSocket
{
/// <summary>
/// Base class for User, Message, and Slash command interactions
/// </summary>
public class SocketCommandBase : SocketInteraction
{
/// <summary>
@@ -105,7 +108,7 @@ namespace Discord.WebSocket
};

if (ephemeral)
response.Data.Value.Flags = 64;
response.Data.Value.Flags = MessageFlags.Ephemeral;

await InteractionHelper.SendInteractionResponse(this.Discord, response, this.Id, Token, options);
}
@@ -140,7 +143,7 @@ namespace Discord.WebSocket
};

if (ephemeral)
args.Flags = 64;
args.Flags = MessageFlags.Ephemeral;

return await InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options);
}
@@ -151,7 +154,7 @@ namespace Discord.WebSocket
/// <returns>
/// A task that represents the asynchronous operation of acknowledging the interaction.
/// </returns>
public override Task DeferAsync(RequestOptions options = null)
public override Task DeferAsync(bool ephemeral = false, RequestOptions options = null)
{
var response = new API.InteractionResponse
{


+ 7
- 3
src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBaseData.cs View File

@@ -5,13 +5,17 @@ using Model = Discord.API.ApplicationCommandInteractionData;

namespace Discord.WebSocket
{
/// <summary>
/// Represents the base data tied with the <see cref="SocketCommandBase"/> interaction.
/// </summary>
public class SocketCommandBaseData : SocketEntity<ulong>, IApplicationCommandInteractionData
{
/// <inheritdoc/>
public string Name { get; private set; }

/// <summary>
/// The <see cref="SocketCommandBaseDataOption"/>'s received with this interaction.
/// </summary>
public IReadOnlyCollection<SocketCommandBaseDataOption> Options { get; private set; }
// id
// type
internal Dictionary<ulong, SocketGuildUser> guildMembers { get; private set; }
= new Dictionary<ulong, SocketGuildUser>();
internal Dictionary<ulong, SocketGlobalUser> users { get; private set; }


+ 4
- 0
src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBaseDataOption.cs View File

@@ -9,8 +9,12 @@ using Model = Discord.API.ApplicationCommandInteractionDataOption;

namespace Discord.WebSocket
{
/// <summary>
/// Represents the base Websocket-based <see cref="IApplicationCommandInteractionDataOption"/> recieved by the gateway
/// </summary>
public class SocketCommandBaseDataOption : IApplicationCommandInteractionDataOption
{
/// <inheritdoc/>
public string Name { get; private set; }

/// <inheritdoc/>


+ 21
- 4
src/Discord.Net.WebSocket/Entities/Interaction/SocketInteraction.cs View File

@@ -71,9 +71,9 @@ namespace Discord.WebSocket
if (dataModel != null)
{
if (dataModel.Type.Equals(ApplicationCommandType.User))
return SocketApplicationUserCommand.Create(client, model, channel);
return SocketUserCommand.Create(client, model, channel);
if (dataModel.Type.Equals(ApplicationCommandType.Message))
return SocketApplicationMessageCommand.Create(client, model, channel);
return SocketMessageCommand.Create(client, model, channel);
}
}
return SocketSlashCommand.Create(client, model, channel);
@@ -172,20 +172,37 @@ namespace Discord.WebSocket
/// A task that represents the asynchronous operation of acknowledging the interaction.
/// </returns>
[Obsolete("This method deprecated, please use DeferAsync instead")]
public Task AcknowledgeAsync(RequestOptions options = null) => DeferAsync(options);
public Task AcknowledgeAsync(RequestOptions options = null) => DeferAsync(options: options);

/// <summary>
/// Acknowledges this interaction.
/// </summary>
/// <param name="ephemeral"><see langword="true"/> to send this message ephemerally, otherwise <see langword="false"/>.</param>
/// <param name="options">The request options for this async request.</param>
/// <returns>
/// A task that represents the asynchronous operation of acknowledging the interaction.
/// </returns>
public abstract Task DeferAsync(RequestOptions options = null);
public abstract Task DeferAsync(bool ephemeral = false, RequestOptions options = null);

private bool CheckToken()
{
// Tokens last for 15 minutes according to https://discord.com/developers/docs/interactions/slash-commands#responding-to-an-interaction
return (DateTime.UtcNow - this.CreatedAt.UtcDateTime).TotalMinutes <= 15d;
}

// IDiscordInteraction

/// <inheritdoc/>
async Task<IUserMessage> IDiscordInteraction.FollowupAsync (string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions,
RequestOptions options, MessageComponent component, Embed embed)
=> await FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, options, component, embed).ConfigureAwait(false);

/// <inheritdoc/>
async Task<IUserMessage> IDiscordInteraction.GetOriginalResponseAsync (RequestOptions options)
=> await GetOriginalResponseAsync(options).ConfigureAwait(false);

/// <inheritdoc/>
async Task<IUserMessage> IDiscordInteraction.ModifyOriginalResponseAsync (Action<MessageProperties> func, RequestOptions options)
=> await ModifyOriginalResponseAsync(func, options).ConfigureAwait(false);
}
}

+ 4
- 1
src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs View File

@@ -122,7 +122,10 @@ namespace Discord.WebSocket
}
internal static SocketMessage Create(DiscordSocketClient discord, ClientState state, SocketUser author, ISocketMessageChannel channel, Model model)
{
if (model.Type == MessageType.Default || model.Type == MessageType.Reply)
if (model.Type == MessageType.Default ||
model.Type == MessageType.Reply ||
model.Type == MessageType.ApplicationCommand ||
model.Type == MessageType.ThreadStarterMessage)
return SocketUserMessage.Create(discord, state, author, channel, model);
else
return SocketSystemMessage.Create(discord, state, author, channel, model);


+ 3
- 1
src/Discord.Net.WebSocket/Entities/Users/SocketGlobalUser.cs View File

@@ -12,6 +12,8 @@ namespace Discord.WebSocket
public override string Username { get; internal set; }
public override ushort DiscriminatorValue { get; internal set; }
public override string AvatarId { get; internal set; }
public override string BannerId { get; internal set; }
public override Color? AccentColor { get; internal set; }
internal override SocketPresence Presence { get; set; }

public override bool IsWebhook => false;
@@ -47,7 +49,7 @@ namespace Discord.WebSocket
discord.RemoveUser(Id);
}
}
internal void Update(ClientState state, PresenceModel model)
{
Presence = SocketPresence.Create(model);


+ 4
- 0
src/Discord.Net.WebSocket/Entities/Users/SocketGroupUser.cs View File

@@ -29,6 +29,10 @@ namespace Discord.WebSocket
/// <inheritdoc />
public override string AvatarId { get { return GlobalUser.AvatarId; } internal set { GlobalUser.AvatarId = value; } }
/// <inheritdoc />
public override string BannerId { get { return GlobalUser.BannerId; } internal set { GlobalUser.BannerId = value; } }
/// <inheritdoc />
public override Color? AccentColor { get { return GlobalUser.AccentColor; } internal set { GlobalUser.AccentColor = value; } }
/// <inheritdoc />
internal override SocketPresence Presence { get { return GlobalUser.Presence; } set { GlobalUser.Presence = value; } }

/// <inheritdoc />


+ 6
- 1
src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs View File

@@ -38,6 +38,11 @@ namespace Discord.WebSocket
public override ushort DiscriminatorValue { get { return GlobalUser.DiscriminatorValue; } internal set { GlobalUser.DiscriminatorValue = value; } }
/// <inheritdoc />
public override string AvatarId { get { return GlobalUser.AvatarId; } internal set { GlobalUser.AvatarId = value; } }
/// <inheritdoc />
public override string BannerId { get { return GlobalUser.BannerId; } internal set { GlobalUser.BannerId = value; } }
/// <inheritdoc />
public override Color? AccentColor { get { return GlobalUser.AccentColor; } internal set { GlobalUser.AccentColor = value; } }

/// <inheritdoc />
public GuildPermissions GuildPermissions => new GuildPermissions(Permissions.ResolveGuild(Guild, this));
internal override SocketPresence Presence { get; set; }
@@ -91,7 +96,7 @@ namespace Discord.WebSocket
/// Returns the position of the user within the role hierarchy.
/// </summary>
/// <remarks>
/// The returned value equal to the position of the highest role the user has, or
/// The returned value equal to the position of the highest role the user has, or
/// <see cref="int.MaxValue"/> if user is the server owner.
/// </remarks>
public int Hierarchy


+ 4
- 0
src/Discord.Net.WebSocket/Entities/Users/SocketSelfUser.cs View File

@@ -29,6 +29,10 @@ namespace Discord.WebSocket
/// <inheritdoc />
public override string AvatarId { get { return GlobalUser.AvatarId; } internal set { GlobalUser.AvatarId = value; } }
/// <inheritdoc />
public override string BannerId { get { return GlobalUser.BannerId; } internal set { GlobalUser.BannerId = value; } }
/// <inheritdoc />
public override Color? AccentColor { get { return GlobalUser.AccentColor; } internal set { GlobalUser.AccentColor = value; } }
/// <inheritdoc />
internal override SocketPresence Presence { get { return GlobalUser.Presence; } set { GlobalUser.Presence = value; } }
/// <inheritdoc />
public UserProperties Flags { get; internal set; }


+ 15
- 1
src/Discord.Net.WebSocket/Entities/Users/SocketThreadUser.cs View File

@@ -36,7 +36,7 @@ namespace Discord.WebSocket

/// <inheritdoc/>
public string Nickname
=> GuildUser.Nickname;
=> GuildUser.Nickname;

/// <inheritdoc/>
public DateTimeOffset? PremiumSince
@@ -53,6 +53,20 @@ namespace Discord.WebSocket
internal set => GuildUser.AvatarId = value;
}

/// <inheritdoc/>
public override string BannerId
{
get => GuildUser.BannerId;
internal set => GuildUser.BannerId = value;
}

/// <inheritdoc/>
public override Color? AccentColor
{
get => GuildUser.AccentColor;
internal set => GuildUser.AccentColor = value;
}

/// <inheritdoc/>
public override ushort DiscriminatorValue
{


+ 8
- 1
src/Discord.Net.WebSocket/Entities/Users/SocketUnknownUser.cs View File

@@ -19,9 +19,16 @@ namespace Discord.WebSocket
public override ushort DiscriminatorValue { get; internal set; }
/// <inheritdoc />
public override string AvatarId { get; internal set; }

/// <inheritdoc />
public override string BannerId { get; internal set; }

/// <inheritdoc />
public override Color? AccentColor { get; internal set; }

/// <inheritdoc />
public override bool IsBot { get; internal set; }
/// <inheritdoc />
public override bool IsWebhook => false;
/// <inheritdoc />


+ 18
- 0
src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs View File

@@ -25,6 +25,10 @@ namespace Discord.WebSocket
/// <inheritdoc />
public abstract string AvatarId { get; internal set; }
/// <inheritdoc />
public abstract string BannerId { get; internal set; }
/// <inheritdoc />
public abstract Color? AccentColor { get; internal set; }
/// <inheritdoc />
public abstract bool IsWebhook { get; }
/// <inheritdoc />
public UserProperties? PublicFlags { get; private set; }
@@ -64,6 +68,16 @@ namespace Discord.WebSocket
AvatarId = model.Avatar.Value;
hasChanges = true;
}
if (model.Banner.IsSpecified && model.Banner.Value != BannerId)
{
BannerId = model.Banner.Value;
hasChanges = true;
}
if (model.AccentColor.IsSpecified && model.AccentColor.Value != AccentColor?.RawValue)
{
AccentColor = model.AccentColor.Value;
hasChanges = true;
}
if (model.Discriminator.IsSpecified)
{
var newVal = ushort.Parse(model.Discriminator.Value, NumberStyles.None, CultureInfo.InvariantCulture);
@@ -99,6 +113,10 @@ namespace Discord.WebSocket
public string GetAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128)
=> CDN.GetUserAvatarUrl(Id, AvatarId, size, format);

/// <inheritdoc />
public string GetBannerUrl(ImageFormat format = ImageFormat.Auto, ushort size = 256)
=> CDN.GetUserBannerUrl(Id, BannerId, size, format);

/// <inheritdoc />
public string GetDefaultAvatarUrl()
=> CDN.GetDefaultUserAvatarUrl(DiscriminatorValue);


+ 17
- 0
src/Discord.Net.WebSocket/Entities/Users/SocketWebhookUser.cs View File

@@ -24,6 +24,23 @@ namespace Discord.WebSocket
public override ushort DiscriminatorValue { get; internal set; }
/// <inheritdoc />
public override string AvatarId { get; internal set; }

/// <inheritdoc />
/// <exception cref="NotSupportedException">Webhook users does not support banners.</exception>
public override string BannerId
{
get => throw new NotSupportedException("Webhook users does not support banners.");
internal set => throw new NotSupportedException("Webhook users does not support banners.");
}

/// <inheritdoc />
/// <exception cref="NotSupportedException">Webhook users does not support accent colors.</exception>
public override Color? AccentColor
{
get => throw new NotSupportedException("Webhook users does not support accent colors.");
internal set => throw new NotSupportedException("Webhook users does not support accent colors.");
}

/// <inheritdoc />
public override bool IsBot { get; internal set; }



+ 1
- 1
src/Discord.Net.Webhook/Discord.Net.Webhook.csproj View File

@@ -6,7 +6,7 @@
<RootNamespace>Discord.Webhook</RootNamespace>
<Description>A core Discord.Net Labs library containing the Webhook client and models.</Description>
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
<Version>2.3.4</Version>
<Version>3.0.0-pre</Version>
<PackageId>Discord.Net.Labs.Webhook</PackageId>
<PackageProjectUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</PackageProjectUrl>
<RepositoryUrl>https://github.com/Discord-Net-Labs/Discord.Net-Labs</RepositoryUrl>


+ 6
- 1
src/Discord.Net.Webhook/Discord.Net.Webhook.xml View File

@@ -31,7 +31,7 @@
<exception cref="T:System.ArgumentException">Thrown if the <paramref name="webhookUrl"/> is an invalid format.</exception>
<exception cref="T:System.ArgumentNullException">Thrown if the <paramref name="webhookUrl"/> is null or whitespace.</exception>
</member>
<member name="M:Discord.Webhook.DiscordWebhookClient.SendMessageAsync(System.String,System.Boolean,System.Collections.Generic.IEnumerable{Discord.Embed},System.String,System.String,Discord.RequestOptions,Discord.AllowedMentions)">
<member name="M:Discord.Webhook.DiscordWebhookClient.SendMessageAsync(System.String,System.Boolean,System.Collections.Generic.IEnumerable{Discord.Embed},System.String,System.String,Discord.RequestOptions,Discord.AllowedMentions,Discord.MessageComponent)">
<summary> Sends a message to the channel for this webhook. </summary>
<returns> Returns the ID of the created message. </returns>
</member>
@@ -99,6 +99,11 @@
Gets or sets the allowed mentions of the message.
</summary>
</member>
<member name="P:Discord.Webhook.WebhookMessageProperties.Components">
<summary>
Gets or sets the components that the message should display.
</summary>
</member>
<member name="M:Discord.Webhook.WebhookClientHelper.GetWebhookAsync(Discord.Webhook.DiscordWebhookClient,System.UInt64)">
<exception cref="T:System.InvalidOperationException">Could not find a webhook with the supplied credentials.</exception>
</member>


+ 2
- 2
src/Discord.Net.Webhook/DiscordWebhookClient.cs View File

@@ -88,8 +88,8 @@ namespace Discord.Webhook
/// <summary> Sends a message to the channel for this webhook. </summary>
/// <returns> Returns the ID of the created message. </returns>
public Task<ulong> SendMessageAsync(string text = null, bool isTTS = false, IEnumerable<Embed> embeds = null,
string username = null, string avatarUrl = null, RequestOptions options = null, AllowedMentions allowedMentions = null)
=> WebhookClientHelper.SendMessageAsync(this, text, isTTS, embeds, username, avatarUrl, allowedMentions, options);
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);

/// <summary>
/// Modifies a message posted using this webhook.


+ 4
- 0
src/Discord.Net.Webhook/Entities/Messages/WebhookMessageProperties.cs View File

@@ -22,5 +22,9 @@ namespace Discord.Webhook
/// Gets or sets the allowed mentions of the message.
/// </summary>
public Optional<AllowedMentions> AllowedMentions { get; set; }
/// <summary>
/// Gets or sets the components that the message should display.
/// </summary>
public Optional<MessageComponent> Components { get; set; }
}
}

+ 3
- 0
src/Discord.Net.Webhook/Entities/Webhooks/RestInternalWebhook.cs View File

@@ -17,6 +17,7 @@ namespace Discord.Webhook
public string Name { get; private set; }
public string AvatarId { get; private set; }
public ulong? GuildId { get; private set; }
public ulong? ApplicationId { get; private set; }

public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id);

@@ -44,6 +45,8 @@ namespace Discord.Webhook
GuildId = model.GuildId.Value;
if (model.Name.IsSpecified)
Name = model.Name.Value;

ApplicationId = model.ApplicationId;
}

public string GetAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128)


+ 5
- 2
src/Discord.Net.Webhook/WebhookClientHelper.cs View File

@@ -21,7 +21,7 @@ namespace Discord.Webhook
return RestInternalWebhook.Create(client, model);
}
public static async Task<ulong> SendMessageAsync(DiscordWebhookClient client,
string text, bool isTTS, IEnumerable<Embed> embeds, string username, string avatarUrl, AllowedMentions allowedMentions, RequestOptions options)
string text, bool isTTS, IEnumerable<Embed> embeds, string username, string avatarUrl, AllowedMentions allowedMentions, RequestOptions options, MessageComponent component)
{
var args = new CreateWebhookMessageParams
{
@@ -37,6 +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();

var model = await client.ApiClient.CreateWebhookMessageAsync(client.Webhook.Id, args, options: options).ConfigureAwait(false);
return model.Id;
@@ -83,7 +85,8 @@ namespace Discord.Webhook
: Optional.Create<API.Embed[]>(),
AllowedMentions = args.AllowedMentions.IsSpecified
? args.AllowedMentions.Value.ToModel()
: Optional.Create<API.AllowedMentions>()
: Optional.Create<API.AllowedMentions>(),
Components = args.Components.IsSpecified ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() : Optional<API.ActionRowComponent[]>.Unspecified,
};

await client.ApiClient.ModifyWebhookMessageAsync(client.Webhook.Id, messageId, apiArgs, options)


+ 10
- 10
src/Discord.Net/Discord.Net.nuspec View File

@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Discord.Net.Labs</id>
<version>3.0.1-pre$suffix$</version>
<version>3.0.2-pre$suffix$</version>
<title>Discord.Net Labs</title>
<authors>Discord.Net Contributors</authors>
<owners>quinchs</owners>
@@ -14,23 +14,23 @@
<iconUrl>https://avatars.githubusercontent.com/u/84047264</iconUrl>
<dependencies>
<group targetFramework="net461">
<dependency id="Discord.Net.Labs.Core" version="3.0.0-pre$suffix$" />
<dependency id="Discord.Net.Labs.Rest" version="3.0.0-pre$suffix$" />
<dependency id="Discord.Net.Labs.WebSocket" version="3.0.0-pre$suffix$" />
<dependency id="Discord.Net.Labs.Core" version="3.0.1-pre$suffix$" />
<dependency id="Discord.Net.Labs.Rest" version="3.0.1-pre$suffix$" />
<dependency id="Discord.Net.Labs.WebSocket" version="3.0.1-pre$suffix$" />
<dependency id="Discord.Net.Labs.Commands" version="2.3.5$suffix$" />
<dependency id="Discord.Net.Labs.Webhook" version="2.3.4$suffix$" />
</group>
<group targetFramework="netstandard2.0">
<dependency id="Discord.Net.Labs.Core" version="3.0.0-pre$suffix$" />
<dependency id="Discord.Net.Labs.Rest" version="3.0.0-pre$suffix$" />
<dependency id="Discord.Net.Labs.WebSocket" version="3.0.0-pre$suffix$" />
<dependency id="Discord.Net.Labs.Core" version="3.0.1-pre$suffix$" />
<dependency id="Discord.Net.Labs.Rest" version="3.0.1-pre$suffix$" />
<dependency id="Discord.Net.Labs.WebSocket" version="3.0.1-pre$suffix$" />
<dependency id="Discord.Net.Labs.Commands" version="2.3.5$suffix$" />
<dependency id="Discord.Net.Labs.Webhook" version="2.3.4$suffix$" />
</group>
<group targetFramework="netstandard2.1">
<dependency id="Discord.Net.Labs.Core" version="3.0.0-pre$suffix$" />
<dependency id="Discord.Net.Labs.Rest" version="3.0.0-pre$suffix$" />
<dependency id="Discord.Net.Labs.WebSocket" version="3.0.0-pre$suffix$" />
<dependency id="Discord.Net.Labs.Core" version="3.0.1-pre$suffix$" />
<dependency id="Discord.Net.Labs.Rest" version="3.0.1-pre$suffix$" />
<dependency id="Discord.Net.Labs.WebSocket" version="3.0.1-pre$suffix$" />
<dependency id="Discord.Net.Labs.Commands" version="2.3.5$suffix$" />
<dependency id="Discord.Net.Labs.Webhook" version="2.3.4$suffix$" />
</group>


+ 14
- 2
test/Discord.Net.Tests.Unit/GuildPermissionsTests.cs View File

@@ -91,7 +91,13 @@ namespace Discord
AssertFlag(() => new GuildPermissions(manageNicknames: true), GuildPermission.ManageNicknames);
AssertFlag(() => new GuildPermissions(manageRoles: true), GuildPermission.ManageRoles);
AssertFlag(() => new GuildPermissions(manageWebhooks: true), GuildPermission.ManageWebhooks);
AssertFlag(() => new GuildPermissions(manageEmojis: true), GuildPermission.ManageEmojis);
AssertFlag(() => new GuildPermissions(manageEmojisAndStickers: true), GuildPermission.ManageEmojisAndStickers);
AssertFlag(() => new GuildPermissions(useSlashCommands: true), GuildPermission.UseSlashCommands);
AssertFlag(() => new GuildPermissions(requestToSpeak: true), GuildPermission.RequestToSpeak);
AssertFlag(() => new GuildPermissions(manageThreads: true), GuildPermission.ManageThreads);
AssertFlag(() => new GuildPermissions(usePublicThreads: true), GuildPermission.UsePublicThreads);
AssertFlag(() => new GuildPermissions(usePrivateThreads: true), GuildPermission.UsePrivateThreads);
AssertFlag(() => new GuildPermissions(useExternalStickers: true), GuildPermission.UseExternalStickers);
}

/// <summary>
@@ -161,7 +167,13 @@ namespace Discord
AssertUtil(GuildPermission.ManageNicknames, x => x.ManageNicknames, (p, enable) => p.Modify(manageNicknames: enable));
AssertUtil(GuildPermission.ManageRoles, x => x.ManageRoles, (p, enable) => p.Modify(manageRoles: enable));
AssertUtil(GuildPermission.ManageWebhooks, x => x.ManageWebhooks, (p, enable) => p.Modify(manageWebhooks: enable));
AssertUtil(GuildPermission.ManageEmojis, x => x.ManageEmojisAndStickers, (p, enable) => p.Modify(manageEmojis: enable));
AssertUtil(GuildPermission.ManageEmojisAndStickers, x => x.ManageEmojisAndStickers, (p, enable) => p.Modify(manageEmojisAndStickers: enable));
AssertUtil(GuildPermission.UseSlashCommands, x => x.UseSlashCommands, (p, enable) => p.Modify(useSlashCommands: enable));
AssertUtil(GuildPermission.RequestToSpeak, x => x.RequestToSpeak, (p, enable) => p.Modify(requestToSpeak: enable));
AssertUtil(GuildPermission.ManageThreads, x => x.ManageThreads, (p, enable) => p.Modify(manageThreads: enable));
AssertUtil(GuildPermission.UsePublicThreads, x => x.UsePublicThreads, (p, enable) => p.Modify(usePublicThreads: enable));
AssertUtil(GuildPermission.UsePrivateThreads, x => x.UsePrivateThreads, (p, enable) => p.Modify(usePrivateThreads: enable));
AssertUtil(GuildPermission.UseExternalStickers, x => x.UseExternalStickers, (p, enable) => p.Modify(useExternalStickers: enable));
}
}
}

Loading…
Cancel
Save