Browse Source

Merge branch 'dev' into fix/app-commands-permissions

pull/2293/head
Cenk Ergen GitHub 3 years ago
parent
commit
782348a75a
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 828 additions and 424 deletions
  1. +2
    -0
      .github/FUNDING.yml
  2. +13
    -0
      CHANGELOG.md
  3. +1
    -1
      Discord.Net.targets
  4. +1
    -1
      docs/docfx.json
  5. +2
    -0
      src/Discord.Net.Commands/Discord.Net.Commands.csproj
  6. +3
    -3
      src/Discord.Net.Commands/Results/MatchResult.cs
  7. +2
    -0
      src/Discord.Net.Core/Discord.Net.Core.csproj
  8. +1
    -0
      src/Discord.Net.Core/DiscordErrorCode.cs
  9. +1
    -1
      src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs
  10. +0
    -1
      src/Discord.Net.Core/Entities/Guilds/IGuild.cs
  11. +1
    -1
      src/Discord.Net.Core/Entities/Guilds/IGuildScheduledEvent.cs
  12. +1
    -1
      src/Discord.Net.Core/Entities/Interactions/ApplicationCommandOptionType.cs
  13. +1
    -1
      src/Discord.Net.Core/Entities/Interactions/IDiscordInteraction.cs
  14. +5
    -5
      src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentBuilder.cs
  15. +4
    -4
      src/Discord.Net.Core/Entities/Interactions/Modals/ModalBuilder.cs
  16. +13
    -2
      src/Discord.Net.Core/Entities/Messages/MessageReference.cs
  17. +1
    -1
      src/Discord.Net.Core/Entities/Users/GuildUserProperties.cs
  18. +3
    -3
      src/Discord.Net.Core/Entities/Users/IGuildUser.cs
  19. +3
    -2
      src/Discord.Net.Core/Format.cs
  20. +1
    -1
      src/Discord.Net.Core/Utils/UrlValidation.cs
  21. +3
    -3
      src/Discord.Net.Interactions/Attributes/AutocompleteAttribute.cs
  22. +0
    -2
      src/Discord.Net.Interactions/Attributes/Modals/ModalInputAttribute.cs
  23. +1
    -1
      src/Discord.Net.Interactions/Attributes/Modals/ModalTextInputAttribute.cs
  24. +2
    -2
      src/Discord.Net.Interactions/Attributes/Preconditions/RequireUserPermissionAttribute.cs
  25. +1
    -1
      src/Discord.Net.Interactions/Builders/Commands/SlashCommandBuilder.cs
  26. +1
    -1
      src/Discord.Net.Interactions/Builders/Modals/Inputs/TextInputComponentBuilder.cs
  27. +1
    -1
      src/Discord.Net.Interactions/Builders/Modals/ModalBuilder.cs
  28. +2
    -1
      src/Discord.Net.Interactions/Builders/ModuleBuilder.cs
  29. +1
    -1
      src/Discord.Net.Interactions/Builders/Parameters/ParameterBuilder.cs
  30. +3
    -1
      src/Discord.Net.Interactions/Discord.Net.Interactions.csproj
  31. +1
    -2
      src/Discord.Net.Interactions/InteractionContext.cs
  32. +6
    -6
      src/Discord.Net.Interactions/InteractionModuleBase.cs
  33. +15
    -10
      src/Discord.Net.Interactions/InteractionService.cs
  34. +2
    -2
      src/Discord.Net.Interactions/RestInteractionModuleBase.cs
  35. +1
    -1
      src/Discord.Net.Interactions/Results/TypeConverterResult.cs
  36. +3
    -0
      src/Discord.Net.Rest/API/Common/MessageReference.cs
  37. +2
    -0
      src/Discord.Net.Rest/Discord.Net.Rest.csproj
  38. +35
    -17
      src/Discord.Net.Rest/DiscordRestApiClient.cs
  39. +5
    -3
      src/Discord.Net.Rest/Entities/Channels/RestStageChannel.cs
  40. +49
    -49
      src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs
  41. +181
    -37
      src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs
  42. +0
    -1
      src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
  43. +1
    -2
      src/Discord.Net.Rest/Entities/Interactions/RestApplicationCommand.cs
  44. +0
    -1
      src/Discord.Net.Rest/Entities/Interactions/RestInteraction.cs
  45. +2
    -1
      src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
  46. +1
    -1
      src/Discord.Net.Rest/Entities/Roles/RestRole.cs
  47. +1
    -0
      src/Discord.Net.Rest/Extensions/EntityExtensions.cs
  48. +1
    -1
      src/Discord.Net.Rest/Net/ED25519/CryptoBytes.cs
  49. +3
    -8
      src/Discord.Net.Rest/Net/Queue/RequestQueue.cs
  50. +2
    -0
      src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj
  51. +3
    -1
      src/Discord.Net.WebSocket/DiscordSocketClient.cs
  52. +2
    -0
      src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs
  53. +5
    -4
      src/Discord.Net.WebSocket/Entities/Channels/SocketStageChannel.cs
  54. +21
    -21
      src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs
  55. +215
    -40
      src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs
  56. +0
    -1
      src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
  57. +85
    -0
      src/Discord.Net.WebSocket/Entities/Interaction/Modals/SocketModal.cs
  58. +1
    -2
      src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketApplicationCommand.cs
  59. +2
    -1
      src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
  60. +2
    -0
      src/Discord.Net.Webhook/Discord.Net.Webhook.csproj
  61. +14
    -14
      src/Discord.Net.Webhook/DiscordWebhookClient.cs
  62. +17
    -16
      src/Discord.Net.Webhook/WebhookClientHelper.cs
  63. +34
    -34
      src/Discord.Net/Discord.Net.nuspec
  64. +42
    -106
      test/Discord.Net.Tests.Unit/MockedEntities/MockedVoiceChannel.cs

+ 2
- 0
.github/FUNDING.yml View File

@@ -1 +1,3 @@
github: quinchs
open_collective: discordnet
custom: https://paypal.me/quinchs

+ 13
- 0
CHANGELOG.md View File

@@ -1,5 +1,18 @@
# Changelog

## [3.6.1] - 2022-04-30
### Added
- #2272 add 50080 Error code (503e720)

### Fixed
- #2267 Permissions v2 Invalid Operation Exception (a8f6075)
- #2271 null user on interaction without bot scope (f2bb55e)
- #2274 Implement fix for Custom Id Segments NRE (0d74c5c)

### Misc
- 3.6.0 (27226f0)


## [3.6.0] - 2022-04-28
### Added
- #2136 Passing CustomId matches into contexts (4ce1801)


+ 1
- 1
Discord.Net.targets View File

@@ -1,6 +1,6 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VersionPrefix>3.6.0</VersionPrefix>
<VersionPrefix>3.6.1</VersionPrefix>
<LangVersion>latest</LangVersion>
<Authors>Discord.Net Contributors</Authors>
<PackageTags>discord;discordapp</PackageTags>


+ 1
- 1
docs/docfx.json View File

@@ -60,7 +60,7 @@
"overwrite": "_overwrites/**/**.md",
"globalMetadata": {
"_appTitle": "Discord.Net Documentation",
"_appFooter": "Discord.Net (c) 2015-2022 3.6.0",
"_appFooter": "Discord.Net (c) 2015-2022 3.6.1",
"_enableSearch": true,
"_appLogoPath": "marketing/logo/SVG/Logomark Purple.svg",
"_appFaviconPath": "favicon.ico"


+ 2
- 0
src/Discord.Net.Commands/Discord.Net.Commands.csproj View File

@@ -7,6 +7,8 @@
<Description>A Discord.Net extension adding support for bot commands.</Description>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net6.0;net5.0;net461;netstandard2.0;netstandard2.1</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">net6.0;net5.0;netstandard2.0;netstandard2.1</TargetFrameworks>
<WarningLevel>5</WarningLevel>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" />


+ 3
- 3
src/Discord.Net.Commands/Results/MatchResult.cs View File

@@ -1,4 +1,4 @@
using System;
using System;

namespace Discord.Commands
{
@@ -12,7 +12,7 @@ namespace Discord.Commands
/// <summary>
/// Gets on which pipeline stage the command may have matched or failed.
/// </summary>
public IResult? Pipeline { get; }
public IResult Pipeline { get; }

/// <inheritdoc />
public CommandError? Error { get; }
@@ -21,7 +21,7 @@ namespace Discord.Commands
/// <inheritdoc />
public bool IsSuccess => !Error.HasValue;

private MatchResult(CommandMatch? match, IResult? pipeline, CommandError? error, string errorReason)
private MatchResult(CommandMatch? match, IResult pipeline, CommandError? error, string errorReason)
{
Match = match;
Error = error;


+ 2
- 0
src/Discord.Net.Core/Discord.Net.Core.csproj View File

@@ -7,6 +7,8 @@
<Description>The core components for the Discord.Net library.</Description>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net6.0;net5.0;net461;netstandard2.0;netstandard2.1</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">net6.0;net5.0;netstandard2.0;netstandard2.1</TargetFrameworks>
<WarningLevel>5</WarningLevel>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />


+ 1
- 0
src/Discord.Net.Core/DiscordErrorCode.cs View File

@@ -152,6 +152,7 @@ namespace Discord
InvalidMessageType = 50068,
PaymentSourceRequiredForGift = 50070,
CannotDeleteRequiredCommunityChannel = 50074,
CannotEditStickersWithinAMessage = 50080,
InvalidSticker = 50081,
CannotExecuteOnArchivedThread = 50083,
InvalidThreadNotificationSettings = 50084,


+ 1
- 1
src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs View File

@@ -6,7 +6,7 @@ namespace Discord
/// <summary>
/// Represents a generic voice channel in a guild.
/// </summary>
public interface IVoiceChannel : INestedChannel, IAudioChannel, IMentionable
public interface IVoiceChannel : IMessageChannel, INestedChannel, IAudioChannel, IMentionable
{
/// <summary>
/// Gets the bit-rate that the clients in this voice channel are requested to use.


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

@@ -1173,7 +1173,6 @@ namespace Discord
/// in order to use this property.
/// </remarks>
/// </param>
/// <param name="speakers">A collection of speakers for the event.</param>
/// <param name="location">The location of the event; links are supported</param>
/// <param name="coverImage">The optional banner image for the event.</param>
/// <param name="options">The options to be used when sending the request.</param>


+ 1
- 1
src/Discord.Net.Core/Entities/Guilds/IGuildScheduledEvent.cs View File

@@ -89,7 +89,7 @@ namespace Discord
/// Gets this events banner image url.
/// </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 name="size">The size of the image to return in. This can be any power of two between 16 and 2048.</param>
/// <returns>The cover images url.</returns>
string GetCoverImageUrl(ImageFormat format = ImageFormat.Auto, ushort size = 1024);



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

@@ -56,7 +56,7 @@ namespace Discord
Number = 10,

/// <summary>
/// A <see cref="Discord.Attachment"/>.
/// A <see cref="IAttachment"/>.
/// </summary>
Attachment = 11
}


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

@@ -55,7 +55,7 @@ namespace Discord
string UserLocale { get; }

/// <summary>
/// Gets the preferred locale of the guild this interaction was executed in. <see cref="null"/> if not executed in a guild.
/// Gets the preferred locale of the guild this interaction was executed in. <see langword="null"/> if not executed in a guild.
/// </summary>
/// <remarks>
/// Non-community guilds (With no locale setting available) will have en-US as the default value sent by Discord.


+ 5
- 5
src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentBuilder.cs View File

@@ -1194,9 +1194,9 @@ namespace Discord
/// <summary>
/// Gets or sets the default value of the text input.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException"><see cref="Value.Length"/> is less than 0.</exception>
/// <exception cref="ArgumentOutOfRangeException"><see cref="Value"/>.Length is less than 0.</exception>
/// <exception cref="ArgumentOutOfRangeException">
/// <see cref="Value.Length"/> is greater than <see cref="LargestMaxLength"/> or <see cref="MaxLength"/>.
/// <see cref="Value"/>.Length is greater than <see cref="LargestMaxLength"/> or <see cref="MaxLength"/>.
/// </exception>
public string Value
{
@@ -1306,18 +1306,18 @@ namespace Discord
/// <summary>
/// Sets the minimum length of the current builder.
/// </summary>
/// <param name="placeholder">The value to set.</param>
/// <param name="minLength">The value to set.</param>
/// <returns>The current builder. </returns>
public TextInputBuilder WithMinLength(int minLength)
{
MinLength = minLength;
return this;
}
/// <summary>
/// Sets the maximum length of the current builder.
/// </summary>
/// <param name="placeholder">The value to set.</param>
/// <param name="maxLength">The value to set.</param>
/// <returns>The current builder. </returns>
public TextInputBuilder WithMaxLength(int maxLength)
{


+ 4
- 4
src/Discord.Net.Core/Entities/Interactions/Modals/ModalBuilder.cs View File

@@ -64,18 +64,18 @@ namespace Discord
/// <summary>
/// Sets the custom id of the current modal.
/// </summary>
/// <param name="title">The value to set the custom id to.</param>
/// <param name="customId">The value to set the custom id to.</param>
/// <returns>The current builder.</returns>
public ModalBuilder WithCustomId(string customId)
{
CustomId = customId;
return this;
}
/// <summary>
/// Adds a component to the current builder.
/// </summary>
/// <param name="title">The component to add.</param>
/// <param name="component">The component to add.</param>
/// <returns>The current builder.</returns>
public ModalBuilder AddTextInput(TextInputBuilder component)
{
@@ -213,7 +213,7 @@ namespace Discord
/// Adds a <see cref="TextInputBuilder"/> to the <see cref="ModalComponentBuilder"/> at the specific row.
/// If the row cannot accept the component then it will add it to a row that can.
/// </summary>
/// <param name="text">The <see cref="TextInputBuilder"> to add.</param>
/// <param name="text">The <see cref="TextInputBuilder"/> to add.</param>
/// <param name="row">The row to add the text input.</param>
/// <exception cref="InvalidOperationException">There are no more rows to add a text input to.</exception>
/// <exception cref="ArgumentException"><paramref name="row"/> must be less than <see cref="MaxActionRowCount"/>.</exception>


+ 13
- 2
src/Discord.Net.Core/Entities/Messages/MessageReference.cs View File

@@ -27,6 +27,12 @@ namespace Discord
/// </summary>
public Optional<ulong> GuildId { get; internal set; }

/// <summary>
/// Gets whether to error if the referenced message doesn't exist instead of sending as a normal (non-reply) message
/// Defaults to true.
/// </summary>
public Optional<bool> FailIfNotExists { get; internal set; }

/// <summary>
/// Initializes a new instance of the <see cref="MessageReference"/> class.
/// </summary>
@@ -39,16 +45,21 @@ namespace Discord
/// <param name="guildId">
/// The ID of the guild that will be referenced. It will be validated if sent.
/// </param>
public MessageReference(ulong? messageId = null, ulong? channelId = null, ulong? guildId = null)
/// <param name="failIfNotExists">
/// Whether to error if the referenced message doesn't exist instead of sending as a normal (non-reply) message. Defaults to true.
/// </param>
public MessageReference(ulong? messageId = null, ulong? channelId = null, ulong? guildId = null, bool? failIfNotExists = null)
{
MessageId = messageId ?? Optional.Create<ulong>();
InternalChannelId = channelId ?? Optional.Create<ulong>();
GuildId = guildId ?? Optional.Create<ulong>();
FailIfNotExists = failIfNotExists ?? Optional.Create<bool>();
}

private string DebuggerDisplay
=> $"Channel ID: ({ChannelId}){(GuildId.IsSpecified ? $", Guild ID: ({GuildId.Value})" : "")}" +
$"{(MessageId.IsSpecified ? $", Message ID: ({MessageId.Value})" : "")}";
$"{(MessageId.IsSpecified ? $", Message ID: ({MessageId.Value})" : "")}" +
$"{(FailIfNotExists.IsSpecified ? $", FailIfNotExists: ({FailIfNotExists.Value})" : "")}";

public override string ToString()
=> DebuggerDisplay;


+ 1
- 1
src/Discord.Net.Core/Entities/Users/GuildUserProperties.cs View File

@@ -79,7 +79,7 @@ namespace Discord
/// Sets a timestamp how long a user should be timed out for.
/// </summary>
/// <remarks>
/// <see cref="null"/> or a time in the past to clear a currently existing timeout.
/// <see langword="null"/> or a time in the past to clear a currently existing timeout.
/// </remarks>
public Optional<DateTimeOffset?> TimedOutUntil { get; set; }
}


+ 3
- 3
src/Discord.Net.Core/Entities/Users/IGuildUser.cs View File

@@ -104,7 +104,7 @@ namespace Discord
/// Gets the date and time that indicates if and for how long a user has been timed out.
/// </summary>
/// <remarks>
/// <see cref="null"/> or a timestamp in the past if the user is not timed out.
/// <see langword="null"/> or a timestamp in the past if the user is not timed out.
/// </remarks>
/// <returns>
/// A <see cref="DateTimeOffset"/> indicating how long the user will be timed out for.
@@ -116,7 +116,7 @@ namespace Discord
/// </summary>
/// <example>
/// <para>The following example checks if the current user has the ability to send a message with attachment in
/// this channel; if so, uploads a file via <see cref="IMessageChannel.SendFileAsync(string, string, bool, Embed, RequestOptions, bool, AllowedMentions, MessageReference)"/>.</para>
/// this channel; if so, uploads a file via <see cref="IMessageChannel.SendFileAsync(string, string, bool, Embed, RequestOptions, bool, AllowedMentions, MessageReference, MessageComponent, ISticker[], Embed[], MessageFlags)"/>.</para>
/// <code language="cs">
/// if (currentUser?.GetPermissions(targetChannel)?.AttachFiles)
/// await targetChannel.SendFileAsync("fortnite.png");
@@ -151,7 +151,7 @@ namespace Discord
/// If the user does not have a guild avatar, this will be the user's regular avatar.
/// </remarks>
/// <param name="format">The format to return.</param>
/// <param name="size">The size of the image to return in. This can be any power of two between 16 and 2048.
/// <param name="size">The size of the image to return in. This can be any power of two between 16 and 2048.</param>
/// <returns>
/// A string representing the URL of the displayed avatar for this user. <see langword="null"/> if the user does not have an avatar in place.
/// </returns>


+ 3
- 2
src/Discord.Net.Core/Format.cs View File

@@ -37,8 +37,9 @@ namespace Discord
/// <summary> Sanitizes the string, safely escaping any Markdown sequences. </summary>
public static string Sanitize(string text)
{
foreach (string unsafeChar in SensitiveCharacters)
text = text.Replace(unsafeChar, $"\\{unsafeChar}");
if (text != null)
foreach (string unsafeChar in SensitiveCharacters)
text = text.Replace(unsafeChar, $"\\{unsafeChar}");
return text;
}



+ 1
- 1
src/Discord.Net.Core/Utils/UrlValidation.cs View File

@@ -23,7 +23,7 @@ namespace Discord.Utils

/// <summary>
/// Not full URL validation right now. Just Ensures the protocol is either http, https, or discord
/// <see cref="Validate(string)"/> should be used everything other than url buttons.
/// <see cref="Validate(string, bool)"/> should be used everything other than url buttons.
/// </summary>
/// <param name="url">The URL to validate before sending to discord.</param>
/// <exception cref="InvalidOperationException">A URL must include a protocol (either http, https, or discord).</exception>


+ 3
- 3
src/Discord.Net.Interactions/Attributes/AutocompleteAttribute.cs View File

@@ -3,7 +3,7 @@ using System;
namespace Discord.Interactions
{
/// <summary>
/// Set the <see cref="ApplicationCommandOptionProperties.Autocomplete"/> to <see langword="true"/>.
/// Set the <see cref="ApplicationCommandOptionProperties.IsAutocomplete"/> to <see langword="true"/>.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
public class AutocompleteAttribute : Attribute
@@ -14,7 +14,7 @@ namespace Discord.Interactions
public Type AutocompleteHandlerType { get; }

/// <summary>
/// Set the <see cref="ApplicationCommandOptionProperties.Autocomplete"/> to <see langword="true"/> and define a <see cref="AutocompleteHandler"/> to handle
/// Set the <see cref="ApplicationCommandOptionProperties.IsAutocomplete"/> to <see langword="true"/> and define a <see cref="AutocompleteHandler"/> to handle
/// Autocomplete interactions targeting the parameter this <see cref="Attribute"/> is applied to.
/// </summary>
/// <remarks>
@@ -29,7 +29,7 @@ namespace Discord.Interactions
}

/// <summary>
/// Set the <see cref="ApplicationCommandOptionProperties.Autocomplete"/> to <see langword="true"/> without specifying a <see cref="AutocompleteHandler"/>.
/// Set the <see cref="ApplicationCommandOptionProperties.IsAutocomplete"/> to <see langword="true"/> without specifying a <see cref="AutocompleteHandler"/>.
/// </summary>
public AutocompleteAttribute() { }
}


+ 0
- 2
src/Discord.Net.Interactions/Attributes/Modals/ModalInputAttribute.cs View File

@@ -21,9 +21,7 @@ namespace Discord.Interactions
/// <summary>
/// Create a new <see cref="ModalInputAttribute"/>.
/// </summary>
/// <param name="label">The label of the input.</param>
/// <param name="customId">The custom id of the input.</param>
/// <param name="required">Whether the user is required to input a value.></param>
protected ModalInputAttribute(string customId)
{
CustomId = customId;


+ 1
- 1
src/Discord.Net.Interactions/Attributes/Modals/ModalTextInputAttribute.cs View File

@@ -36,7 +36,7 @@ namespace Discord.Interactions
/// <summary>
/// Create a new <see cref="ModalTextInputAttribute"/>.
/// </summary>
/// <param name="customId"The custom id of the text input.></param>
/// <param name="customId">The custom id of the text input.></param>
/// <param name="style">The style of the text input.</param>
/// <param name="placeholder">The placeholder of the text input.</param>
/// <param name="minLength">The minimum length of the text input's content.</param>


+ 2
- 2
src/Discord.Net.Interactions/Attributes/Preconditions/RequireUserPermissionAttribute.cs View File

@@ -29,7 +29,7 @@ namespace Discord.Interactions
/// <remarks>
/// This precondition will always fail if the command is being invoked in a <see cref="IPrivateChannel"/>.
/// </remarks>
/// <param name="permission">
/// <param name="guildPermission">
/// The <see cref="Discord.GuildPermission" /> that the user must have. Multiple permissions can be
/// specified by ORing the permissions together.
/// </param>
@@ -41,7 +41,7 @@ namespace Discord.Interactions
/// <summary>
/// Requires that the user invoking the command to have a specific <see cref="Discord.ChannelPermission"/>.
/// </summary>
/// <param name="permission">
/// <param name="channelPermission">
/// The <see cref="Discord.ChannelPermission"/> that the user must have. Multiple permissions can be
/// specified by ORing the permissions together.
/// </param>


+ 1
- 1
src/Discord.Net.Interactions/Builders/Commands/SlashCommandBuilder.cs View File

@@ -56,7 +56,7 @@ namespace Discord.Interactions.Builders
/// <summary>
/// Sets <see cref="DefaultPermission"/>.
/// </summary>
/// <param name="defaultPermision">New value of the <see cref="DefaultPermission"/>.</param>
/// <param name="permission">New value of the <see cref="DefaultPermission"/>.</param>
/// <returns>
/// The builder instance.
/// </returns>


+ 1
- 1
src/Discord.Net.Interactions/Builders/Modals/Inputs/TextInputComponentBuilder.cs View File

@@ -41,7 +41,7 @@ namespace Discord.Interactions.Builders
/// <summary>
/// Sets <see cref="Style"/>.
/// </summary>
/// <param name="style">New value of the <see cref="SetValue(string)"/>.</param>
/// <param name="style">New value of the <see cref="Style"/>.</param>
/// <returns>
/// The builder instance.
/// </returns>


+ 1
- 1
src/Discord.Net.Interactions/Builders/Modals/ModalBuilder.cs View File

@@ -64,7 +64,7 @@ namespace Discord.Interactions.Builders
}

/// <summary>
/// Adds text components to <see cref="TextComponents"/>.
/// Adds text components to <see cref="Components"/>.
/// </summary>
/// <param name="configure">Text Component builder factory.</param>
/// <returns>


+ 2
- 1
src/Discord.Net.Interactions/Builders/ModuleBuilder.cs View File

@@ -357,7 +357,8 @@ namespace Discord.Interactions.Builders
return this;

}

/// <summary>
/// Adds a modal command builder to <see cref="ModalCommands"/>.
/// </summary>
/// <param name="configure"><see cref="ModalCommands"/> factory.</param>


+ 1
- 1
src/Discord.Net.Interactions/Builders/Parameters/ParameterBuilder.cs View File

@@ -122,7 +122,7 @@ namespace Discord.Interactions.Builders
/// <summary>
/// Adds preconditions to <see cref="Preconditions"/>
/// </summary>
/// <param name="preconditions">New attributes to be added to <see cref="Preconditions"/>.</param>
/// <param name="attributes">New attributes to be added to <see cref="Preconditions"/>.</param>
/// <returns>
/// The builder instance.
/// </returns>


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

@@ -7,8 +7,10 @@
<RootNamespace>Discord.Interactions</RootNamespace>
<AssemblyName>Discord.Net.Interactions</AssemblyName>
<Description>A Discord.Net extension adding support for Application Commands.</Description>
<WarningLevel>5</WarningLevel>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" />
<ProjectReference Include="..\Discord.Net.Rest\Discord.Net.Rest.csproj" />


+ 1
- 2
src/Discord.Net.Interactions/InteractionContext.cs View File

@@ -24,8 +24,7 @@ namespace Discord.Interactions
/// </summary>
/// <param name="client">The underlying client.</param>
/// <param name="interaction">The underlying interaction.</param>
/// <param name="user"><see cref="IUser"/> who executed the command.</param>
/// <param name="channel"><see cref="ISocketMessageChannel"/> the command originated from.</param>
/// <param name="channel"><see cref="IMessageChannel"/> the command originated from.</param>
public InteractionContext(IDiscordClient client, IDiscordInteraction interaction, IMessageChannel channel = null)
{
Client = client;


+ 6
- 6
src/Discord.Net.Interactions/InteractionModuleBase.cs View File

@@ -45,7 +45,7 @@ namespace Discord.Interactions
protected virtual async Task DeferAsync(bool ephemeral = false, RequestOptions options = null) =>
await Context.Interaction.DeferAsync(ephemeral, options).ConfigureAwait(false);

/// <inheritdoc cref="IDiscordInteraction.RespondAsync(string, Embed[], bool, bool, AllowedMentions, RequestOptions, MessageComponent, Embed)"/>
/// <inheritdoc cref="IDiscordInteraction.RespondAsync(string, Embed[], bool, bool, AllowedMentions, MessageComponent, Embed, RequestOptions)"/>
protected virtual async Task RespondAsync (string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent components = null, Embed embed = null) =>
await Context.Interaction.RespondAsync(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false);
@@ -70,7 +70,7 @@ namespace Discord.Interactions
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null)
=> Context.Interaction.RespondWithFilesAsync(attachments, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options);

/// <inheritdoc cref="IDiscordInteraction.FollowupAsync(string, Embed[], bool, bool, AllowedMentions, RequestOptions, MessageComponent, Embed)"/>
/// <inheritdoc cref="IDiscordInteraction.FollowupAsync(string, Embed[], bool, bool, AllowedMentions, MessageComponent, Embed, RequestOptions)"/>
protected virtual async Task<IUserMessage> FollowupAsync (string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent components = null, Embed embed = null) =>
await Context.Interaction.FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false);
@@ -95,7 +95,7 @@ namespace Discord.Interactions
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null)
=> Context.Interaction.FollowupWithFilesAsync(attachments, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options);

/// <inheritdoc cref="IMessageChannel.SendMessageAsync(string, bool, Embed, RequestOptions, AllowedMentions, MessageReference, MessageComponent, ISticker[], Embed[])"/>
/// <inheritdoc cref="IMessageChannel.SendMessageAsync(string, bool, Embed, RequestOptions, AllowedMentions, MessageReference, MessageComponent, ISticker[], Embed[], MessageFlags)"/>
protected virtual async Task<IUserMessage> ReplyAsync (string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null,
AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null) =>
await Context.Channel.SendMessageAsync(text, false, embed, options, allowedMentions, messageReference, components).ConfigureAwait(false);
@@ -118,9 +118,9 @@ namespace Discord.Interactions
/// <inheritdoc cref="IDiscordInteraction.RespondWithModalAsync(Modal, RequestOptions)"/>
protected virtual async Task RespondWithModalAsync(Modal modal, RequestOptions options = null) => await Context.Interaction.RespondWithModalAsync(modal);
/// <inheritdoc cref="IDiscordInteractionExtentions.RespondWithModalAsync(IDiscordInteraction, IModal, RequestOptions)"/>
protected virtual async Task RespondWithModalAsync<T>(string customId, RequestOptions options = null) where T : class, IModal
=> await Context.Interaction.RespondWithModalAsync<T>(customId, options);
/// <inheritdoc cref="IDiscordInteractionExtentions.RespondWithModalAsync{T}(IDiscordInteraction, string, RequestOptions, Action{ModalBuilder})"/>
protected virtual async Task RespondWithModalAsync<TModal>(string customId, RequestOptions options = null) where TModal : class, IModal
=> await Context.Interaction.RespondWithModalAsync<TModal>(customId, options);

//IInteractionModuleBase



+ 15
- 10
src/Discord.Net.Interactions/InteractionService.cs View File

@@ -421,7 +421,7 @@ namespace Discord.Interactions
/// </summary>
/// <remarks>
/// Commands will be registered as standalone commands, if you want the <see cref="GroupAttribute"/> to take effect,
/// use <see cref="AddModulesToGuildAsync(IGuild, ModuleInfo[])"/>. Registering a commands without group names might cause the command traversal to fail.
/// use <see cref="AddModulesToGuildAsync(IGuild, bool, ModuleInfo[])"/>. Registering a commands without group names might cause the command traversal to fail.
/// </remarks>
/// <param name="guild">The target guild.</param>
/// <param name="commands">Commands to be registered to Discord.</param>
@@ -517,7 +517,7 @@ namespace Discord.Interactions
/// </summary>
/// <remarks>
/// Commands will be registered as standalone commands, if you want the <see cref="GroupAttribute"/> to take effect,
/// use <see cref="AddModulesToGuildAsync(IGuild, ModuleInfo[])"/>. Registering a commands without group names might cause the command traversal to fail.
/// use <see cref="AddModulesToGuildAsync(IGuild, bool, ModuleInfo[])"/>. Registering a commands without group names might cause the command traversal to fail.
/// </remarks>
/// <param name="commands">Commands to be registered to Discord.</param>
/// <returns>
@@ -834,11 +834,16 @@ namespace Discord.Interactions
if (!searchResult.Command.SupportsWildCards || context is not IRouteMatchContainer matchContainer)
return;

var matches = new RouteSegmentMatch[searchResult.RegexCaptureGroups.Length];
for (var i = 0; i < searchResult.RegexCaptureGroups.Length; i++)
matches[i] = new RouteSegmentMatch(searchResult.RegexCaptureGroups[i]);
if (searchResult.RegexCaptureGroups?.Length > 0)
{
var matches = new RouteSegmentMatch[searchResult.RegexCaptureGroups.Length];
for (var i = 0; i < searchResult.RegexCaptureGroups.Length; i++)
matches[i] = new RouteSegmentMatch(searchResult.RegexCaptureGroups[i]);

matchContainer.SetSegmentMatches(matches);
matchContainer.SetSegmentMatches(matches);
}
else
matchContainer.SetSegmentMatches(Array.Empty<RouteSegmentMatch>());
}

internal TypeConverter GetTypeConverter(Type type, IServiceProvider services = null)
@@ -960,7 +965,7 @@ namespace Discord.Interactions
/// Removes a type reader for the given type.
/// </summary>
/// <remarks>
/// Removing a <see cref="TypeReader"/> from the <see cref="CommandService"/> will not dereference the <see cref="TypeReader"/> from the loaded module/command instances.
/// Removing a <see cref="TypeReader"/> from the <see cref="InteractionService"/> will not dereference the <see cref="TypeReader"/> from the loaded module/command instances.
/// You need to reload the modules for the changes to take effect.
/// </remarks>
/// <param name="type">The type to remove the reader from.</param>
@@ -973,7 +978,7 @@ namespace Discord.Interactions
/// Removes a generic type reader from the type <typeparamref name="T"/>.
/// </summary>
/// <remarks>
/// Removing a <see cref="TypeReader"/> from the <see cref="CommandService"/> will not dereference the <see cref="TypeReader"/> from the loaded module/command instances.
/// Removing a <see cref="TypeReader"/> from the <see cref="InteractionService"/> will not dereference the <see cref="TypeReader"/> from the loaded module/command instances.
/// You need to reload the modules for the changes to take effect.
/// </remarks>
/// <typeparam name="T">The type to remove the readers from.</typeparam>
@@ -986,7 +991,7 @@ namespace Discord.Interactions
/// Removes a generic type reader from the given type.
/// </summary>
/// <remarks>
/// Removing a <see cref="TypeReader"/> from the <see cref="CommandService"/> will not dereference the <see cref="TypeReader"/> from the loaded module/command instances.
/// Removing a <see cref="TypeReader"/> from the <see cref="InteractionService"/> will not dereference the <see cref="TypeReader"/> from the loaded module/command instances.
/// You need to reload the modules for the changes to take effect.
/// </remarks>
/// <param name="type">The type to remove the reader from.</param>
@@ -999,7 +1004,7 @@ namespace Discord.Interactions
/// Serialize an object using a <see cref="TypeReader"/> into a <see cref="string"/> to be placed in a Component CustomId.
/// </summary>
/// <remarks>
/// Removing a <see cref="TypeReader"/> from the <see cref="CommandService"/> will not dereference the <see cref="TypeReader"/> from the loaded module/command instances.
/// Removing a <see cref="TypeReader"/> from the <see cref="InteractionService"/> will not dereference the <see cref="TypeReader"/> from the loaded module/command instances.
/// You need to reload the modules for the changes to take effect.
/// </remarks>
/// <typeparam name="T">Type of the object to be serialized.</typeparam>


+ 2
- 2
src/Discord.Net.Interactions/RestInteractionModuleBase.cs View File

@@ -87,12 +87,12 @@ namespace Discord.Interactions
await InteractionService._restResponseCallback(Context, payload).ConfigureAwait(false);
}

protected override async Task RespondWithModalAsync<T>(string customId, RequestOptions options = null)
protected override async Task RespondWithModalAsync<TModal>(string customId, RequestOptions options = null)
{
if (Context.Interaction is not RestInteraction restInteraction)
throw new InvalidOperationException($"Invalid interaction type. Interaction must be a type of {nameof(RestInteraction)} in order to execute this method");

var payload = restInteraction.RespondWithModal<T>(customId, options);
var payload = restInteraction.RespondWithModal<TModal>(customId, options);

if (Context is IRestInteractionContext restContext && restContext.InteractionResponseCallback != null)
await restContext.InteractionResponseCallback.Invoke(payload).ConfigureAwait(false);


+ 1
- 1
src/Discord.Net.Interactions/Results/TypeConverterResult.cs View File

@@ -3,7 +3,7 @@ using System;
namespace Discord.Interactions
{
/// <summary>
/// Represents a result type for <see cref="TypeConverter.ReadAsync(IInteractionContext, WebSocket.SocketSlashCommandDataOption, IServiceProvider)"/>.
/// Represents a result type for <see cref="TypeConverter.ReadAsync(IInteractionContext, IApplicationCommandInteractionDataOption, IServiceProvider)"/>.
/// </summary>
public struct TypeConverterResult : IResult
{


+ 3
- 0
src/Discord.Net.Rest/API/Common/MessageReference.cs View File

@@ -12,5 +12,8 @@ namespace Discord.API

[JsonProperty("guild_id")]
public Optional<ulong> GuildId { get; set; }

[JsonProperty("fail_if_not_exists")]
public Optional<bool> FailIfNotExists { get; set; }
}
}

+ 2
- 0
src/Discord.Net.Rest/Discord.Net.Rest.csproj View File

@@ -7,6 +7,8 @@
<Description>A core Discord.Net library containing the REST client and models.</Description>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net6.0;net5.0;net461;netstandard2.0;netstandard2.1</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">net6.0;net5.0;netstandard2.0;netstandard2.1</TargetFrameworks>
<WarningLevel>5</WarningLevel>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" />


+ 35
- 17
src/Discord.Net.Rest/DiscordRestApiClient.cs View File

@@ -173,10 +173,12 @@ namespace Discord.API
private async Task LogoutInternalAsync()
{
//An exception here will lock the client into the unusable LoggingOut state, but that's probably fine since our client is in an undefined state too.
if (LoginState == LoginState.LoggedOut) return;
if (LoginState == LoginState.LoggedOut)
return;
LoginState = LoginState.LoggingOut;

try { _loginCancelToken?.Cancel(false); }
try
{ _loginCancelToken?.Cancel(false); }
catch { }

await DisconnectInternalAsync(null).ConfigureAwait(false);
@@ -398,7 +400,7 @@ namespace Discord.API
Preconditions.AtLeast(args.Position, 0, nameof(args.Position));
Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name));

if(args.Name.IsSpecified)
if (args.Name.IsSpecified)
Preconditions.AtMost(args.Name.Value.Length, 100, nameof(args.Name));

options = RequestOptions.CreateOrClone(options);
@@ -414,9 +416,9 @@ namespace Discord.API
Preconditions.AtLeast(args.Position, 0, nameof(args.Position));
Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name));

if(args.Name.IsSpecified)
if (args.Name.IsSpecified)
Preconditions.AtMost(args.Name.Value.Length, 100, nameof(args.Name));
if(args.Topic.IsSpecified)
if (args.Topic.IsSpecified)
Preconditions.AtMost(args.Topic.Value.Length, 1024, nameof(args.Name));

Preconditions.AtLeast(args.SlowModeInterval, 0, nameof(args.SlowModeInterval));
@@ -798,9 +800,11 @@ namespace Discord.API
var ids = new BucketIds(channelId: channelId);
return await SendJsonAsync<Message>("POST", () => $"channels/{channelId}/messages", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false);
}


/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
/// <exception cref="InvalidOperationException">This operation may only be called with a <see cref="TokenType.Webhook"/> token.</exception>
public async Task<Message> CreateWebhookMessageAsync(ulong webhookId, CreateWebhookMessageParams args, RequestOptions options = null)
public async Task<Message> CreateWebhookMessageAsync(ulong webhookId, CreateWebhookMessageParams args, RequestOptions options = null, ulong? threadId = null)
{
if (AuthTokenType != TokenType.Webhook)
throw new InvalidOperationException($"This operation may only be called with a {nameof(TokenType.Webhook)} token.");
@@ -816,12 +820,12 @@ namespace Discord.API
options = RequestOptions.CreateOrClone(options);

var ids = new BucketIds(webhookId: webhookId);
return await SendJsonAsync<Message>("POST", () => $"webhooks/{webhookId}/{AuthToken}?wait=true", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false);
return await SendJsonAsync<Message>("POST", () => $"webhooks/{webhookId}/{AuthToken}?{WebhookQuery(true, threadId)}", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false);
}

/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
/// <exception cref="InvalidOperationException">This operation may only be called with a <see cref="TokenType.Webhook"/> token.</exception>
public async Task ModifyWebhookMessageAsync(ulong webhookId, ulong messageId, ModifyWebhookMessageParams args, RequestOptions options = null)
public async Task ModifyWebhookMessageAsync(ulong webhookId, ulong messageId, ModifyWebhookMessageParams args, RequestOptions options = null, ulong? threadId = null)
{
if (AuthTokenType != TokenType.Webhook)
throw new InvalidOperationException($"This operation may only be called with a {nameof(TokenType.Webhook)} token.");
@@ -837,11 +841,11 @@ namespace Discord.API
options = RequestOptions.CreateOrClone(options);

var ids = new BucketIds(webhookId: webhookId);
await SendJsonAsync<Message>("PATCH", () => $"webhooks/{webhookId}/{AuthToken}/messages/{messageId}", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false);
await SendJsonAsync<Message>("PATCH", () => $"webhooks/{webhookId}/{AuthToken}/messages/{messageId}${WebhookQuery(false, threadId)}", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false);
}

/// <exception cref="InvalidOperationException">This operation may only be called with a <see cref="TokenType.Webhook"/> token.</exception>
public async Task DeleteWebhookMessageAsync(ulong webhookId, ulong messageId, RequestOptions options = null)
public async Task DeleteWebhookMessageAsync(ulong webhookId, ulong messageId, RequestOptions options = null, ulong? threadId = null)
{
if (AuthTokenType != TokenType.Webhook)
throw new InvalidOperationException($"This operation may only be called with a {nameof(TokenType.Webhook)} token.");
@@ -852,7 +856,7 @@ namespace Discord.API
options = RequestOptions.CreateOrClone(options);

var ids = new BucketIds(webhookId: webhookId);
await SendAsync("DELETE", () => $"webhooks/{webhookId}/{AuthToken}/messages/{messageId}", ids, options: options).ConfigureAwait(false);
await SendAsync("DELETE", () => $"webhooks/{webhookId}/{AuthToken}/messages/{messageId}?{WebhookQuery(false, threadId)}", ids, options: options).ConfigureAwait(false);
}

/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
@@ -873,7 +877,7 @@ namespace Discord.API

/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
/// <exception cref="InvalidOperationException">This operation may only be called with a <see cref="TokenType.Webhook"/> token.</exception>
public async Task<Message> UploadWebhookFileAsync(ulong webhookId, UploadWebhookFileParams args, RequestOptions options = null)
public async Task<Message> UploadWebhookFileAsync(ulong webhookId, UploadWebhookFileParams args, RequestOptions options = null, ulong? threadId = null)
{
if (AuthTokenType != TokenType.Webhook)
throw new InvalidOperationException($"This operation may only be called with a {nameof(TokenType.Webhook)} token.");
@@ -893,7 +897,7 @@ namespace Discord.API
}

var ids = new BucketIds(webhookId: webhookId);
return await SendMultipartAsync<Message>("POST", () => $"webhooks/{webhookId}/{AuthToken}?wait=true", args.ToDictionary(), ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false);
return await SendMultipartAsync<Message>("POST", () => $"webhooks/{webhookId}/{AuthToken}?{WebhookQuery(true, threadId)}", args.ToDictionary(), ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false);
}
public async Task DeleteMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null)
{
@@ -1380,7 +1384,7 @@ namespace Discord.API
if ((!args.Embeds.IsSpecified || args.Embeds.Value == null || args.Embeds.Value.Length == 0) && !args.File.IsSpecified)
Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content));

if(args.Content.IsSpecified && args.Content.Value?.Length > DiscordConfig.MaxMessageSize)
if (args.Content.IsSpecified && args.Content.Value?.Length > DiscordConfig.MaxMessageSize)
throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(args.Content));

options = RequestOptions.CreateOrClone(options);
@@ -1400,7 +1404,7 @@ namespace Discord.API
throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(args.Content));

options = RequestOptions.CreateOrClone(options);
var ids = new BucketIds();
return await SendMultipartAsync<Message>("POST", () => $"webhooks/{CurrentApplicationId}/{token}?wait=true", args.ToDictionary(), ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false);
}
@@ -1729,8 +1733,10 @@ namespace Discord.API
if (args.TargetType.IsSpecified)
{
Preconditions.NotEqual((int)args.TargetType.Value, (int)TargetUserType.Undefined, nameof(args.TargetType));
if (args.TargetType.Value == TargetUserType.Stream) Preconditions.GreaterThan(args.TargetUserId, 0, nameof(args.TargetUserId));
if (args.TargetType.Value == TargetUserType.EmbeddedApplication) Preconditions.GreaterThan(args.TargetApplicationId, 0, nameof(args.TargetUserId));
if (args.TargetType.Value == TargetUserType.Stream)
Preconditions.GreaterThan(args.TargetUserId, 0, nameof(args.TargetUserId));
if (args.TargetType.Value == TargetUserType.EmbeddedApplication)
Preconditions.GreaterThan(args.TargetApplicationId, 0, nameof(args.TargetUserId));
}
options = RequestOptions.CreateOrClone(options);

@@ -2414,6 +2420,18 @@ namespace Discord.API

return (expr as MemberExpression).Member.Name;
}

private static string WebhookQuery(bool wait = false, ulong? threadId = null)
{
List<string> querys = new List<string>() { };
if (wait)
querys.Add("wait=true");
if (threadId.HasValue)
querys.Add($"thread_id={threadId}");

return $"{string.Join("&", querys)}";
}

#endregion
}
}

+ 5
- 3
src/Discord.Net.Rest/Entities/Channels/RestStageChannel.cs View File

@@ -12,7 +12,11 @@ namespace Discord.Rest
public class RestStageChannel : RestVoiceChannel, IStageChannel
{
/// <inheritdoc/>
public string Topic { get; private set; }
/// <remarks>
/// This field is always false for stage channels.
/// </remarks>
public override bool IsTextInVoice
=> false;

/// <inheritdoc/>
public StagePrivacyLevel? PrivacyLevel { get; private set; }
@@ -37,13 +41,11 @@ namespace Discord.Rest
IsLive = isLive;
if(isLive)
{
Topic = model.Topic;
PrivacyLevel = model.PrivacyLevel;
IsDiscoverableDisabled = model.DiscoverableDisabled;
}
else
{
Topic = null;
PrivacyLevel = null;
IsDiscoverableDisabled = null;
}


+ 49
- 49
src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs View File

@@ -86,25 +86,25 @@ namespace Discord.Rest
=> ChannelHelper.GetUsersAsync(this, Guild, Discord, null, null, options);

/// <inheritdoc />
public Task<RestMessage> GetMessageAsync(ulong id, RequestOptions options = null)
public virtual Task<RestMessage> GetMessageAsync(ulong id, RequestOptions options = null)
=> ChannelHelper.GetMessageAsync(this, Discord, id, options);
/// <inheritdoc />
public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null)
public virtual IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null)
=> ChannelHelper.GetMessagesAsync(this, Discord, null, Direction.Before, limit, options);
/// <inheritdoc />
public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null)
public virtual IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null)
=> ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit, options);
/// <inheritdoc />
public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null)
public virtual IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null)
=> ChannelHelper.GetMessagesAsync(this, Discord, fromMessage.Id, dir, limit, options);
/// <inheritdoc />
public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null)
public virtual Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null)
=> ChannelHelper.GetPinnedMessagesAsync(this, Discord, options);

/// <inheritdoc />
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
/// <exception cref="ArgumentException">The only valid <see cref="MessageFlags"/> are <see cref="MessageFlags.SuppressEmbeds"/> and <see cref="MessageFlags.None"/>.</exception>
public Task<RestUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null,
public virtual Task<RestUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null,
RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null,
MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None)
=> ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, allowedMentions, messageReference,
@@ -136,7 +136,7 @@ namespace Discord.Rest
/// <exception cref="IOException">An I/O error occurred while opening the file.</exception>
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
/// <exception cref="ArgumentException">The only valid <see cref="MessageFlags"/> are <see cref="MessageFlags.SuppressEmbeds"/> and <see cref="MessageFlags.None"/>.</exception>
public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null,
public virtual Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null,
RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null,
MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null,
Embed[] embeds = null, MessageFlags flags = MessageFlags.None)
@@ -146,7 +146,7 @@ namespace Discord.Rest
/// <inheritdoc />
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
/// <exception cref="ArgumentException">The only valid <see cref="MessageFlags"/> are <see cref="MessageFlags.SuppressEmbeds"/> and <see cref="MessageFlags.None"/>.</exception>
public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false,
public virtual Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false,
Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null,
MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null,
Embed[] embeds = null, MessageFlags flags = MessageFlags.None)
@@ -156,7 +156,7 @@ namespace Discord.Rest
/// <inheritdoc />
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
/// <exception cref="ArgumentException">The only valid <see cref="MessageFlags"/> are <see cref="MessageFlags.SuppressEmbeds"/> and <see cref="MessageFlags.None"/>.</exception>
public Task<RestUserMessage> SendFileAsync(FileAttachment attachment, string text, bool isTTS = false,
public virtual Task<RestUserMessage> SendFileAsync(FileAttachment attachment, string text, bool isTTS = false,
Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null,
MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null,
Embed[] embeds = null, MessageFlags flags = MessageFlags.None)
@@ -166,35 +166,35 @@ namespace Discord.Rest
/// <inheritdoc />
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
/// <exception cref="ArgumentException">The only valid <see cref="MessageFlags"/> are <see cref="MessageFlags.SuppressEmbeds"/> and <see cref="MessageFlags.None"/>.</exception>
public Task<RestUserMessage> SendFilesAsync(IEnumerable<FileAttachment> attachments, string text, bool isTTS = false,
public virtual Task<RestUserMessage> SendFilesAsync(IEnumerable<FileAttachment> attachments, string text, bool isTTS = false,
Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null,
MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null,
Embed[] embeds = null, MessageFlags flags = MessageFlags.None)
=> ChannelHelper.SendFilesAsync(this, Discord, attachments, text, isTTS, embed, allowedMentions, messageReference, components, stickers, options, embeds, flags);

/// <inheritdoc />
public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null)
public virtual Task DeleteMessageAsync(ulong messageId, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options);
/// <inheritdoc />
public Task DeleteMessageAsync(IMessage message, RequestOptions options = null)
public virtual Task DeleteMessageAsync(IMessage message, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options);

/// <inheritdoc />
public Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null)
public virtual Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null)
=> ChannelHelper.DeleteMessagesAsync(this, Discord, messages.Select(x => x.Id), options);
/// <inheritdoc />
public Task DeleteMessagesAsync(IEnumerable<ulong> messageIds, RequestOptions options = null)
public virtual Task DeleteMessagesAsync(IEnumerable<ulong> messageIds, RequestOptions options = null)
=> ChannelHelper.DeleteMessagesAsync(this, Discord, messageIds, options);

/// <inheritdoc />
public async Task<IUserMessage> ModifyMessageAsync(ulong messageId, Action<MessageProperties> func, RequestOptions options = null)
public virtual async Task<IUserMessage> ModifyMessageAsync(ulong messageId, Action<MessageProperties> func, RequestOptions options = null)
=> await ChannelHelper.ModifyMessageAsync(this, messageId, func, Discord, options).ConfigureAwait(false);

/// <inheritdoc />
public Task TriggerTypingAsync(RequestOptions options = null)
public virtual Task TriggerTypingAsync(RequestOptions options = null)
=> ChannelHelper.TriggerTypingAsync(this, Discord, options);
/// <inheritdoc />
public IDisposable EnterTypingState(RequestOptions options = null)
public virtual IDisposable EnterTypingState(RequestOptions options = null)
=> ChannelHelper.EnterTypingState(this, Discord, options);

/// <summary>
@@ -231,38 +231,6 @@ namespace Discord.Rest
public virtual Task<IReadOnlyCollection<RestWebhook>> GetWebhooksAsync(RequestOptions options = null)
=> ChannelHelper.GetWebhooksAsync(this, Discord, options);

/// <summary>
/// Gets the parent (category) channel of this channel.
/// </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 the category channel
/// representing the parent of this channel; <c>null</c> if none is set.
/// </returns>
public virtual Task<ICategoryChannel> GetCategoryAsync(RequestOptions options = null)
=> ChannelHelper.GetCategoryAsync(this, Discord, options);
/// <inheritdoc />
public Task SyncPermissionsAsync(RequestOptions options = null)
=> ChannelHelper.SyncPermissionsAsync(this, Discord, options);
#endregion

#region Invites
/// <inheritdoc />
public virtual async Task<IInviteMetadata> CreateInviteAsync(int? maxAge = 86400, int? maxUses = null, bool isTemporary = false, bool isUnique = false, RequestOptions options = null)
=> await ChannelHelper.CreateInviteAsync(this, Discord, maxAge, maxUses, isTemporary, isUnique, options).ConfigureAwait(false);
public virtual async Task<IInviteMetadata> CreateInviteToApplicationAsync(ulong applicationId, int? maxAge = 86400, int? maxUses = default(int?), bool isTemporary = false, bool isUnique = false, RequestOptions options = null)
=> await ChannelHelper.CreateInviteToApplicationAsync(this, Discord, maxAge, maxUses, isTemporary, isUnique, applicationId, options);
/// <inheritdoc />
public virtual async Task<IInviteMetadata> CreateInviteToApplicationAsync(DefaultApplications application, int? maxAge = 86400, int? maxUses = default(int?), bool isTemporary = false, bool isUnique = false, RequestOptions options = null)
=> await ChannelHelper.CreateInviteToApplicationAsync(this, Discord, maxAge, maxUses, isTemporary, isUnique, (ulong)application, options);
public virtual Task<IInviteMetadata> CreateInviteToStreamAsync(IUser user, int? maxAge, int? maxUses = default(int?), bool isTemporary = false, bool isUnique = false, RequestOptions options = null)
=> throw new NotImplementedException();
/// <inheritdoc />
public virtual async Task<IReadOnlyCollection<IInviteMetadata>> GetInvitesAsync(RequestOptions options = null)
=> await ChannelHelper.GetInvitesAsync(this, Discord, options).ConfigureAwait(false);

private string DebuggerDisplay => $"{Name} ({Id}, Text)";

/// <summary>
/// Creates a thread within this <see cref="ITextChannel"/>.
/// </summary>
@@ -299,6 +267,38 @@ namespace Discord.Rest
var model = await ThreadHelper.CreateThreadAsync(Discord, this, name, type, autoArchiveDuration, message, invitable, slowmode, options);
return RestThreadChannel.Create(Discord, Guild, model);
}

/// <summary>
/// Gets the parent (category) channel of this channel.
/// </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 the category channel
/// representing the parent of this channel; <c>null</c> if none is set.
/// </returns>
public virtual Task<ICategoryChannel> GetCategoryAsync(RequestOptions options = null)
=> ChannelHelper.GetCategoryAsync(this, Discord, options);
/// <inheritdoc />
public Task SyncPermissionsAsync(RequestOptions options = null)
=> ChannelHelper.SyncPermissionsAsync(this, Discord, options);
#endregion

#region Invites
/// <inheritdoc />
public virtual async Task<IInviteMetadata> CreateInviteAsync(int? maxAge = 86400, int? maxUses = null, bool isTemporary = false, bool isUnique = false, RequestOptions options = null)
=> await ChannelHelper.CreateInviteAsync(this, Discord, maxAge, maxUses, isTemporary, isUnique, options).ConfigureAwait(false);
public virtual async Task<IInviteMetadata> CreateInviteToApplicationAsync(ulong applicationId, int? maxAge = 86400, int? maxUses = default(int?), bool isTemporary = false, bool isUnique = false, RequestOptions options = null)
=> await ChannelHelper.CreateInviteToApplicationAsync(this, Discord, maxAge, maxUses, isTemporary, isUnique, applicationId, options);
/// <inheritdoc />
public virtual async Task<IInviteMetadata> CreateInviteToApplicationAsync(DefaultApplications application, int? maxAge = 86400, int? maxUses = default(int?), bool isTemporary = false, bool isUnique = false, RequestOptions options = null)
=> await ChannelHelper.CreateInviteToApplicationAsync(this, Discord, maxAge, maxUses, isTemporary, isUnique, (ulong)application, options);
public virtual Task<IInviteMetadata> CreateInviteToStreamAsync(IUser user, int? maxAge, int? maxUses = default(int?), bool isTemporary = false, bool isUnique = false, RequestOptions options = null)
=> throw new NotImplementedException();
/// <inheritdoc />
public virtual async Task<IReadOnlyCollection<IInviteMetadata>> GetInvitesAsync(RequestOptions options = null)
=> await ChannelHelper.GetInvitesAsync(this, Discord, options).ConfigureAwait(false);

private string DebuggerDisplay => $"{Name} ({Id}, Text)";
#endregion

#region ITextChannel


+ 181
- 37
src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs View File

@@ -2,6 +2,7 @@ using Discord.Audio;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Model = Discord.API.Channel;
@@ -12,21 +13,21 @@ namespace Discord.Rest
/// Represents a REST-based voice channel in a guild.
/// </summary>
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public class RestVoiceChannel : RestGuildChannel, IVoiceChannel, IRestAudioChannel
public class RestVoiceChannel : RestTextChannel, IVoiceChannel, IRestAudioChannel
{
#region RestVoiceChannel
/// <summary>
/// Gets whether or not the guild has Text-In-Voice enabled and the voice channel is a TiV channel.
/// </summary>
public virtual bool IsTextInVoice
=> Guild.Features.HasTextInVoice;
/// <inheritdoc />
public int Bitrate { get; private set; }
/// <inheritdoc />
public int? UserLimit { get; private set; }
/// <inheritdoc />
public ulong? CategoryId { get; private set; }
/// <inheritdoc/>
public string RTCRegion { get; private set; }

/// <inheritdoc />
public string Mention => MentionUtils.MentionChannel(Id);

internal RestVoiceChannel(BaseDiscordClient discord, IGuild guild, ulong id)
: base(discord, guild, id)
{
@@ -41,7 +42,6 @@ namespace Discord.Rest
internal override void Update(Model model)
{
base.Update(model);
CategoryId = model.CategoryId;

if(model.Bitrate.IsSpecified)
Bitrate = model.Bitrate.Value;
@@ -59,41 +59,185 @@ namespace Discord.Rest
Update(model);
}

/// <summary>
/// Gets the parent (category) channel of this channel.
/// </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 the category channel
/// representing the parent of this channel; <c>null</c> if none is set.
/// </returns>
public Task<ICategoryChannel> GetCategoryAsync(RequestOptions options = null)
=> ChannelHelper.GetCategoryAsync(this, Discord, options);
/// <inheritdoc />
public Task SyncPermissionsAsync(RequestOptions options = null)
=> ChannelHelper.SyncPermissionsAsync(this, Discord, options);
#endregion
/// <inheritdoc/>
/// <exception cref="InvalidOperationException">Cannot modify text channel properties of a voice channel.</exception>
public override Task ModifyAsync(Action<TextChannelProperties> func, RequestOptions options = null)
=> throw new InvalidOperationException("Cannot modify text channel properties of a voice channel");

#region Invites
/// <inheritdoc />
public async Task<IInviteMetadata> CreateInviteAsync(int? maxAge = 86400, int? maxUses = null, bool isTemporary = false, bool isUnique = false, RequestOptions options = null)
=> await ChannelHelper.CreateInviteAsync(this, Discord, maxAge, maxUses, isTemporary, isUnique, options).ConfigureAwait(false);
/// <inheritdoc />
public async Task<IInviteMetadata> CreateInviteToApplicationAsync(ulong applicationId, int? maxAge, int? maxUses = default(int?), bool isTemporary = false, bool isUnique = false, RequestOptions options = null)
=> await ChannelHelper.CreateInviteToApplicationAsync(this, Discord, maxAge, maxUses, isTemporary, isUnique, applicationId, options).ConfigureAwait(false);
/// <inheritdoc />
public virtual async Task<IInviteMetadata> CreateInviteToApplicationAsync(DefaultApplications application, int? maxAge = 86400, int? maxUses = default(int?), bool isTemporary = false, bool isUnique = false, RequestOptions options = null)
=> await ChannelHelper.CreateInviteToApplicationAsync(this, Discord, maxAge, maxUses, isTemporary, isUnique, (ulong)application, options);
/// <inheritdoc />
public async Task<IInviteMetadata> CreateInviteToStreamAsync(IUser user, int? maxAge, int? maxUses = default(int?), bool isTemporary = false, bool isUnique = false, RequestOptions options = null)
=> await ChannelHelper.CreateInviteToStreamAsync(this, Discord, maxAge, maxUses, isTemporary, isUnique, user, options).ConfigureAwait(false);
/// <inheritdoc />
public async Task<IReadOnlyCollection<IInviteMetadata>> GetInvitesAsync(RequestOptions options = null)
=> await ChannelHelper.GetInvitesAsync(this, Discord, options).ConfigureAwait(false);
/// <inheritdoc/>
/// <exception cref="InvalidOperationException">Cannot create a thread within a voice channel.</exception>
public override Task<RestThreadChannel> CreateThreadAsync(string name, ThreadType type = ThreadType.PublicThread, ThreadArchiveDuration autoArchiveDuration = ThreadArchiveDuration.OneDay, IMessage message = null, bool? invitable = null, int? slowmode = null, RequestOptions options = null)
=> throw new InvalidOperationException("Cannot create a thread within a voice channel");

#endregion

private string DebuggerDisplay => $"{Name} ({Id}, Voice)";

#region TextOverrides

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task<RestMessage> GetMessageAsync(ulong id, RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.GetMessageAsync(id, options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task DeleteMessageAsync(IMessage message, RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.DeleteMessageAsync(message, options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task DeleteMessageAsync(ulong messageId, RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.DeleteMessageAsync(messageId, options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.DeleteMessagesAsync(messages, options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task DeleteMessagesAsync(IEnumerable<ulong> messageIds, RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.DeleteMessagesAsync(messageIds, options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override IDisposable EnterTypingState(RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.EnterTypingState(options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = 100, RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.GetMessagesAsync(fromMessage, dir, limit, options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(int limit = 100, RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.GetMessagesAsync(limit, options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = 100, RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.GetMessagesAsync(fromMessageId, dir, limit, options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.GetPinnedMessagesAsync(options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task<RestWebhook> GetWebhookAsync(ulong id, RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.GetWebhookAsync(id, options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task<IReadOnlyCollection<RestWebhook>> GetWebhooksAsync(RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.GetWebhooksAsync(options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task<RestWebhook> CreateWebhookAsync(string name, Stream avatar = null, RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.CreateWebhookAsync(name, avatar, options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task<IUserMessage> ModifyMessageAsync(ulong messageId, Action<MessageProperties> func, RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.ModifyMessageAsync(messageId, func, options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task<RestUserMessage> SendFileAsync(FileAttachment attachment, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.SendFileAsync(attachment, text, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds, flags);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, components, stickers, embeds, flags);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, components, stickers, embeds, flags);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task<RestUserMessage> SendFilesAsync(IEnumerable<FileAttachment> attachments, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.SendFilesAsync(attachments, text, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds, flags);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task<RestUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.SendMessageAsync(text, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds, flags);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task TriggerTypingAsync(RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.TriggerTypingAsync(options);
}

#endregion


#region IAudioChannel
/// <inheritdoc />
/// <exception cref="NotSupportedException">Connecting to a REST-based channel is not supported.</exception>


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

@@ -1161,7 +1161,6 @@ namespace Discord.Rest
/// in order to use this property.
/// </remarks>
/// </param>
/// <param name="speakers">A collection of speakers for the event.</param>
/// <param name="location">The location of the event; links are supported</param>
/// <param name="coverImage">The optional banner image for the event.</param>
/// <param name="options">The options to be used when sending the request.</param>


+ 1
- 2
src/Discord.Net.Rest/Entities/Interactions/RestApplicationCommand.cs View File

@@ -65,8 +65,7 @@ namespace Discord.Rest
: ImmutableArray.Create<RestApplicationCommandOption>();

IsEnabledInDm = model.DmPermission.GetValueOrDefault(true).GetValueOrDefault(true);
DefaultMemberPermissions = model.DefaultMemberPermission.GetValueOrDefault(null).HasValue
? new GuildPermissions((ulong)model.DefaultMemberPermission.GetValueOrDefault(null).Value) : GuildPermissions.None;
DefaultMemberPermissions = new GuildPermissions((ulong)model.DefaultMemberPermission.GetValueOrDefault(0).GetValueOrDefault(0));
}

/// <inheritdoc/>


+ 0
- 1
src/Discord.Net.Rest/Entities/Interactions/RestInteraction.cs View File

@@ -333,7 +333,6 @@ namespace Discord.Rest
=> await FollowupWithFilesAsync(attachments, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false);
/// <inheritdoc/>
Task IDiscordInteraction.RespondWithFilesAsync(IEnumerable<FileAttachment> attachments, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) => throw new NotSupportedException("REST-Based interactions don't support files.");
/// <inheritdoc/>
#if NETCOREAPP3_0_OR_GREATER != true
/// <inheritdoc/>
Task IDiscordInteraction.RespondWithFileAsync(Stream fileStream, string fileName, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) => throw new NotSupportedException("REST-Based interactions don't support files.");


+ 2
- 1
src/Discord.Net.Rest/Entities/Messages/RestMessage.cs View File

@@ -144,7 +144,8 @@ namespace Discord.Rest
{
GuildId = model.Reference.Value.GuildId,
InternalChannelId = model.Reference.Value.ChannelId,
MessageId = model.Reference.Value.MessageId
MessageId = model.Reference.Value.MessageId,
FailIfNotExists = model.Reference.Value.FailIfNotExists
};
}



+ 1
- 1
src/Discord.Net.Rest/Entities/Roles/RestRole.cs View File

@@ -25,7 +25,7 @@ namespace Discord.Rest
public string Name { get; private set; }
/// <inheritdoc />
public string Icon { get; private set; }
/// <inheritdoc>/>
/// <inheritdoc />
public Emoji Emoji { get; private set; }
/// <inheritdoc />
public GuildPermissions Permissions { get; private set; }


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

@@ -87,6 +87,7 @@ namespace Discord.Rest
ChannelId = entity.InternalChannelId,
GuildId = entity.GuildId,
MessageId = entity.MessageId,
FailIfNotExists = entity.FailIfNotExists
};
}
public static IEnumerable<string> EnumerateMentionTypes(this AllowedMentionTypes mentionTypes)


+ 1
- 1
src/Discord.Net.Rest/Net/ED25519/CryptoBytes.cs View File

@@ -243,7 +243,7 @@ namespace Discord.Net.ED25519
/// <summary>
/// // Decode a base58-encoded string into byte array
/// </summary>
/// <param name="strBase58">Base58 data string</param>
/// <param name="input">Base58 data string</param>
/// <returns>Byte array</returns>
public static byte[] Base58Decode(string input)
{


+ 3
- 8
src/Discord.Net.Rest/Net/Queue/RequestQueue.cs View File

@@ -60,14 +60,9 @@ namespace Discord.Net.Queue
_clearToken?.Cancel();
_clearToken?.Dispose();
_clearToken = new CancellationTokenSource();
if (_parentToken != null)
{
_requestCancelTokenSource?.Dispose();
_requestCancelTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_clearToken.Token, _parentToken);
_requestCancelToken = _requestCancelTokenSource.Token;
}
else
_requestCancelToken = _clearToken.Token;
_requestCancelTokenSource?.Dispose();
_requestCancelTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_clearToken.Token, _parentToken);
_requestCancelToken = _requestCancelTokenSource.Token;
}
finally { _tokenLock.Release(); }
}


+ 2
- 0
src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj View File

@@ -8,6 +8,8 @@
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net6.0;net5.0;net461;netstandard2.0;netstandard2.1</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">net6.0;net5.0;netstandard2.0;netstandard2.1</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<WarningLevel>5</WarningLevel>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" />


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

@@ -2331,7 +2331,9 @@ namespace Discord.WebSocket

SocketUser user = data.User.IsSpecified
? State.GetOrAddUser(data.User.Value.Id, (_) => SocketGlobalUser.Create(this, State, data.User.Value))
: guild?.AddOrUpdateUser(data.Member.Value); // null if the bot scope isn't set, so the guild cannot be retrieved.
: guild != null
? guild.AddOrUpdateUser(data.Member.Value) // null if the bot scope isn't set, so the guild cannot be retrieved.
: State.GetOrAddUser(data.Member.Value.User.Id, (_) => SocketGlobalUser.Create(this, State, data.Member.Value.User));

SocketChannel channel = null;
if(data.ChannelId.IsSpecified)


+ 2
- 0
src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs View File

@@ -222,6 +222,8 @@ namespace Discord.WebSocket

#region IChannel
/// <inheritdoc />
string IChannel.Name => Name;
/// <inheritdoc />
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options)
=> ImmutableArray.Create<IReadOnlyCollection<IUser>>(Users).ToAsyncEnumerable(); //Overridden in Text/Voice
/// <inheritdoc />


+ 5
- 4
src/Discord.Net.WebSocket/Entities/Channels/SocketStageChannel.cs View File

@@ -15,7 +15,11 @@ namespace Discord.WebSocket
public class SocketStageChannel : SocketVoiceChannel, IStageChannel
{
/// <inheritdoc/>
public string Topic { get; private set; }
/// <remarks>
/// This field is always false for stage channels.
/// </remarks>
public override bool IsTextInVoice
=> false;

/// <inheritdoc/>
public StagePrivacyLevel? PrivacyLevel { get; private set; }
@@ -49,19 +53,16 @@ namespace Discord.WebSocket
entity.Update(state, model);
return entity;
}

internal void Update(StageInstance model, bool isLive = false)
{
IsLive = isLive;
if (isLive)
{
Topic = model.Topic;
PrivacyLevel = model.PrivacyLevel;
IsDiscoverableDisabled = model.DiscoverableDisabled;
}
else
{
Topic = null;
PrivacyLevel = null;
IsDiscoverableDisabled = null;
}


+ 21
- 21
src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs View File

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

#region Messages
/// <inheritdoc />
public SocketMessage GetCachedMessage(ulong id)
public virtual SocketMessage GetCachedMessage(ulong id)
=> _messages?.Get(id);
/// <summary>
/// Gets a message from this message channel.
@@ -143,7 +143,7 @@ namespace Discord.WebSocket
/// A task that represents an asynchronous get operation for retrieving the message. The task result contains
/// the retrieved message; <c>null</c> if no message is found with the specified identifier.
/// </returns>
public async Task<IMessage> GetMessageAsync(ulong id, RequestOptions options = null)
public virtual async Task<IMessage> GetMessageAsync(ulong id, RequestOptions options = null)
{
IMessage msg = _messages?.Get(id);
if (msg == null)
@@ -163,7 +163,7 @@ namespace Discord.WebSocket
/// <returns>
/// Paged collection of messages.
/// </returns>
public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null)
public virtual IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null)
=> SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, null, Direction.Before, limit, CacheMode.AllowDownload, options);
/// <summary>
/// Gets a collection of messages in this channel.
@@ -179,7 +179,7 @@ namespace Discord.WebSocket
/// <returns>
/// Paged collection of messages.
/// </returns>
public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null)
public virtual IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null)
=> SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit, CacheMode.AllowDownload, options);
/// <summary>
/// Gets a collection of messages in this channel.
@@ -195,25 +195,25 @@ namespace Discord.WebSocket
/// <returns>
/// Paged collection of messages.
/// </returns>
public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null)
public virtual IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null)
=> SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, CacheMode.AllowDownload, options);
/// <inheritdoc />
public IReadOnlyCollection<SocketMessage> GetCachedMessages(int limit = DiscordConfig.MaxMessagesPerBatch)
public virtual IReadOnlyCollection<SocketMessage> GetCachedMessages(int limit = DiscordConfig.MaxMessagesPerBatch)
=> SocketChannelHelper.GetCachedMessages(this, Discord, _messages, null, Direction.Before, limit);
/// <inheritdoc />
public IReadOnlyCollection<SocketMessage> GetCachedMessages(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch)
public virtual IReadOnlyCollection<SocketMessage> GetCachedMessages(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch)
=> SocketChannelHelper.GetCachedMessages(this, Discord, _messages, fromMessageId, dir, limit);
/// <inheritdoc />
public IReadOnlyCollection<SocketMessage> GetCachedMessages(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch)
public virtual IReadOnlyCollection<SocketMessage> GetCachedMessages(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch)
=> SocketChannelHelper.GetCachedMessages(this, Discord, _messages, fromMessage.Id, dir, limit);
/// <inheritdoc />
public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null)
public virtual Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null)
=> ChannelHelper.GetPinnedMessagesAsync(this, Discord, options);

/// <inheritdoc />
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
/// <exception cref="ArgumentException">The only valid <see cref="MessageFlags"/> are <see cref="MessageFlags.SuppressEmbeds"/> and <see cref="MessageFlags.None"/>.</exception>
public Task<RestUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null,
public virtual Task<RestUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null,
RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null,
MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None)
=> ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, allowedMentions, messageReference,
@@ -221,7 +221,7 @@ namespace Discord.WebSocket

/// <inheritdoc />
/// <exception cref="ArgumentException">The only valid <see cref="MessageFlags"/> are <see cref="MessageFlags.SuppressEmbeds"/> and <see cref="MessageFlags.None"/>.</exception>
public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null,
public virtual Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null,
RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null,
MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null,
Embed[] embeds = null, MessageFlags flags = MessageFlags.None)
@@ -230,7 +230,7 @@ namespace Discord.WebSocket
/// <inheritdoc />
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
/// <exception cref="ArgumentException">The only valid <see cref="MessageFlags"/> are <see cref="MessageFlags.SuppressEmbeds"/> and <see cref="MessageFlags.None"/>.</exception>
public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false,
public virtual Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false,
Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null,
MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null,
Embed[] embeds = null, MessageFlags flags = MessageFlags.None)
@@ -239,7 +239,7 @@ namespace Discord.WebSocket
/// <inheritdoc />
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
/// <exception cref="ArgumentException">The only valid <see cref="MessageFlags"/> are <see cref="MessageFlags.SuppressEmbeds"/> and <see cref="MessageFlags.None"/>.</exception>
public Task<RestUserMessage> SendFileAsync(FileAttachment attachment, string text, bool isTTS = false,
public virtual Task<RestUserMessage> SendFileAsync(FileAttachment attachment, string text, bool isTTS = false,
Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null,
MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null,
Embed[] embeds = null, MessageFlags flags = MessageFlags.None)
@@ -248,7 +248,7 @@ namespace Discord.WebSocket
/// <inheritdoc />
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
/// <exception cref="ArgumentException">The only valid <see cref="MessageFlags"/> are <see cref="MessageFlags.SuppressEmbeds"/> and <see cref="MessageFlags.None"/>.</exception>
public Task<RestUserMessage> SendFilesAsync(IEnumerable<FileAttachment> attachments, string text, bool isTTS = false,
public virtual Task<RestUserMessage> SendFilesAsync(IEnumerable<FileAttachment> attachments, string text, bool isTTS = false,
Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null,
MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null,
Embed[] embeds = null, MessageFlags flags = MessageFlags.None)
@@ -256,28 +256,28 @@ namespace Discord.WebSocket
messageReference, components, stickers, options, embeds, flags);

/// <inheritdoc />
public Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null)
public virtual Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null)
=> ChannelHelper.DeleteMessagesAsync(this, Discord, messages.Select(x => x.Id), options);
/// <inheritdoc />
public Task DeleteMessagesAsync(IEnumerable<ulong> messageIds, RequestOptions options = null)
public virtual Task DeleteMessagesAsync(IEnumerable<ulong> messageIds, RequestOptions options = null)
=> ChannelHelper.DeleteMessagesAsync(this, Discord, messageIds, options);

/// <inheritdoc />
public async Task<IUserMessage> ModifyMessageAsync(ulong messageId, Action<MessageProperties> func, RequestOptions options = null)
public virtual async Task<IUserMessage> ModifyMessageAsync(ulong messageId, Action<MessageProperties> func, RequestOptions options = null)
=> await ChannelHelper.ModifyMessageAsync(this, messageId, func, Discord, options).ConfigureAwait(false);

/// <inheritdoc />
public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null)
public virtual Task DeleteMessageAsync(ulong messageId, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options);
/// <inheritdoc />
public Task DeleteMessageAsync(IMessage message, RequestOptions options = null)
public virtual Task DeleteMessageAsync(IMessage message, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options);

/// <inheritdoc />
public Task TriggerTypingAsync(RequestOptions options = null)
public virtual Task TriggerTypingAsync(RequestOptions options = null)
=> ChannelHelper.TriggerTypingAsync(this, Discord, options);
/// <inheritdoc />
public IDisposable EnterTypingState(RequestOptions options = null)
public virtual IDisposable EnterTypingState(RequestOptions options = null)
=> ChannelHelper.EnterTypingState(this, Discord, options);

internal void AddMessage(SocketMessage msg)


+ 215
- 40
src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs View File

@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Model = Discord.API.Channel;
@@ -14,33 +15,21 @@ namespace Discord.WebSocket
/// Represents a WebSocket-based voice channel in a guild.
/// </summary>
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public class SocketVoiceChannel : SocketGuildChannel, IVoiceChannel, ISocketAudioChannel
public class SocketVoiceChannel : SocketTextChannel, IVoiceChannel, ISocketAudioChannel
{
#region SocketVoiceChannel
/// <inheritdoc />
public int Bitrate { get; private set; }
/// <inheritdoc />
public int? UserLimit { get; private set; }
/// <inheritdoc/>
public string RTCRegion { get; private set; }

/// <inheritdoc />
public ulong? CategoryId { get; private set; }
/// <summary>
/// Gets the parent (category) channel of this channel.
/// Gets whether or not the guild has Text-In-Voice enabled and the voice channel is a TiV channel.
/// </summary>
/// <returns>
/// A category channel representing the parent of this channel; <c>null</c> if none is set.
/// </returns>
public ICategoryChannel Category
=> CategoryId.HasValue ? Guild.GetChannel(CategoryId.Value) as ICategoryChannel : null;
public virtual bool IsTextInVoice
=> Guild.Features.HasTextInVoice;

/// <inheritdoc />
public string Mention => MentionUtils.MentionChannel(Id);

public int Bitrate { get; private set; }
/// <inheritdoc />
public int? UserLimit { get; private set; }
/// <inheritdoc />
public Task SyncPermissionsAsync(RequestOptions options = null)
=> ChannelHelper.SyncPermissionsAsync(this, Discord, options);
public string RTCRegion { get; private set; }

/// <summary>
/// Gets a collection of users that are currently connected to this voice channel.
@@ -48,7 +37,7 @@ namespace Discord.WebSocket
/// <returns>
/// A read-only collection of users that are currently connected to this voice channel.
/// </returns>
public override IReadOnlyCollection<SocketGuildUser> Users
public IReadOnlyCollection<SocketGuildUser> ConnectedUsers
=> Guild.Users.Where(x => x.VoiceChannel?.Id == Id).ToImmutableArray();

internal SocketVoiceChannel(DiscordSocketClient discord, ulong id, SocketGuild guild)
@@ -65,7 +54,6 @@ namespace Discord.WebSocket
internal override void Update(ClientState state, Model model)
{
base.Update(state, model);
CategoryId = model.CategoryId;
Bitrate = model.Bitrate.Value;
UserLimit = model.UserLimit.Value != 0 ? model.UserLimit.Value : (int?)null;
RTCRegion = model.RTCRegion.GetValueOrDefault(null);
@@ -99,28 +87,215 @@ namespace Discord.WebSocket
return user;
return null;
}
#endregion

#region Invites
/// <inheritdoc />
public async Task<IInviteMetadata> CreateInviteAsync(int? maxAge = 86400, int? maxUses = null, bool isTemporary = false, bool isUnique = false, RequestOptions options = null)
=> await ChannelHelper.CreateInviteAsync(this, Discord, maxAge, maxUses, isTemporary, isUnique, options).ConfigureAwait(false);
/// <inheritdoc />
public async Task<IInviteMetadata> CreateInviteToApplicationAsync(ulong applicationId, int? maxAge, int? maxUses = default(int?), bool isTemporary = false, bool isUnique = false, RequestOptions options = null)
=> await ChannelHelper.CreateInviteToApplicationAsync(this, Discord, maxAge, maxUses, isTemporary, isUnique, applicationId, options).ConfigureAwait(false);
/// <inheritdoc />
public virtual async Task<IInviteMetadata> CreateInviteToApplicationAsync(DefaultApplications application, int? maxAge = 86400, int? maxUses = default(int?), bool isTemporary = false, bool isUnique = false, RequestOptions options = null)
=> await ChannelHelper.CreateInviteToApplicationAsync(this, Discord, maxAge, maxUses, isTemporary, isUnique, (ulong)application, options);
/// <inheritdoc />
public async Task<IInviteMetadata> CreateInviteToStreamAsync(IUser user, int? maxAge, int? maxUses = default(int?), bool isTemporary = false, bool isUnique = false, RequestOptions options = null)
=> await ChannelHelper.CreateInviteToStreamAsync(this, Discord, maxAge, maxUses, isTemporary, isUnique, user, options).ConfigureAwait(false);
/// <inheritdoc />
public async Task<IReadOnlyCollection<IInviteMetadata>> GetInvitesAsync(RequestOptions options = null)
=> await ChannelHelper.GetInvitesAsync(this, Discord, options).ConfigureAwait(false);
/// <inheritdoc/> <exception cref="InvalidOperationException">Cannot create threads in voice channels.</exception>
public override Task<SocketThreadChannel> CreateThreadAsync(string name, ThreadType type = ThreadType.PublicThread, ThreadArchiveDuration autoArchiveDuration = ThreadArchiveDuration.OneDay, IMessage message = null, bool? invitable = null, int? slowmode = null, RequestOptions options = null)
=> throw new InvalidOperationException("Voice channels cannot contain threads.");

/// <inheritdoc/> <exception cref="InvalidOperationException">Cannot modify text channel properties for voice channels.</exception>
public override Task ModifyAsync(Action<TextChannelProperties> func, RequestOptions options = null)
=> throw new InvalidOperationException("Cannot modify text channel properties for voice channels.");

#endregion

#region TextOverrides

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task<IMessage> GetMessageAsync(ulong id, RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.GetMessageAsync(id, options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task DeleteMessageAsync(IMessage message, RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.DeleteMessageAsync(message, options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task DeleteMessageAsync(ulong messageId, RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.DeleteMessageAsync(messageId, options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.DeleteMessagesAsync(messages, options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task DeleteMessagesAsync(IEnumerable<ulong> messageIds, RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.DeleteMessagesAsync(messageIds, options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override IDisposable EnterTypingState(RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.EnterTypingState(options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override SocketMessage GetCachedMessage(ulong id)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.GetCachedMessage(id);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override IReadOnlyCollection<SocketMessage> GetCachedMessages(IMessage fromMessage, Direction dir, int limit = 100)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.GetCachedMessages(fromMessage, dir, limit);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override IReadOnlyCollection<SocketMessage> GetCachedMessages(int limit = 100)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.GetCachedMessages(limit);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override IReadOnlyCollection<SocketMessage> GetCachedMessages(ulong fromMessageId, Direction dir, int limit = 100)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.GetCachedMessages(fromMessageId, dir, limit);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = 100, RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.GetMessagesAsync(fromMessage, dir, limit, options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = 100, RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.GetMessagesAsync(limit, options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = 100, RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.GetMessagesAsync(fromMessageId, dir, limit, options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.GetPinnedMessagesAsync(options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task<RestWebhook> GetWebhookAsync(ulong id, RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.GetWebhookAsync(id, options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task<IReadOnlyCollection<RestWebhook>> GetWebhooksAsync(RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.GetWebhooksAsync(options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task<RestWebhook> CreateWebhookAsync(string name, Stream avatar = null, RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.CreateWebhookAsync(name, avatar, options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task<IUserMessage> ModifyMessageAsync(ulong messageId, Action<MessageProperties> func, RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.ModifyMessageAsync(messageId, func, options);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task<RestUserMessage> SendFileAsync(FileAttachment attachment, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.SendFileAsync(attachment, text, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds, flags);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, components, stickers, embeds, flags);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference, components, stickers, embeds, flags);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task<RestUserMessage> SendFilesAsync(IEnumerable<FileAttachment> attachments, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.SendFilesAsync(attachments, text, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds, flags);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task<RestUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.SendMessageAsync(text, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds, flags);
}

/// <inheritdoc/> <exception cref="NotSupportedException">This function is only supported in Text-In-Voice channels.</exception>
public override Task TriggerTypingAsync(RequestOptions options = null)
{
if (!IsTextInVoice)
throw new NotSupportedException("This function is only supported in Text-In-Voice channels");
return base.TriggerTypingAsync(options);
}

#endregion

private string DebuggerDisplay => $"{Name} ({Id}, Voice)";
internal new SocketVoiceChannel Clone() => MemberwiseClone() as SocketVoiceChannel;
#endregion

#region IGuildChannel
/// <inheritdoc />


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

@@ -1291,7 +1291,6 @@ namespace Discord.WebSocket
/// in order to use this property.
/// </remarks>
/// </param>
/// <param name="speakers">A collection of speakers for the event.</param>
/// <param name="location">The location of the event; links are supported</param>
/// <param name="coverImage">The optional banner image for the event.</param>
/// <param name="options">The options to be used when sending the request.</param>


+ 85
- 0
src/Discord.Net.WebSocket/Entities/Interaction/Modals/SocketModal.cs View File

@@ -174,6 +174,91 @@ namespace Discord.WebSocket
HasResponded = true;
}

public async Task UpdateAsync(Action<MessageProperties> func, RequestOptions options = null)
{
var args = new MessageProperties();
func(args);

if (!IsValidToken)
throw new InvalidOperationException("Interaction token is no longer valid");

if (!InteractionHelper.CanSendResponse(this))
throw new TimeoutException($"Cannot respond to an interaction after {InteractionHelper.ResponseTimeLimit} seconds!");

if (args.AllowedMentions.IsSpecified)
{
var allowedMentions = args.AllowedMentions.Value;
Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions), "A max of 100 role Ids are allowed.");
Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions), "A max of 100 user Ids are allowed.");
}

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

bool hasText = args.Content.IsSpecified ? !string.IsNullOrEmpty(args.Content.Value) : false;
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.");

// 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)
{
var allowedMentions = args.AllowedMentions.Value;
if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Users)
&& allowedMentions.UserIds != null && allowedMentions.UserIds.Count > 0)
{
throw new ArgumentException("The Users flag is mutually exclusive with the list of User Ids.", nameof(args.AllowedMentions));
}

if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Roles)
&& allowedMentions.RoleIds != null && allowedMentions.RoleIds.Count > 0)
{
throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(args.AllowedMentions));
}
}

var response = new API.InteractionResponse
{
Type = InteractionResponseType.UpdateMessage,
Data = new API.InteractionCallbackData
{
Content = args.Content,
AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value?.ToModel() : Optional<API.AllowedMentions>.Unspecified,
Embeds = apiEmbeds?.ToArray() ?? Optional<API.Embed[]>.Unspecified,
Components = args.Components.IsSpecified
? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Array.Empty<API.ActionRowComponent>()
: Optional<API.ActionRowComponent[]>.Unspecified,
Flags = args.Flags.IsSpecified ? args.Flags.Value ?? Optional<MessageFlags>.Unspecified : Optional<MessageFlags>.Unspecified
}
};

lock (_lock)
{
if (HasResponded)
{
throw new InvalidOperationException("Cannot respond, update, or defer twice to the same interaction");
}
}

await InteractionHelper.SendInteractionResponseAsync(Discord, response, this, Channel, options).ConfigureAwait(false);
HasResponded = true;
}

/// <inheritdoc/>
public override async Task<RestFollowupMessage> FollowupAsync(
string text = null,


+ 1
- 2
src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketApplicationCommand.cs View File

@@ -94,8 +94,7 @@ namespace Discord.WebSocket
: ImmutableArray.Create<SocketApplicationCommandOption>();

IsEnabledInDm = model.DmPermission.GetValueOrDefault(true).GetValueOrDefault(true);
DefaultMemberPermissions = model.DefaultMemberPermission.GetValueOrDefault(null).HasValue
? new GuildPermissions((ulong)model.DefaultMemberPermission.GetValueOrDefault(null).Value) : GuildPermissions.None;
DefaultMemberPermissions = new GuildPermissions((ulong)model.DefaultMemberPermission.GetValueOrDefault(0).GetValueOrDefault(0));
}

/// <inheritdoc/>


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

@@ -182,7 +182,8 @@ namespace Discord.WebSocket
{
GuildId = model.Reference.Value.GuildId,
InternalChannelId = model.Reference.Value.ChannelId,
MessageId = model.Reference.Value.MessageId
MessageId = model.Reference.Value.MessageId,
FailIfNotExists = model.Reference.Value.FailIfNotExists
};
}



+ 2
- 0
src/Discord.Net.Webhook/Discord.Net.Webhook.csproj View File

@@ -6,6 +6,8 @@
<RootNamespace>Discord.Webhook</RootNamespace>
<Description>A core Discord.Net library containing the Webhook client and models.</Description>
<TargetFrameworks>net6.0;net5.0;netstandard2.0;netstandard2.1</TargetFrameworks>
<WarningLevel>5</WarningLevel>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" />


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

@@ -88,8 +88,8 @@ namespace Discord.Webhook
/// <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,
MessageComponent components = null, MessageFlags flags = MessageFlags.None)
=> WebhookClientHelper.SendMessageAsync(this, text, isTTS, embeds, username, avatarUrl, allowedMentions, options, components, flags);
MessageComponent components = null, MessageFlags flags = MessageFlags.None, ulong? threadId = null)
=> WebhookClientHelper.SendMessageAsync(this, text, isTTS, embeds, username, avatarUrl, allowedMentions, options, components, flags, threadId);

/// <summary>
/// Modifies a message posted using this webhook.
@@ -103,8 +103,8 @@ namespace Discord.Webhook
/// <returns>
/// A task that represents the asynchronous modification operation.
/// </returns>
public Task ModifyMessageAsync(ulong messageId, Action<WebhookMessageProperties> func, RequestOptions options = null)
=> WebhookClientHelper.ModifyMessageAsync(this, messageId, func, options);
public Task ModifyMessageAsync(ulong messageId, Action<WebhookMessageProperties> func, RequestOptions options = null, ulong? threadId = null)
=> WebhookClientHelper.ModifyMessageAsync(this, messageId, func, options, threadId);

/// <summary>
/// Deletes a message posted using this webhook.
@@ -117,43 +117,43 @@ namespace Discord.Webhook
/// <returns>
/// A task that represents the asynchronous deletion operation.
/// </returns>
public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null)
=> WebhookClientHelper.DeleteMessageAsync(this, messageId, options);
public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null, ulong ? threadId = null)
=> WebhookClientHelper.DeleteMessageAsync(this, messageId, options, threadId);

/// <summary> Sends a message to the channel for this webhook with an attachment. </summary>
/// <returns> Returns the ID of the created message. </returns>
public Task<ulong> SendFileAsync(string filePath, string text, bool isTTS = false,
IEnumerable<Embed> embeds = null, string username = null, string avatarUrl = null,
RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null,
MessageComponent components = null, MessageFlags flags = MessageFlags.None)
MessageComponent components = null, MessageFlags flags = MessageFlags.None, ulong? threadId = null)
=> WebhookClientHelper.SendFileAsync(this, filePath, text, isTTS, embeds, username, avatarUrl,
allowedMentions, options, isSpoiler, components, flags);
allowedMentions, options, isSpoiler, components, flags, threadId);
/// <summary> Sends a message to the channel for this webhook with an attachment. </summary>
/// <returns> Returns the ID of the created message. </returns>
public Task<ulong> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false,
IEnumerable<Embed> embeds = null, string username = null, string avatarUrl = null,
RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null,
MessageComponent components = null, MessageFlags flags = MessageFlags.None)
MessageComponent components = null, MessageFlags flags = MessageFlags.None, ulong? threadId = null)
=> WebhookClientHelper.SendFileAsync(this, stream, filename, text, isTTS, embeds, username,
avatarUrl, allowedMentions, options, isSpoiler, components, flags);
avatarUrl, allowedMentions, options, isSpoiler, components, flags, threadId);

/// <summary> Sends a message to the channel for this webhook with an attachment. </summary>
/// <returns> Returns the ID of the created message. </returns>
public Task<ulong> SendFileAsync(FileAttachment attachment, string text, bool isTTS = false,
IEnumerable<Embed> embeds = null, string username = null, string avatarUrl = null,
RequestOptions options = null, AllowedMentions allowedMentions = null, MessageComponent components = null,
MessageFlags flags = MessageFlags.None)
MessageFlags flags = MessageFlags.None, ulong? threadId = null)
=> WebhookClientHelper.SendFileAsync(this, attachment, text, isTTS, embeds, username,
avatarUrl, allowedMentions, components, options, flags);
avatarUrl, allowedMentions, components, options, flags, threadId);

/// <summary> Sends a message to the channel for this webhook with an attachment. </summary>
/// <returns> Returns the ID of the created message. </returns>
public Task<ulong> SendFilesAsync(IEnumerable<FileAttachment> attachments, string text, bool isTTS = false,
IEnumerable<Embed> embeds = null, string username = null, string avatarUrl = null,
RequestOptions options = null, AllowedMentions allowedMentions = null, MessageComponent components = null,
MessageFlags flags = MessageFlags.None)
MessageFlags flags = MessageFlags.None, ulong? threadId = null)
=> WebhookClientHelper.SendFilesAsync(this, attachments, text, isTTS, embeds, username, avatarUrl,
allowedMentions, components, options, flags);
allowedMentions, components, options, flags, threadId);


/// <summary> Modifies the properties of this webhook. </summary>


+ 17
- 16
src/Discord.Net.Webhook/WebhookClientHelper.cs View File

@@ -21,8 +21,8 @@ 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, MessageComponent components, MessageFlags flags)
string text, bool isTTS, IEnumerable<Embed> embeds, string username, string avatarUrl,
AllowedMentions allowedMentions, RequestOptions options, MessageComponent components, MessageFlags flags, ulong? threadId = null)
{
var args = new CreateWebhookMessageParams
{
@@ -44,12 +44,13 @@ namespace Discord.Webhook

if (flags is not MessageFlags.None and not MessageFlags.SuppressEmbeds)
throw new ArgumentException("The only valid MessageFlags are SuppressEmbeds and none.", nameof(flags));
var model = await client.ApiClient.CreateWebhookMessageAsync(client.Webhook.Id, args, options: options).ConfigureAwait(false);
var model = await client.ApiClient.CreateWebhookMessageAsync(client.Webhook.Id, args, options: options, threadId: threadId).ConfigureAwait(false);
return model.Id;
}

public static async Task ModifyMessageAsync(DiscordWebhookClient client, ulong messageId,
Action<WebhookMessageProperties> func, RequestOptions options)
Action<WebhookMessageProperties> func, RequestOptions options, ulong? threadId)
{
var args = new WebhookMessageProperties();
func(args);
@@ -94,35 +95,35 @@ namespace Discord.Webhook
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)
await client.ApiClient.ModifyWebhookMessageAsync(client.Webhook.Id, messageId, apiArgs, options, threadId)
.ConfigureAwait(false);
}
public static async Task DeleteMessageAsync(DiscordWebhookClient client, ulong messageId, RequestOptions options)
public static async Task DeleteMessageAsync(DiscordWebhookClient client, ulong messageId, RequestOptions options, ulong? threadId)
{
await client.ApiClient.DeleteWebhookMessageAsync(client.Webhook.Id, messageId, options).ConfigureAwait(false);
await client.ApiClient.DeleteWebhookMessageAsync(client.Webhook.Id, messageId, options, threadId).ConfigureAwait(false);
}
public static async Task<ulong> SendFileAsync(DiscordWebhookClient client, string filePath, string text, bool isTTS,
IEnumerable<Embed> embeds, string username, string avatarUrl, AllowedMentions allowedMentions, RequestOptions options,
bool isSpoiler, MessageComponent components, MessageFlags flags = MessageFlags.None)
bool isSpoiler, MessageComponent components, MessageFlags flags = MessageFlags.None, ulong? threadId = null)
{
string filename = Path.GetFileName(filePath);
using (var file = File.OpenRead(filePath))
return await SendFileAsync(client, file, filename, text, isTTS, embeds, username, avatarUrl, allowedMentions, options, isSpoiler, components, flags).ConfigureAwait(false);
return await SendFileAsync(client, file, filename, text, isTTS, embeds, username, avatarUrl, allowedMentions, options, isSpoiler, components, flags, threadId).ConfigureAwait(false);
}
public static Task<ulong> SendFileAsync(DiscordWebhookClient client, Stream stream, string filename, string text, bool isTTS,
IEnumerable<Embed> embeds, string username, string avatarUrl, AllowedMentions allowedMentions, RequestOptions options, bool isSpoiler,
MessageComponent components, MessageFlags flags)
=> SendFileAsync(client, new FileAttachment(stream, filename, isSpoiler: isSpoiler), text, isTTS, embeds, username, avatarUrl, allowedMentions, components, options, flags);
MessageComponent components, MessageFlags flags, ulong? threadId)
=> SendFileAsync(client, new FileAttachment(stream, filename, isSpoiler: isSpoiler), text, isTTS, embeds, username, avatarUrl, allowedMentions, components, options, flags, threadId);

public static Task<ulong> SendFileAsync(DiscordWebhookClient client, FileAttachment attachment, string text, bool isTTS,
IEnumerable<Embed> embeds, string username, string avatarUrl, AllowedMentions allowedMentions,
MessageComponent components, RequestOptions options, MessageFlags flags)
=> SendFilesAsync(client, new FileAttachment[] { attachment }, text, isTTS, embeds, username, avatarUrl, allowedMentions, components, options, flags);
MessageComponent components, RequestOptions options, MessageFlags flags, ulong? threadId)
=> SendFilesAsync(client, new FileAttachment[] { attachment }, text, isTTS, embeds, username, avatarUrl, allowedMentions, components, options, flags, threadId);

public static async Task<ulong> SendFilesAsync(DiscordWebhookClient client,
IEnumerable<FileAttachment> attachments, string text, bool isTTS, IEnumerable<Embed> embeds, string username,
string avatarUrl, AllowedMentions allowedMentions, MessageComponent components, RequestOptions options,
MessageFlags flags)
MessageFlags flags, ulong? threadId)
{
embeds ??= Array.Empty<Embed>();
@@ -164,7 +165,7 @@ namespace Discord.Webhook
MessageComponents = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
Flags = flags
};
var msg = await client.ApiClient.UploadWebhookFileAsync(client.Webhook.Id, args, options).ConfigureAwait(false);
var msg = await client.ApiClient.UploadWebhookFileAsync(client.Webhook.Id, args, options, threadId).ConfigureAwait(false);
return msg.Id;
}



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

@@ -2,57 +2,57 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Discord.Net</id>
<version>3.6.0$suffix$</version>
<version>3.6.1$suffix$</version>
<title>Discord.Net</title>
<authors>Discord.Net Contributors</authors>
<owners>foxbot</owners>
<description>An asynchronous API wrapper for Discord. This metapackage includes all of the optional Discord.Net components.</description>
<tags>discord;discordapp</tags>
<projectUrl>https://github.com/RogueException/Discord.Net</projectUrl>
<projectUrl>https://github.com/discord-net/Discord.Net</projectUrl>
<licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<iconUrl>https://github.com/RogueException/Discord.Net/raw/dev/docs/marketing/logo/PackageLogo.png</iconUrl>
<iconUrl>https://github.com/discord-net/Discord.Net/raw/dev/docs/marketing/logo/PackageLogo.png</iconUrl>
<dependencies>
<group targetFramework="net6.0">
<dependency id="Discord.Net.Core" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Rest" version="3.6.0$suffix$" />
<dependency id="Discord.Net.WebSocket" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Commands" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Webhook" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Interactions" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Core" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Rest" version="3.6.1$suffix$" />
<dependency id="Discord.Net.WebSocket" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Commands" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Webhook" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Interactions" version="3.6.1$suffix$" />
</group>
<group targetFramework="net5.0">
<dependency id="Discord.Net.Core" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Rest" version="3.6.0$suffix$" />
<dependency id="Discord.Net.WebSocket" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Commands" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Webhook" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Interactions" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Core" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Rest" version="3.6.1$suffix$" />
<dependency id="Discord.Net.WebSocket" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Commands" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Webhook" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Interactions" version="3.6.1$suffix$" />
</group>
<group targetFramework="net461">
<dependency id="Discord.Net.Core" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Rest" version="3.6.0$suffix$" />
<dependency id="Discord.Net.WebSocket" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Commands" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Webhook" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Interactions" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Core" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Rest" version="3.6.1$suffix$" />
<dependency id="Discord.Net.WebSocket" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Commands" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Webhook" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Interactions" version="3.6.1$suffix$" />
</group>
<group targetFramework="netstandard2.0">
<dependency id="Discord.Net.Core" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Rest" version="3.6.0$suffix$" />
<dependency id="Discord.Net.WebSocket" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Commands" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Webhook" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Interactions" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Core" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Rest" version="3.6.1$suffix$" />
<dependency id="Discord.Net.WebSocket" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Commands" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Webhook" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Interactions" version="3.6.1$suffix$" />
</group>
<group targetFramework="netstandard2.1">
<dependency id="Discord.Net.Core" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Rest" version="3.6.0$suffix$" />
<dependency id="Discord.Net.WebSocket" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Commands" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Webhook" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Interactions" version="3.6.0$suffix$" />
<dependency id="Discord.Net.Core" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Rest" version="3.6.1$suffix$" />
<dependency id="Discord.Net.WebSocket" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Commands" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Webhook" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Interactions" version="3.6.1$suffix$" />
</group>
</dependencies>
</metadata>
</package>
</package>

+ 42
- 106
test/Discord.Net.Tests.Unit/MockedEntities/MockedVoiceChannel.cs View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Discord.Audio;
@@ -12,8 +13,6 @@ namespace Discord

public int? UserLimit => throw new NotImplementedException();

public string Mention => throw new NotImplementedException();

public ulong? CategoryId => throw new NotImplementedException();

public int Position => throw new NotImplementedException();
@@ -24,116 +23,53 @@ namespace Discord

public IReadOnlyCollection<Overwrite> PermissionOverwrites => throw new NotImplementedException();

public string RTCRegion => throw new NotImplementedException();

public string Name => throw new NotImplementedException();

public DateTimeOffset CreatedAt => throw new NotImplementedException();
public ulong Id => throw new NotImplementedException();

public string RTCRegion => throw new NotImplementedException();

public Task AddPermissionOverwriteAsync(IRole role, OverwritePermissions permissions, RequestOptions options = null)
{
throw new NotImplementedException();
}

public Task AddPermissionOverwriteAsync(IUser user, OverwritePermissions permissions, RequestOptions options = null)
{
throw new NotImplementedException();
}
public ulong Id => throw new NotImplementedException();

public Task<IAudioClient> ConnectAsync(bool selfDeaf = false, bool selfMute = false, bool external = false)
{
throw new NotImplementedException();
}
public string Mention => throw new NotImplementedException();

public Task<IInviteMetadata> CreateInviteAsync(int? maxAge = 86400, int? maxUses = null, bool isTemporary = false, bool isUnique = false, RequestOptions options = null)
{
throw new NotImplementedException();
}
public Task<IInviteMetadata> CreateInviteToApplicationAsync(ulong applicationId, int? maxAge, int? maxUses = default(int?), bool isTemporary = false, bool isUnique = false, RequestOptions options = null)
=> throw new NotImplementedException();
public Task AddPermissionOverwriteAsync(IRole role, OverwritePermissions permissions, RequestOptions options = null) => throw new NotImplementedException();
public Task AddPermissionOverwriteAsync(IUser user, OverwritePermissions permissions, RequestOptions options = null) => throw new NotImplementedException();
public Task<IAudioClient> ConnectAsync(bool selfDeaf = false, bool selfMute = false, bool external = false) => throw new NotImplementedException();
public Task<IInviteMetadata> CreateInviteAsync(int? maxAge = 86400, int? maxUses = null, bool isTemporary = false, bool isUnique = false, RequestOptions options = null) => throw new NotImplementedException();
public Task<IInviteMetadata> CreateInviteToApplicationAsync(ulong applicationId, int? maxAge = 86400, int? maxUses = null, bool isTemporary = false, bool isUnique = false, RequestOptions options = null) => throw new NotImplementedException();
public Task<IInviteMetadata> CreateInviteToApplicationAsync(DefaultApplications application, int? maxAge = 86400, int? maxUses = null, bool isTemporary = false, bool isUnique = false, RequestOptions options = null) => throw new NotImplementedException();
public Task<IInviteMetadata> CreateInviteToStreamAsync(IUser user, int? maxAge, int? maxUses = default(int?), bool isTemporary = false, bool isUnique = false, RequestOptions options = null)
=> throw new NotImplementedException();

public Task DeleteAsync(RequestOptions options = null)
{
throw new NotImplementedException();
}

public Task DisconnectAsync()
{
throw new NotImplementedException();
}

public Task ModifyAsync(Action<AudioChannelProperties> func, RequestOptions options)
{
throw new NotImplementedException();
}

public Task<ICategoryChannel> GetCategoryAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null)
{
throw new NotImplementedException();
}

public Task<IReadOnlyCollection<IInviteMetadata>> GetInvitesAsync(RequestOptions options = null)
{
throw new NotImplementedException();
}

public OverwritePermissions? GetPermissionOverwrite(IRole role)
{
throw new NotImplementedException();
}

public OverwritePermissions? GetPermissionOverwrite(IUser user)
{
throw new NotImplementedException();
}

public Task<IGuildUser> GetUserAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null)
{
throw new NotImplementedException();
}

public IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> GetUsersAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null)
{
throw new NotImplementedException();
}

public Task ModifyAsync(Action<VoiceChannelProperties> func, RequestOptions options = null)
{
throw new NotImplementedException();
}

public Task ModifyAsync(Action<GuildChannelProperties> func, RequestOptions options = null)
{
throw new NotImplementedException();
}

public Task RemovePermissionOverwriteAsync(IRole role, RequestOptions options = null)
{
throw new NotImplementedException();
}

public Task RemovePermissionOverwriteAsync(IUser user, RequestOptions options = null)
{
throw new NotImplementedException();
}

public Task SyncPermissionsAsync(RequestOptions options = null)
{
throw new NotImplementedException();
}

Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options)
{
throw new NotImplementedException();
}

IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options)
{
throw new NotImplementedException();
}
public Task<IInviteMetadata> CreateInviteToStreamAsync(IUser user, int? maxAge = 86400, int? maxUses = null, bool isTemporary = false, bool isUnique = false, RequestOptions options = null) => throw new NotImplementedException();
public Task DeleteAsync(RequestOptions options = null) => throw new NotImplementedException();
public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) => throw new NotImplementedException();
public Task DeleteMessageAsync(IMessage message, RequestOptions options = null) => throw new NotImplementedException();
public Task DisconnectAsync() => throw new NotImplementedException();
public IDisposable EnterTypingState(RequestOptions options = null) => throw new NotImplementedException();
public Task<ICategoryChannel> GetCategoryAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null) => throw new NotImplementedException();
public Task<IReadOnlyCollection<IInviteMetadata>> GetInvitesAsync(RequestOptions options = null) => throw new NotImplementedException();
public Task<IMessage> GetMessageAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null) => throw new NotImplementedException();
public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = 100, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null) => throw new NotImplementedException();
public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = 100, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null) => throw new NotImplementedException();
public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = 100, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null) => throw new NotImplementedException();
public OverwritePermissions? GetPermissionOverwrite(IRole role) => throw new NotImplementedException();
public OverwritePermissions? GetPermissionOverwrite(IUser user) => throw new NotImplementedException();
public Task<IReadOnlyCollection<IMessage>> GetPinnedMessagesAsync(RequestOptions options = null) => throw new NotImplementedException();
public Task<IGuildUser> GetUserAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null) => throw new NotImplementedException();
public IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> GetUsersAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null) => throw new NotImplementedException();
public Task ModifyAsync(Action<VoiceChannelProperties> func, RequestOptions options = null) => throw new NotImplementedException();
public Task ModifyAsync(Action<GuildChannelProperties> func, RequestOptions options = null) => throw new NotImplementedException();
public Task ModifyAsync(Action<AudioChannelProperties> func, RequestOptions options = null) => throw new NotImplementedException();
public Task<IUserMessage> ModifyMessageAsync(ulong messageId, Action<MessageProperties> func, RequestOptions options = null) => throw new NotImplementedException();
public Task RemovePermissionOverwriteAsync(IRole role, RequestOptions options = null) => throw new NotImplementedException();
public Task RemovePermissionOverwriteAsync(IUser user, RequestOptions options = null) => throw new NotImplementedException();
public Task<IUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None) => throw new NotImplementedException();
public Task<IUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None) => throw new NotImplementedException();
public Task<IUserMessage> SendFileAsync(FileAttachment attachment, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None) => throw new NotImplementedException();
public Task<IUserMessage> SendFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None) => throw new NotImplementedException();
public Task<IUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None) => throw new NotImplementedException();
public Task SyncPermissionsAsync(RequestOptions options = null) => throw new NotImplementedException();
public Task TriggerTypingAsync(RequestOptions options = null) => throw new NotImplementedException();
Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) => throw new NotImplementedException();
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) => throw new NotImplementedException();
}
}

Loading…
Cancel
Save