Browse Source

Update some models and add channel rest methods

5.0
Paulo 4 years ago
parent
commit
864ce9c990
42 changed files with 2359 additions and 51 deletions
  1. +4
    -4
      src/Models/AuditLogs/AuditLogChangeKey.cs
  2. +9
    -0
      src/Models/Channels/CategoryChannel.cs
  3. +74
    -4
      src/Models/Channels/Channel.cs
  4. +9
    -0
      src/Models/Channels/DMChannel.cs
  5. +9
    -0
      src/Models/Channels/GroupChannel.cs
  6. +9
    -0
      src/Models/Channels/GuildChannel.cs
  7. +9
    -0
      src/Models/Channels/GuildTextChannel.cs
  8. +9
    -0
      src/Models/Channels/PrivateChannel.cs
  9. +9
    -0
      src/Models/Channels/StageChannel.cs
  10. +2
    -2
      src/Models/Channels/StageInstance.cs
  11. +9
    -0
      src/Models/Channels/ThreadChannel.cs
  12. +9
    -0
      src/Models/Channels/VoiceChannel.cs
  13. +4
    -4
      src/Models/Guilds/Guild.cs
  14. +2
    -2
      src/Models/Guilds/GuildMember.cs
  15. +22
    -2
      src/Models/Invites/InviteWithMetadata.cs
  16. +21
    -1
      src/Models/Messages/Message.cs
  17. +6
    -6
      src/Models/Messages/MessageType.cs
  18. +10
    -0
      src/Models/Messages/Reaction.cs
  19. +346
    -5
      src/Rest/DiscordRestClient.cs
  20. +57
    -0
      src/Rest/DiscordRestException.cs
  21. +839
    -4
      src/Rest/Net/IDiscordRestApi.cs
  22. +2
    -1
      src/Rest/Net/Requests/AuditLogs/GetGuildAuditLogParams.cs
  23. +32
    -0
      src/Rest/Net/Requests/Channels/EditChannelPermissionsParams.cs
  24. +23
    -0
      src/Rest/Net/Requests/Channels/FollowNewsChannelParams.cs
  25. +28
    -0
      src/Rest/Net/Requests/Channels/GroupDMAddRecipientParams.cs
  26. +27
    -0
      src/Rest/Net/Requests/Channels/ListJoinedPrivateArchivedThreadsParams.cs
  27. +28
    -0
      src/Rest/Net/Requests/Channels/ListPrivateArchivedThreadsParams.cs
  28. +28
    -0
      src/Rest/Net/Requests/Channels/ListPublicArchivedThreadsParams.cs
  29. +33
    -0
      src/Rest/Net/Requests/Channels/ModifyGroupChannelParams.cs
  30. +101
    -0
      src/Rest/Net/Requests/Channels/ModifyGuildChannelParams.cs
  31. +57
    -0
      src/Rest/Net/Requests/Channels/ModifyThreadChannelParams.cs
  32. +37
    -0
      src/Rest/Net/Requests/Channels/StartThreadWithMessageParams.cs
  33. +37
    -0
      src/Rest/Net/Requests/Channels/StartThreadWithoutMessageParams.cs
  34. +73
    -0
      src/Rest/Net/Requests/Invites/CreateChannelInviteParams.cs
  35. +27
    -0
      src/Rest/Net/Requests/Messages/BulkDeleteMessagesParams.cs
  36. +71
    -0
      src/Rest/Net/Requests/Messages/CreateMessageParams.cs
  37. +74
    -0
      src/Rest/Net/Requests/Messages/EditMessageParams.cs
  38. +44
    -0
      src/Rest/Net/Requests/Messages/GetChannelMessagesParams.cs
  39. +33
    -0
      src/Rest/Net/Requests/Messages/GetReactionsParams.cs
  40. +20
    -0
      src/Rest/Net/Requests/MultipartFile.cs
  41. +30
    -0
      src/Rest/Net/Responses/ThreadList.cs
  42. +86
    -16
      src/Rest/Preconditions.cs

+ 4
- 4
src/Models/AuditLogs/AuditLogChangeKey.cs View File

@@ -147,17 +147,17 @@ namespace Discord.Net.Models
//

/// <summary>
/// Text or voice <see cref="Channel"/> position changed.
/// <see cref="GuildTextChannel"/> or <see cref="VoiceChannel"/> position changed.
/// </summary>
Position,

/// <summary>
/// Text <see cref="Channel"/> topic changed.
/// <see cref="GuildTextChannel"/> topic changed.
/// </summary>
Topic,

/// <summary>
/// Voice <see cref="Channel"/> bitrate changed.
/// <see cref="VoiceChannel"/> bitrate changed.
/// </summary>
Bitrate,

@@ -297,7 +297,7 @@ namespace Discord.Net.Models
//

/// <summary>
/// New user limit in a voice <see cref="Channel"/>.
/// New user limit in a <see cref="VoiceChannel"/>.
/// </summary>
UserLimit,
}


+ 9
- 0
src/Models/Channels/CategoryChannel.cs View File

@@ -0,0 +1,9 @@
namespace Discord.Net.Models
{
/// <summary>
/// To be written - just a filler for xml docs for now
/// </summary>
public record CategoryChannel : GuildChannel
{
}
}

+ 74
- 4
src/Models/Channels/Channel.cs View File

@@ -11,6 +11,76 @@ namespace Discord.Net.Models
/// </remarks>
public record Channel
{
/// <summary>
/// Minimum langth of a channel name.
/// </summary>
public const int MinChannelNameLength = 1;

/// <summary>
/// Maximum langth of a channel name.
/// </summary>
public const int MaxChannelNameLength = 100;

/// <summary>
/// Minimum langth of a channel topic.
/// </summary>
public const int MinChannelTopicLength = 0;

/// <summary>
/// Maximum langth of a channel topic.
/// </summary>
public const int MaxChannelTopicLength = 1024;

/// <summary>
/// Minimum langth of a channel topic.
/// </summary>
public const int MinRateLimitPerUserDuration = 0;

/// <summary>
/// Maximum langth of a channel topic.
/// </summary>
public const int MaxRateLimitPerUserDuration = 1024;

/// <summary>
/// Minimum amount of users in channel.
/// </summary>
public const int MinUserLimit = 0;

/// <summary>
/// Maximum amount of users in channel.
/// </summary>
public const int MaxUserLimit = 99;

/// <summary>
/// Minimum bitrate for a channel.
/// </summary>
public const int MinBitrate = 8000;

/// <summary>
/// Maximum bitrate for a channel.
/// </summary>
public const int MaxBitrate = 128000;

/// <summary>
/// Minimum afk timeout duration for a channel.
/// </summary>
public const int MinAfkTimeoutDuration = 60;

/// <summary>
/// Maximum afk timeout duration for a channel.
/// </summary>
public const int MaxAFkTimeoutDuration = 3600;

/// <summary>
/// Minimum amount of messages to requests in a channel.
/// </summary>
public const int MinGetMessagesAmount = 1;

/// <summary>
/// Maximum amount of messages to requests in a channel.
/// </summary>
public const int MaxGetMessagesAmount = 100;

/// <summary>
/// The id of this <see cref="Channel"/>.
/// </summary>
@@ -73,13 +143,13 @@ namespace Discord.Net.Models
public Optional<Snowflake?> LastMessageId { get; init; }

/// <summary>
/// The bitrate (in bits) of the voice <see cref="Channel"/>.
/// The bitrate (in bits) of the <see cref="VoiceChannel"/>.
/// </summary>
[JsonPropertyName("bitrate")]
public Optional<int> Bitrate { get; init; }

/// <summary>
/// The <see cref="User"/> limit of the voice <see cref="Channel"/>.
/// The <see cref="User"/> limit of the <see cref="VoiceChannel"/>.
/// </summary>
[JsonPropertyName("user_limit")]
public Optional<int> UserLimit { get; init; }
@@ -135,13 +205,13 @@ namespace Discord.Net.Models
public Optional<DateTimeOffset?> LastPinTimestamp { get; init; }

/// <summary>
/// Voice region id for the voice <see cref="Channel"/>, automatic when set to null.
/// Voice region id for the <see cref="VoiceChannel"/>, automatic when set to null.
/// </summary>
[JsonPropertyName("rtc_region")]
public Optional<string?> RtcRegion { get; init; }

/// <summary>
/// The camera video quality mode of the voice channel, 1 when not present.
/// The camera video quality mode of the <see cref="VoiceChannel"/>, 1 when not present.
/// </summary>
[JsonPropertyName("video_quality_mode")]
public Optional<VideoQualityMode> VideoQualityMode { get; init; }


+ 9
- 0
src/Models/Channels/DMChannel.cs View File

@@ -0,0 +1,9 @@
namespace Discord.Net.Models
{
/// <summary>
/// To be written - just a filler for xml docs for now
/// </summary>
public record DMChannel : PrivateChannel
{
}
}

+ 9
- 0
src/Models/Channels/GroupChannel.cs View File

@@ -0,0 +1,9 @@
namespace Discord.Net.Models
{
/// <summary>
/// To be written - just a filler for xml docs for now
/// </summary>
public record GroupChannel : Channel
{
}
}

+ 9
- 0
src/Models/Channels/GuildChannel.cs View File

@@ -0,0 +1,9 @@
namespace Discord.Net.Models
{
/// <summary>
/// To be written - just a filler for xml docs for now
/// </summary>
public record GuildChannel : Channel
{
}
}

+ 9
- 0
src/Models/Channels/GuildTextChannel.cs View File

@@ -0,0 +1,9 @@
namespace Discord.Net.Models
{
/// <summary>
/// To be written - just a filler for xml docs for now
/// </summary>
public record GuildTextChannel : GuildChannel
{
}
}

+ 9
- 0
src/Models/Channels/PrivateChannel.cs View File

@@ -0,0 +1,9 @@
namespace Discord.Net.Models
{
/// <summary>
/// To be written - just a filler for xml docs for now
/// </summary>
public record PrivateChannel : Channel
{
}
}

+ 9
- 0
src/Models/Channels/StageChannel.cs View File

@@ -0,0 +1,9 @@
namespace Discord.Net.Models
{
/// <summary>
/// To be written - just a filler for xml docs for now
/// </summary>
public record StageChannel : GuildChannel
{
}
}

+ 2
- 2
src/Models/Channels/StageInstance.cs View File

@@ -17,13 +17,13 @@ namespace Discord.Net.Models
public Snowflake Id { get; }

/// <summary>
/// The <see cref="Guild"/> id of the associated Stage <see cref="Channel"/>.
/// The <see cref="Guild"/> id of the associated <see cref="StageChannel"/>.
/// </summary>
[JsonPropertyName("guild_id")]
public Snowflake GuildId { get; }

/// <summary>
/// The id of the associated Stage <see cref="Channel"/>.
/// The id of the associated <see cref="StageChannel"/>.
/// </summary>
[JsonPropertyName("channel_id")]
public Snowflake ChannelId { get; }


+ 9
- 0
src/Models/Channels/ThreadChannel.cs View File

@@ -0,0 +1,9 @@
namespace Discord.Net.Models
{
/// <summary>
/// To be written - just a filler for xml docs for now
/// </summary>
public record ThreadChannel : GuildChannel
{
}
}

+ 9
- 0
src/Models/Channels/VoiceChannel.cs View File

@@ -0,0 +1,9 @@
namespace Discord.Net.Models
{
/// <summary>
/// To be written - just a filler for xml docs for now
/// </summary>
public record VoiceChannel : GuildChannel
{
}
}

+ 4
- 4
src/Models/Guilds/Guild.cs View File

@@ -159,7 +159,7 @@ namespace Discord.Net.Models
public int SystemChannelFlags { get; init; }

/// <summary>
/// The id of the <see cref="Channel"/> where Community <see cref="Guild"/>s
/// The id of the <see cref="Channel"/> where Community <see cref="Guild"/>s.
/// can display rules and/or guidelines.
/// </summary>
[JsonPropertyName("rules_channel_id")]
@@ -190,7 +190,7 @@ namespace Discord.Net.Models
public Optional<int> MemberCount { get; init; }

/// <summary>
/// States of <see cref="GuildMember"/>s currently in voice <see cref="Channel"/>s.
/// States of <see cref="GuildMember"/>s currently in <see cref="VoiceChannel"/>s.
/// </summary>
/// <remarks>
/// Lacks the <see cref="VoiceState.GuildId"/>.
@@ -211,8 +211,8 @@ namespace Discord.Net.Models
public Optional<Channel[]> Channels { get; init; }

/// <summary>
/// All active thread <see cref="Channel"/>s in the <see cref="Guild"/> that current <see cref="User"/>
/// has permission to view.
/// All active <see cref="ThreadChannel"/>s in the <see cref="Guild"/> that the
/// current <see cref="User"/> has permission to view.
/// </summary>
[JsonPropertyName("threads")]
public Optional<Channel[]> Threads { get; init; }


+ 2
- 2
src/Models/Guilds/GuildMember.cs View File

@@ -42,13 +42,13 @@ namespace Discord.Net.Models
public Optional<DateTimeOffset?> PremiumSince { get; init; }

/// <summary>
/// Whether the user is deafened in voice <see cref="Channel"/>s.
/// Whether the user is deafened in <see cref="VoiceChannel"/>s.
/// </summary>
[JsonPropertyName("deaf")]
public bool Deaf { get; init; }

/// <summary>
/// Whether the user is muted in voice <see cref="Channel"/>s.
/// Whether the user is muted in <see cref="VoiceChannel"/>s.
/// </summary>
[JsonPropertyName("mute")]
public bool Mute { get; init; }


src/Models/Invites/InviteMetadata.cs → src/Models/Invites/InviteWithMetadata.cs View File

@@ -9,8 +9,28 @@ namespace Discord.Net.Models
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/invite#invite-metadata-object-invite-metadata-structure"/>
/// </remarks>
public record InviteMetadata : Invite
public record InviteWithMetadata : Invite
{
/// <summary>
/// Minimum age for an invite in seconds.
/// </summary>
public const int MinAgeTime = 0;

/// <summary>
/// Maximum age for an invite in seconds.
/// </summary>
public const int MaxAgeTime = 604800;

/// <summary>
/// Minimum amount of uses for an invite.
/// </summary>
public const int MinUseCount = 0;

/// <summary>
/// Maximum amount of uses for an invite.
/// </summary>
public const int MaxUseCount = 100;

/// <summary>
/// Number of times this <see cref="Invite"/> has been used.
/// </summary>
@@ -18,7 +38,7 @@ namespace Discord.Net.Models
public int Uses { get; init; }

/// <summary>
/// Max number of times this <see cref="Invite"/> can be used.
/// Maximum number of times this <see cref="Invite"/> can be used.
/// </summary>
[JsonPropertyName("max_uses")]
public int MaxUses { get; init; }

+ 21
- 1
src/Models/Messages/Message.cs View File

@@ -11,6 +11,26 @@ namespace Discord.Net.Models
/// </remarks>
public record Message
{
/// <summary>
/// Maximum length of the message content.
/// </summary>
public const int MaxContentLength = 2000;

/// <summary>
/// Maximum amount of embeds.
/// </summary>
public const int MaxEmbeds = 10;

/// <summary>
/// Minimum amount of messages to bulk delete.
/// </summary>
public const int MinBulkDeleteAmount = 2;

/// <summary>
/// Maximum amount of messages to bulk delete.
/// </summary>
public const int MaxBulkDeleteAmount = 100;

/// <summary>
/// Id of the <see cref="Message"/>.
/// </summary>
@@ -182,7 +202,7 @@ namespace Discord.Net.Models
public Optional<MessageInteraction> Interaction { get; init; }

/// <summary>
/// The thread <see cref="Channel"/> that was started from this <see cref="Message"/>,
/// The <see cref="ThreadChannel"/> that was started from this <see cref="Message"/>,
/// includes <see cref="ThreadMember"/>s.
/// </summary>
[JsonPropertyName("thread")]


+ 6
- 6
src/Models/Messages/MessageType.cs View File

@@ -14,12 +14,12 @@ namespace Discord.Net.Models
Default = 0,

/// <summary>
/// Recipient was added to a group <see cref="Channel"/>.
/// Recipient was added to a <see cref="GroupChannel"/>.
/// </summary>
RecipientAdd = 1,

/// <summary>
/// Recipient was removed from a group <see cref="Channel"/>.
/// Recipient was removed from a <see cref="GroupChannel"/>.
/// </summary>
RecipientRemove = 2,

@@ -29,12 +29,12 @@ namespace Discord.Net.Models
Call = 3,

/// <summary>
/// Group <see cref="Channel"/> name was changed.
/// <see cref="GroupChannel"/> name was changed.
/// </summary>
ChannelNameChange = 4,

/// <summary>
/// Group <see cref="Channel"/> icon was changed.
/// <see cref="GroupChannel"/> icon was changed.
/// </summary>
ChannelIconChange = 5,

@@ -96,7 +96,7 @@ namespace Discord.Net.Models
GuildDiscoveryGracePeriodFinalWarning = 17,

/// <summary>
/// A thread <see cref="Channel"/> was created.
/// A <see cref="ThreadChannel"/> was created.
/// </summary>
ThreadCreated = 18,

@@ -111,7 +111,7 @@ namespace Discord.Net.Models
ApplicationCommand = 20,

/// <summary>
/// Starter message for a thread <see cref="Channel"/>.
/// Starter message for a <see cref="ThreadChannel"/>.
/// </summary>
ThreadStarterMessage = 21,



+ 10
- 0
src/Models/Messages/Reaction.cs View File

@@ -10,6 +10,16 @@ namespace Discord.Net.Models
/// </remarks>
public record Reaction
{
/// <summary>
/// Minimum amount of users that reacted in a message.
/// </summary>
public const int MinGetReactionsAmount = 1;

/// <summary>
/// Maximum amount of users that reacted in a message.
/// </summary>
public const int MaxGetReactionsAmount = 100;

/// <summary>
/// Times this <see cref="Models.Emoji"/> has been used to react.
/// </summary>


+ 346
- 5
src/Rest/DiscordRestClient.cs View File

@@ -28,16 +28,357 @@ namespace Discord.Net
#region Audit Log

/// <inheritdoc/>
public Task<AuditLog> GetGuildAuditLogAsync(Snowflake guildId, GetGuildAuditLogParams? args = null, CancellationToken cancellationToken = default)
public Task<AuditLog> GetGuildAuditLogAsync(Snowflake guildId, GetGuildAuditLogParams args, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(guildId, nameof(guildId));
if (args == null)
args = new();
else
args.Validate();
Preconditions.NotNull(args, nameof(args));
args.Validate();
return _api.GetGuildAuditLogAsync(guildId, args, cancellationToken);
}

#endregion

#region Channel

/// <inheritdoc/>
public Task<Channel> GetChannelAsync(Snowflake channelId, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
return _api.GetChannelAsync(channelId, cancellationToken);
}

/// <inheritdoc/>
public Task<GroupChannel> ModifyChannelAsync(Snowflake channelId, ModifyGroupChannelParams args, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotNull(args, nameof(args));
args.Validate();
return _api.ModifyChannelAsync(channelId, args, cancellationToken);
}

/// <inheritdoc/>
public Task<GuildChannel> ModifyChannelAsync(Snowflake channelId, ModifyGuildChannelParams args, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotNull(args, nameof(args));
args.Validate();
return _api.ModifyChannelAsync(channelId, args, cancellationToken);
}

/// <inheritdoc/>
public Task<ThreadChannel> ModifyChannelAsync(Snowflake channelId, ModifyThreadChannelParams args, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotNull(args, nameof(args));
args.Validate();
return _api.ModifyChannelAsync(channelId, args, cancellationToken);
}

/// <inheritdoc/>
public Task<Channel> DeleteChannelAsync(Snowflake channelId, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
return _api.DeleteChannelAsync(channelId, cancellationToken);
}

/// <inheritdoc/>
public Task<Message[]> GetChannelMessagesAsync(Snowflake channelId, GetChannelMessagesParams args, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotNull(args, nameof(args));
args.Validate();
return _api.GetChannelMessagesAsync(channelId, args, cancellationToken);
}

/// <inheritdoc/>
public Task<Message> GetChannelMessageAsync(Snowflake channelId, Snowflake messageId, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotZero(messageId, nameof(messageId));
return _api.GetChannelMessageAsync(channelId, messageId, cancellationToken);
}

/// <inheritdoc/>
public Task<Message> CreateMessageAsync(Snowflake channelId, CreateMessageParams args, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotNull(args, nameof(args));
args.Validate();
return _api.CreateMessageAsync(channelId, args, cancellationToken);
}

/// <inheritdoc/>
public Task<Message> CrosspostMessageAsync(Snowflake channelId, Snowflake messageId, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotZero(messageId, nameof(messageId));
return _api.CrosspostMessageAsync(channelId, messageId, cancellationToken);
}

/// <inheritdoc/>
public Task CreateReactionAsync(Snowflake channelId, Snowflake messageId, Emoji emoji, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotZero(messageId, nameof(messageId));
Preconditions.NotNull(emoji, nameof(emoji));
return _api.CreateReactionAsync(channelId, messageId, emoji, cancellationToken);
}

/// <inheritdoc/>
public Task DeleteOwnReactionAsync(Snowflake channelId, Snowflake messageId, Emoji emoji, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotZero(messageId, nameof(messageId));
Preconditions.NotNull(emoji, nameof(emoji));
return _api.DeleteOwnReactionAsync(channelId, messageId, emoji, cancellationToken);
}

/// <inheritdoc/>
public Task DeleteUserReactionAsync(Snowflake channelId, Snowflake messageId, Snowflake userId, Emoji emoji, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(messageId, nameof(messageId));
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotZero(userId, nameof(userId));
Preconditions.NotNull(emoji, nameof(emoji));
return _api.DeleteUserReactionAsync(channelId, messageId, userId, emoji, cancellationToken);
}

/// <inheritdoc/>
public Task<User[]> GetReactionsAsync(Snowflake channelId, Snowflake messageId, Emoji emoji, GetReactionsParams args, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotZero(messageId, nameof(messageId));
Preconditions.NotNull(emoji, nameof(emoji));
Preconditions.NotNull(args, nameof(args));
args.Validate();
return _api.GetReactionsAsync(channelId, messageId, emoji, args, cancellationToken);
}

/// <inheritdoc/>
public Task DeleteAllReactionsAsync(Snowflake channelId, Snowflake messageId, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(messageId, nameof(messageId));
Preconditions.NotZero(channelId, nameof(channelId));
return _api.DeleteAllReactionsAsync(messageId, channelId, cancellationToken);
}

/// <inheritdoc/>
public Task DeleteAllReactionsforEmojiAsync(Snowflake channelId, Snowflake messageId, Emoji emoji, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotZero(messageId, nameof(messageId));
Preconditions.NotNull(emoji, nameof(emoji));
return _api.DeleteAllReactionsforEmojiAsync(channelId, messageId, emoji, cancellationToken);
}

/// <inheritdoc/>
public Task<Message> EditMessageAsync(Snowflake channelId, Snowflake messageId, EditMessageParams args, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotZero(messageId, nameof(messageId));
Preconditions.NotNull(args, nameof(args));
args.Validate();
return _api.EditMessageAsync(channelId, messageId, args, cancellationToken);
}

/// <inheritdoc/>
public Task DeleteMessageAsync(Snowflake channelId, Snowflake messageId, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotZero(messageId, nameof(messageId));
return _api.DeleteMessageAsync(channelId, messageId, cancellationToken);
}

/// <inheritdoc/>
public Task BulkDeleteMessagesAsync(Snowflake channelId, BulkDeleteMessagesParams args, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotNull(args, nameof(args));
args.Validate();
return _api.BulkDeleteMessagesAsync(channelId, args, cancellationToken);
}

/// <inheritdoc/>
public Task EditChannelPermissionsAsync(Snowflake channelId, Snowflake overwriteId, EditChannelPermissionsParams args, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotZero(overwriteId, nameof(overwriteId));
Preconditions.NotNull(args, nameof(args));
args.Validate();
return _api.EditChannelPermissionsAsync(channelId, overwriteId, args, cancellationToken);
}

/// <inheritdoc/>
public Task<InviteWithMetadata[]> GetChannelInvitesAsync(Snowflake channelId, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
return _api.GetChannelInvitesAsync(channelId, cancellationToken);
}

/// <inheritdoc/>
public Task<Invite> CreateChannelInviteAsync(Snowflake channelId, CreateChannelInviteParams args, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotNull(args, nameof(args));
args.Validate();
return _api.CreateChannelInviteAsync(channelId, args, cancellationToken);
}

/// <inheritdoc/>
public Task DeleteChannelPermissionAsync(Snowflake channelId, Snowflake overwriteId, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotZero(overwriteId, nameof(overwriteId));
return _api.DeleteChannelPermissionAsync(channelId, overwriteId, cancellationToken);
}

/// <inheritdoc/>
public Task<FollowedChannel> FollowNewsChannelAsync(Snowflake channelId, FollowNewsChannelParams args, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotNull(args, nameof(args));
args.Validate();
return _api.FollowNewsChannelAsync(channelId, args, cancellationToken);
}

/// <inheritdoc/>
public Task TriggerTypingIndicatorAsync(Snowflake channelId, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
return _api.TriggerTypingIndicatorAsync(channelId, cancellationToken);
}

/// <inheritdoc/>
public Task<Message[]> GetPinnedMessagesAsync(Snowflake channelId, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
return _api.GetPinnedMessagesAsync(channelId, cancellationToken);
}

/// <inheritdoc/>
public Task PinMessageAsync(Snowflake channelId, Snowflake messageId, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotZero(messageId, nameof(messageId));
return _api.PinMessageAsync(channelId, messageId, cancellationToken);
}

/// <inheritdoc/>
public Task UnpinMessageAsync(Snowflake channelId, Snowflake messageId, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotZero(messageId, nameof(messageId));
return _api.UnpinMessageAsync(channelId, messageId, cancellationToken);
}

/// <inheritdoc/>
public Task GroupDMAddRecipientAsync(Snowflake channelId, Snowflake userId, GroupDMAddRecipientParams args, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotZero(userId, nameof(userId));
Preconditions.NotNull(args, nameof(args));
args.Validate();
return _api.GroupDMAddRecipientAsync(channelId, userId, args, cancellationToken);
}

/// <inheritdoc/>
public Task GroupDMRemoveRecipientAsync(Snowflake channelId, Snowflake userId, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotZero(userId, nameof(userId));
return _api.GroupDMRemoveRecipientAsync(channelId, userId, cancellationToken);
}

/// <inheritdoc/>
public Task<ThreadChannel> StartThreadAsync(Snowflake channelId, Snowflake messageId, StartThreadWithMessageParams args, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotZero(messageId, nameof(messageId));
Preconditions.NotNull(args, nameof(args));
args.Validate();
return _api.StartThreadAsync(channelId, messageId, args, cancellationToken);
}

/// <inheritdoc/>
public Task<ThreadChannel> StartThreadAsync(Snowflake channelId, StartThreadWithoutMessageParams args, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotNull(args, nameof(args));
args.Validate();
return _api.StartThreadAsync(channelId, args, cancellationToken);
}

/// <inheritdoc/>
public Task JoinThreadAsync(Snowflake channelId, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
return _api.JoinThreadAsync(channelId, cancellationToken);
}

/// <inheritdoc/>
public Task AddThreadMemberAsync(Snowflake channelId, Snowflake userId, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotZero(userId, nameof(userId));
return _api.AddThreadMemberAsync(channelId, userId, cancellationToken);
}

/// <inheritdoc/>
public Task LeaveThreadAsync(Snowflake channelId, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
return _api.LeaveThreadAsync(channelId, cancellationToken);
}

/// <inheritdoc/>
public Task RemoveThreadMemberAsync(Snowflake channelId, Snowflake userId, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotZero(userId, nameof(userId));
return _api.RemoveThreadMemberAsync(channelId, userId, cancellationToken);
}

/// <inheritdoc/>
public Task<ThreadMember[]> ListThreadMembersAsync(Snowflake channelId, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
return _api.ListThreadMembersAsync(channelId, cancellationToken);
}

/// <inheritdoc/>
public Task<ThreadList> ListActiveThreadsAsync(Snowflake channelId, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
return _api.ListActiveThreadsAsync(channelId, cancellationToken);
}

/// <inheritdoc/>
public Task<ThreadList> ListPublicArchivedThreadsAsync(Snowflake channelId, ListPublicArchivedThreadsParams args, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotNull(args, nameof(args));
args.Validate();
return _api.ListPublicArchivedThreadsAsync(channelId, args, cancellationToken);
}

/// <inheritdoc/>
public Task<ThreadList> ListPrivateArchivedThreadsAsync(Snowflake channelId, ListPrivateArchivedThreadsParams args, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotNull(args, nameof(args));
args.Validate();
return _api.ListPrivateArchivedThreadsAsync(channelId, args, cancellationToken);
}

/// <inheritdoc/>
public Task<ThreadList> ListJoinedPrivateArchivedThreadsAsync(Snowflake channelId, ListJoinedPrivateArchivedThreadsParams args, CancellationToken cancellationToken = default)
{
Preconditions.NotZero(channelId, nameof(channelId));
Preconditions.NotNull(args, nameof(args));
args.Validate();
return _api.ListJoinedPrivateArchivedThreadsAsync(channelId, args, cancellationToken);
}

#endregion Channel
}
}

+ 57
- 0
src/Rest/DiscordRestException.cs View File

@@ -0,0 +1,57 @@
using System;
using System.Net;

namespace Discord.Net.Rest
{
/// <summary>
/// Represents errors received in response to rest requests.
/// </summary>
public class DiscordRestException : Exception
{
/// <summary>
/// HTTP status code returned.
/// </summary>
public HttpStatusCode HttpCode { get; }

/// <summary>
/// Discord JSON error code.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/topics/opcodes-and-status-codes#json-json-error-codes"/>
/// </remarks>
public int? DiscordCode { get; }

/// <summary>
/// Reason of this error.
/// </summary>
public string? Reason { get; }

/// <summary>
/// Creates a <see cref="DiscordRestException"/> with the provided error data.
/// </summary>
/// <param name="httpCode">
/// HTTP status code.
/// </param>
/// <param name="discordCode">
/// Discord JSON error code.
/// </param>
/// <param name="reason">
/// Reason of this error.
/// </param>
public DiscordRestException(HttpStatusCode httpCode, int? discordCode = null, string? reason = null)
: base(CreateMessage(httpCode, discordCode, reason))
{
HttpCode = httpCode;
DiscordCode = discordCode;
Reason = reason;
}

private static string CreateMessage(HttpStatusCode httpCode, int? discordCode = null, string? reason = null)
{
if (!string.IsNullOrEmpty(reason))
return $"The server responded with error {discordCode ?? (int)httpCode}: {reason}";
else
return $"The server responded with error {discordCode ?? (int)httpCode}: {httpCode}";
}
}
}

+ 839
- 4
src/Rest/Net/IDiscordRestApi.cs View File

@@ -13,13 +13,13 @@ namespace Discord.Net.Rest
#region Audit Log

/// <summary>
/// Returns an audit log object for the guild.
/// Gets an <see cref="AuditLog"/> for the <see cref="Guild"/>.
/// </summary>
/// <remarks>
/// Requires the <see cref="Permissions.ViewAuditLog"/> permission.
/// <see href="https://discord.com/developers/docs/resources/audit-log#get-guild-audit-log"/>
/// </remarks>
/// <param name="guildId">
/// The guild identifier.
/// The <see cref="Guild"/> identifier.
/// </param>
/// <param name="args">
/// Parameters to include in the request.
@@ -27,8 +27,843 @@ namespace Discord.Net.Rest
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
Task<AuditLog> GetGuildAuditLogAsync(Snowflake guildId, GetGuildAuditLogParams? args = null, CancellationToken cancellationToken = default);
/// <returns>
/// A task that contains an <see cref="AuditLog"/>.
/// </returns>
Task<AuditLog> GetGuildAuditLogAsync(Snowflake guildId, GetGuildAuditLogParams args, CancellationToken cancellationToken = default);

#endregion

#region Channel

/// <summary>
/// Gets a <see cref="Channel"/> by their identifier.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#get-channel"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that contains a <see cref="Channel"/> if it exists;
/// otherwise, <see langword="null"/>.
/// </returns>
Task<Channel> GetChannelAsync(Snowflake channelId, CancellationToken cancellationToken = default);

/// <summary>
/// Updates a <see cref="GroupChannel"/>'s settings.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#modify-channel"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="GroupChannel"/> identifier.
/// </param>
/// <param name="args">
/// Parameters to include in the request.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that contains the updated <see cref="GroupChannel"/>.
/// </returns>
/// <exception cref="DiscordRestException">
/// Thrown when invalid parameters are supplied in <paramref name="args"/>.
/// </exception>
Task<GroupChannel> ModifyChannelAsync(Snowflake channelId, ModifyGroupChannelParams args, CancellationToken cancellationToken = default);

/// <summary>
/// Updates a <see cref="GuildChannel"/>'s settings.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#modify-channel"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="GuildChannel"/> identifier.
/// </param>
/// <param name="args">
/// Parameters to include in the request.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that contains the updated <see cref="GuildChannel"/>.
/// </returns>
/// <exception cref="DiscordRestException">
/// Thrown when invalid parameters are supplied in <paramref name="args"/>.
/// </exception>
Task<GuildChannel> ModifyChannelAsync(Snowflake channelId, ModifyGuildChannelParams args, CancellationToken cancellationToken = default);

/// <summary>
/// Updates a <see cref="ThreadChannel"/>'s settings.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#modify-channel"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="ThreadChannel"/> identifier.
/// </param>
/// <param name="args">
/// Parameters to include in the request.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that contains the updated <see cref="ThreadChannel"/>.
/// </returns>
/// <exception cref="DiscordRestException">
/// Thrown when invalid parameters are supplied in <paramref name="args"/>.
/// </exception>
Task<ThreadChannel> ModifyChannelAsync(Snowflake channelId, ModifyThreadChannelParams args, CancellationToken cancellationToken = default);

/// <summary>
/// Deletes a <see cref="Channel"/>, or closes a <see cref="PrivateChannel"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#deleteclose-channel"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that contains the deleted <see cref="Channel"/>.
/// </returns>
Task<Channel> DeleteChannelAsync(Snowflake channelId, CancellationToken cancellationToken = default);

/// <summary>
/// Gets an array of <see cref="Message"/>s from a <see cref="Channel"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#get-channel-messages"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="args">
/// Parameters to include in the request.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that contains an array of <see cref="Message"/>s from the <see cref="Channel"/>.
/// </returns>
Task<Message[]> GetChannelMessagesAsync(Snowflake channelId, GetChannelMessagesParams args, CancellationToken cancellationToken = default);

/// <summary>
/// Gets a specific <see cref="Message"/> from a <see cref="Channel"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#get-channel-message"/>
/// </remarks>
/// <param name="messageId">
/// The <see cref="Message"/> identifier.
/// </param>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that contains the specified <see cref="Message"/> or <see langword="null"/>.
/// </returns>
Task<Message> GetChannelMessageAsync(Snowflake channelId, Snowflake messageId, CancellationToken cancellationToken = default);

/// <summary>
/// Sends a <see cref="Message"/> to a <see cref="Channel"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#create-message"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="args">
/// Parameters to include in the request.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that contains the <see cref="Message"/> created.
/// </returns>
Task<Message> CreateMessageAsync(Snowflake channelId, CreateMessageParams args, CancellationToken cancellationToken = default);

/// <summary>
/// Crossposts a <see cref="Message"/> in a News <see cref="Channel"/> to following channels.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#crosspost-message"/>
/// </remarks>
/// <param name="messageId">
/// The <see cref="Message"/> identifier.
/// </param>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that contains the crossposted <see cref="Message"/>.
/// </returns>
Task<Message> CrosspostMessageAsync(Snowflake channelId, Snowflake messageId, CancellationToken cancellationToken = default);

/// <summary>
/// Creates a <see cref="Reaction"/> for a <see cref="Message"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#create-reaction"/>
/// </remarks>
/// <param name="messageId">
/// The <see cref="Message"/> identifier.
/// </param>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="emoji">
/// An <see cref="Emoji"/>.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that represents this asynchronous operation.
/// </returns>
Task CreateReactionAsync(Snowflake channelId, Snowflake messageId, Emoji emoji, CancellationToken cancellationToken = default);

/// <summary>
/// Deletes a <see cref="Reaction"/> the current user has made for the <see cref="Message"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#delete-own-reaction"/>
/// </remarks>
/// <param name="messageId">
/// The <see cref="Message"/> identifier.
/// </param>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="emoji">
/// An <see cref="Emoji"/>.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that represents this asynchronous operation.
/// </returns>
Task DeleteOwnReactionAsync(Snowflake channelId, Snowflake messageId, Emoji emoji, CancellationToken cancellationToken = default);

/// <summary>
/// Deletes another <see cref="User"/>'s <see cref="Reaction"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#delete-user-reaction"/>
/// </remarks>
/// <param name="messageId">
/// The <see cref="Message"/> identifier.
/// </param>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="userId">
/// The <see cref="User"/> identifier.
/// </param>
/// <param name="emoji">
/// An <see cref="Emoji"/>.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that represents this asynchronous operation.
/// </returns>
Task DeleteUserReactionAsync(Snowflake channelId, Snowflake messageId, Snowflake userId, Emoji emoji, CancellationToken cancellationToken = default);

/// <summary>
/// Gets an array of <see cref="User"/>s that reacted with this <see cref="Emoji"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#get-reactions"/>
/// </remarks>
/// <param name="messageId">
/// The <see cref="Message"/> identifier.
/// </param>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="emoji">
/// An <see cref="Emoji"/>.
/// </param>
/// <param name="args">
/// Parameters to include in the request.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that contains an array of <see cref="User"/>s that reacted with
/// the provided <see cref="Emoji"/>.
/// </returns>
Task<User[]> GetReactionsAsync(Snowflake channelId, Snowflake messageId, Emoji emoji, GetReactionsParams args, CancellationToken cancellationToken = default);

/// <summary>
/// Deletes all <see cref="Reaction"/>s on a <see cref="Message"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#delete-all-reactions"/>
/// </remarks>
/// <param name="messageId">
/// The <see cref="Message"/> identifier.
/// </param>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that represents this asynchronous operation.
/// </returns>
Task DeleteAllReactionsAsync(Snowflake channelId, Snowflake messageId, CancellationToken cancellationToken = default);

/// <summary>
/// Deletes all the <see cref="Reaction"/>s for a given <see cref="Emoji"/>
/// on a <see cref="Message"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji"/>
/// </remarks>
/// <param name="messageId">
/// The <see cref="Message"/> identifier.
/// </param>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="emoji">
/// An <see cref="Emoji"/>.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that represents this asynchronous operation.
/// </returns>
Task DeleteAllReactionsforEmojiAsync(Snowflake channelId, Snowflake messageId, Emoji emoji, CancellationToken cancellationToken = default);

/// <summary>
/// Edits a previously sent <see cref="Message"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#edit-message"/>
/// </remarks>
/// <param name="messageId">
/// The <see cref="Message"/> identifier.
/// </param>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="args">
/// Parameters to include in the request.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that contains the updated <see cref="Message"/>.
/// </returns>
Task<Message> EditMessageAsync(Snowflake channelId, Snowflake messageId, EditMessageParams args, CancellationToken cancellationToken = default);

/// <summary>
/// Deletes a message.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#delete-message"/>
/// </remarks>
/// <param name="messageId">
/// The <see cref="Message"/> identifier.
/// </param>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that represents this asynchronous operation.
/// </returns>
Task DeleteMessageAsync(Snowflake channelId, Snowflake messageId, CancellationToken cancellationToken = default);

/// <summary>
/// Deletes multiple <see cref="Message"/>s in a single request.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#bulk-delete-messages"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="args">
/// Parameters to include in the request.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that represents this asynchronous operation.
/// </returns>
/// <exception cref="DiscordRestException">
/// Thrown when invalid parameters are supplied in <paramref name="args"/>.
/// </exception>
Task BulkDeleteMessagesAsync(Snowflake channelId, BulkDeleteMessagesParams args, CancellationToken cancellationToken = default);

/// <summary>
/// Edits the <see cref="Channel"/> permission <see cref="Overwrite"/>s for a <see cref="User"/> or <see cref="Role"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#edit-channel-permissions"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="overwriteId">
/// The <see cref="Overwrite"/> identifier.
/// </param>
/// <param name="args">
/// Parameters to include in the request.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that represents this asynchronous operation.
/// </returns>
Task EditChannelPermissionsAsync(Snowflake channelId, Snowflake overwriteId, EditChannelPermissionsParams args, CancellationToken cancellationToken = default);

/// <summary>
/// Gets an array of <see cref="Invite"/>s for a <see cref="Channel"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#get-channel-invites"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that contains an array of <see cref="InviteWithMetadata"/>s.
/// </returns>
Task<InviteWithMetadata[]> GetChannelInvitesAsync(Snowflake channelId, CancellationToken cancellationToken = default);

/// <summary>
/// Creates a new <see cref="Invite"/> for the <see cref="Channel"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#create-channel-invite"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="args">
/// Parameters to include in the request.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that contains the created <see cref="Invite"/>.
/// </returns>
Task<Invite> CreateChannelInviteAsync(Snowflake channelId, CreateChannelInviteParams args, CancellationToken cancellationToken = default);

/// <summary>
/// Deletes a <see cref="Channel"/> permission <see cref="Overwrite"/> for a <see cref="User"/> or <see cref="Role"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#delete-channel-permission"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="overwriteId">
/// The <see cref="Overwrite"/> identifier.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that represents this asynchronous operation.
/// </returns>
Task DeleteChannelPermissionAsync(Snowflake channelId, Snowflake overwriteId, CancellationToken cancellationToken = default);

/// <summary>
/// Follow a News <see cref="Channel"/> to send <see cref="Message"/>s to a target <see cref="Channel"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#follow-news-channel"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="args">
/// Parameters to include in the request.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that contains the <see cref="FollowedChannel"/>.
/// </returns>
Task<FollowedChannel> FollowNewsChannelAsync(Snowflake channelId, FollowNewsChannelParams args, CancellationToken cancellationToken = default);

/// <summary>
/// Posts a typing indicator for the specified <see cref="Channel"/>.
/// </summary>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that represents this asynchronous operation.
/// </returns>
Task TriggerTypingIndicatorAsync(Snowflake channelId, CancellationToken cancellationToken = default);

/// <summary>
/// Gets all pinned <see cref="Message"/>s in the <see cref="Channel"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#get-pinned-messages"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that contains an array of all pinned <see cref="Message"/>s.
/// </returns>
Task<Message[]> GetPinnedMessagesAsync(Snowflake channelId, CancellationToken cancellationToken = default);

/// <summary>
/// Pins a <see cref="Message"/> in a <see cref="Channel"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#pin-message"/>
/// </remarks>
/// <param name="messageId">
/// The <see cref="Message"/> identifier.
/// </param>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that represents this asynchronous operation.
/// </returns>
Task PinMessageAsync(Snowflake channelId, Snowflake messageId, CancellationToken cancellationToken = default);

/// <summary>
/// Unpins a <see cref="Message"/> in a <see cref="Channel"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#unpin-message"/>
/// </remarks>
/// <param name="messageId">
/// The <see cref="Message"/> identifier.
/// </param>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that represents this asynchronous operation.
/// </returns>
Task UnpinMessageAsync(Snowflake channelId, Snowflake messageId, CancellationToken cancellationToken = default);

/// <summary>
/// Adds a recipient to a <see cref="GroupChannel"/> using their access token.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#group-dm-add-recipient"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="userId">
/// The <see cref="User"/> identifier.
/// </param>
/// <param name="args">
/// Parameters to include in the request.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that represents this asynchronous operation.
/// </returns>
Task GroupDMAddRecipientAsync(Snowflake channelId, Snowflake userId, GroupDMAddRecipientParams args, CancellationToken cancellationToken = default);

/// <summary>
/// Removes a recipient from a <see cref="GroupChannel"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#group-dm-remove-recipient"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="userId">
/// The <see cref="User"/> identifier.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that represents this asynchronous operation.
/// </returns>
Task GroupDMRemoveRecipientAsync(Snowflake channelId, Snowflake userId, CancellationToken cancellationToken = default);

/// <summary>
/// Creates a new <see cref="ThreadChannel"/> from an existing <see cref="Message"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#start-thread-with-message"/>
/// </remarks>
/// <param name="messageId">
/// The <see cref="Message"/> identifier.
/// </param>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="args">
/// Parameters to include in the request.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that contains the created <see cref="ThreadChannel"/>.
/// </returns>
/// <exception cref="DiscordRestException">
/// Thrown when invalid parameters are supplied in <paramref name="args"/>.
/// </exception>
Task<ThreadChannel> StartThreadAsync(Snowflake channelId, Snowflake messageId, StartThreadWithMessageParams args, CancellationToken cancellationToken = default);

/// <summary>
/// Creates a new private <see cref="ThreadChannel"/> that is not connected to an existing <see cref="Message"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#start-thread-without-message"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="args">
/// Parameters to include in the request.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that contains the created <see cref="ThreadChannel"/>.
/// </returns>
/// <exception cref="DiscordRestException">
/// Thrown when invalid parameters are supplied in <paramref name="args"/>.
/// </exception>
Task<ThreadChannel> StartThreadAsync(Snowflake channelId, StartThreadWithoutMessageParams args, CancellationToken cancellationToken = default);

/// <summary>
/// Adds the current user to a <see cref="ThreadChannel"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#join-thread"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="ThreadChannel"/> identifier.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that represents this asynchronous operation.
/// </returns>
Task JoinThreadAsync(Snowflake channelId, CancellationToken cancellationToken = default);

/// <summary>
/// Adds another <see cref="GuildMember"/> to a <see cref="ThreadChannel"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#add-thread-member"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="ThreadChannel"/> identifier.
/// </param>
/// <param name="userId">
/// The <see cref="GuildMember"/> identifier.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that represents this asynchronous operation.
/// </returns>
Task AddThreadMemberAsync(Snowflake channelId, Snowflake userId, CancellationToken cancellationToken = default);

/// <summary>
/// Removes the current user from a <see cref="ThreadChannel"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#leave-thread"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="ThreadChannel"/> identifier.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that represents this asynchronous operation.
/// </returns>
Task LeaveThreadAsync(Snowflake channelId, CancellationToken cancellationToken = default);

/// <summary>
/// Removes another <see cref="GuildMember"/> from a <see cref="ThreadChannel"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#remove-thread-member"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="ThreadChannel"/> identifier.
/// </param>
/// <param name="userId">
/// The <see cref="GuildMember"/> identifier.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that represents this asynchronous operation.
/// </returns>
Task RemoveThreadMemberAsync(Snowflake channelId, Snowflake userId, CancellationToken cancellationToken = default);

/// <summary>
/// Gets an array of <see cref="ThreadMember"/>s that are part of the <see cref="ThreadChannel"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#list-thread-members"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="ThreadChannel"/> identifier.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that contains an array of <see cref="ThreadMember"/>s that are part of the
/// specified <see cref="ThreadChannel"/>.
/// </returns>
Task<ThreadMember[]> ListThreadMembersAsync(Snowflake channelId, CancellationToken cancellationToken = default);

/// <summary>
/// Gets all active <see cref="ThreadChannel"/>s in the <see cref="Channel"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#list-active-threads"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that contains a <see cref="ThreadList"/>.
/// </returns>
Task<ThreadList> ListActiveThreadsAsync(Snowflake channelId, CancellationToken cancellationToken = default);

/// <summary>
/// Gets archived <see cref="ThreadChannel"/>s in the <see cref="Channel"/> that are public.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#list-public-archived-threads"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="args">
/// Parameters to include in the request.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that contains a <see cref="ThreadList"/>.
/// </returns>
Task<ThreadList> ListPublicArchivedThreadsAsync(Snowflake channelId, ListPublicArchivedThreadsParams args, CancellationToken cancellationToken = default);

/// <summary>
/// Gets archived <see cref="ThreadChannel"/>s in the <see cref="Channel"/> that are private.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#list-private-archived-threads"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="args">
/// Parameters to include in the request.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that contains a <see cref="ThreadList"/>.
/// </returns>
Task<ThreadList> ListPrivateArchivedThreadsAsync(Snowflake channelId, ListPrivateArchivedThreadsParams args, CancellationToken cancellationToken = default);

/// <summary>
/// Gets archived <see cref="ThreadChannel"/>s in the <see cref="Channel"/> that are
/// private and the current user has joined.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#list-joined-private-archived-threads"/>
/// </remarks>
/// <param name="channelId">
/// The <see cref="Channel"/> identifier.
/// </param>
/// <param name="args">
/// Parameters to include in the request.
/// </param>
/// <param name="cancellationToken">
/// Cancellation token for the request.
/// </param>
/// <returns>
/// A task that contains a <see cref="ThreadList"/>.
/// </returns>
Task<ThreadList> ListJoinedPrivateArchivedThreadsAsync(Snowflake channelId, ListJoinedPrivateArchivedThreadsParams args, CancellationToken cancellationToken = default);

#endregion Channel


}
}

src/Rest/Net/Requests/AuditLog/GetGuildAuditLogParams.cs → src/Rest/Net/Requests/AuditLogs/GetGuildAuditLogParams.cs View File

@@ -8,7 +8,7 @@ namespace Discord.Net.Rest
public record GetGuildAuditLogParams
{
/// <summary>
/// Filter the log for actions made by a user.
/// Filter the log for actions made by a <see cref="User"/>.
/// </summary>
public Optional<Snowflake> UserId { get; set; }

@@ -36,6 +36,7 @@ namespace Discord.Net.Rest
public void Validate()
{
Preconditions.NotZero(UserId, nameof(UserId));
Preconditions.NotZero(Before, nameof(Before));
Preconditions.AtLeast(Limit, AuditLog.MinimumGetEntryAmount, nameof(Limit));
Preconditions.AtMost(Limit, AuditLog.MaximumGetEntryAmount, nameof(Limit));
}

+ 32
- 0
src/Rest/Net/Requests/Channels/EditChannelPermissionsParams.cs View File

@@ -0,0 +1,32 @@
using Discord.Net.Models;

namespace Discord.Net.Rest
{
/// <summary>
/// Parameters to add to the request.
/// </summary>
public record EditChannelPermissionsParams
{
/// <summary>
/// The bitwise value of all allowed permissions.
/// </summary>
public Permissions Allow { get; set; }

/// <summary>
/// The bitwise value of all disallowed permissions.
/// </summary>
public Permissions Deny { get; set; }

/// <summary>
/// Type of overwrite.
/// </summary>
public OverwriteType Type { get; set; }

/// <summary>
/// Validates the data.
/// </summary>
public void Validate()
{
}
}
}

+ 23
- 0
src/Rest/Net/Requests/Channels/FollowNewsChannelParams.cs View File

@@ -0,0 +1,23 @@
using Discord.Net.Models;

namespace Discord.Net.Rest
{
/// <summary>
/// Parameters to add to the request.
/// </summary>
public record FollowNewsChannelParams
{
/// <summary>
/// Id of target <see cref="Channel"/>.
/// </summary>
public Snowflake WebhookChannelId { get; set; }

/// <summary>
/// Validates the data.
/// </summary>
public void Validate()
{
Preconditions.NotZero(WebhookChannelId, nameof(WebhookChannelId));
}
}
}

+ 28
- 0
src/Rest/Net/Requests/Channels/GroupDMAddRecipientParams.cs View File

@@ -0,0 +1,28 @@
using Discord.Net.Models;

namespace Discord.Net.Rest
{
/// <summary>
/// Parameters to add to the request.
/// </summary>
public record GroupDMAddRecipientParams
{
/// <summary>
/// Access token of a <see cref="User"/> that has granted your app the gdm.join scope.
/// </summary>
public string? AccessToken { get; set; } // Required property candidate

/// <summary>
/// Nickname of the <see cref="User"/> being added.
/// </summary>
public string? Nick { get; set; } // Required property candidate

/// <summary>
/// Validates the data.
/// </summary>
public void Validate()
{
Preconditions.NotNullOrEmpty(AccessToken, nameof(AccessToken));
}
}
}

+ 27
- 0
src/Rest/Net/Requests/Channels/ListJoinedPrivateArchivedThreadsParams.cs View File

@@ -0,0 +1,27 @@
namespace Discord.Net.Rest
{
/// <summary>
/// Parameters to add to the request.
/// </summary>
public record ListJoinedPrivateArchivedThreadsParams
{
/// <summary>
/// Returns threads before this id.
/// </summary>
public Optional<Snowflake> Before { get; set; }

/// <summary>
/// Maximum number of threads to return.
/// </summary>
public Optional<int> Limit { get; set; }

/// <summary>
/// Validates the data.
/// </summary>
public void Validate()
{
Preconditions.NotZero(Before, nameof(Before));
Preconditions.NotZero(Limit, nameof(Limit));
}
}
}

+ 28
- 0
src/Rest/Net/Requests/Channels/ListPrivateArchivedThreadsParams.cs View File

@@ -0,0 +1,28 @@
using System;

namespace Discord.Net.Rest
{
/// <summary>
/// Parameters to add to the request.
/// </summary>
public record ListPrivateArchivedThreadsParams
{
/// <summary>
/// Returns threads before this timestamp.
/// </summary>
public Optional<DateTimeOffset> Before { get; set; }

/// <summary>
/// Maximum number of threads to return.
/// </summary>
public Optional<int> Limit { get; set; }

/// <summary>
/// Validates the data.
/// </summary>
public void Validate()
{
Preconditions.NotZero(Limit, nameof(Limit));
}
}
}

+ 28
- 0
src/Rest/Net/Requests/Channels/ListPublicArchivedThreadsParams.cs View File

@@ -0,0 +1,28 @@
using System;

namespace Discord.Net.Rest
{
/// <summary>
/// Parameters to add to the request.
/// </summary>
public record ListPublicArchivedThreadsParams
{
/// <summary>
/// Returns threads before this timestamp.
/// </summary>
public Optional<DateTimeOffset> Before { get; set; }

/// <summary>
/// Maximum number of threads to return.
/// </summary>
public Optional<int> Limit { get; set; }

/// <summary>
/// Validates the data.
/// </summary>
public void Validate()
{
Preconditions.NotZero(Limit, nameof(Limit));
}
}
}

+ 33
- 0
src/Rest/Net/Requests/Channels/ModifyGroupChannelParams.cs View File

@@ -0,0 +1,33 @@
using Discord.Net.Models;

namespace Discord.Net.Rest
{
/// <summary>
/// Parameters to include in a request to modify a <see cref="GroupChannel"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#modify-channel-json-params-group-dm"/>
/// </remarks>
public record ModifyGroupChannelParams
{
/// <summary>
/// <see cref="Channel"/> name.
/// </summary>
public Optional<string> Name { get; set; }

/// <summary>
/// Base64 encoded icon.
/// </summary>
public Optional<string> Icon { get; set; }

/// <summary>
/// Validates the data.
/// </summary>
public void Validate()
{
Preconditions.NotNullOrWhitespace(Name!, nameof(Name));
Preconditions.LengthAtLeast(Name!, Channel.MinChannelNameLength, nameof(Name));
Preconditions.LengthAtMost(Name!, Channel.MaxChannelNameLength, nameof(Name));
}
}
}

+ 101
- 0
src/Rest/Net/Requests/Channels/ModifyGuildChannelParams.cs View File

@@ -0,0 +1,101 @@
using Discord.Net.Models;

namespace Discord.Net.Rest
{
/// <summary>
/// Parameters to include in a request to modify a <see cref="GuildChannel"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#modify-channel-json-params-guild-channel"/>
/// </remarks>
public record ModifyGuildChannelParams
{
/// <summary>
/// <see cref="Channel"/> name.
/// </summary>
public Optional<string> Name { get; set; }

/// <summary>
/// The type of <see cref="Channel"/>.
/// </summary>
/// <remarks>
/// Only conversion between text and news is supported and only in guilds with the "NEWS" feature.
/// </remarks>
public Optional<ChannelType> Type { get; set; }

/// <summary>
/// The position of the <see cref="Channel"/> in the left-hand listing.
/// </summary>
public Optional<int?> Position { get; set; }

/// <summary>
/// <see cref="Channel"/> topic.
/// </summary>
public Optional<string?> Topic { get; set; }

/// <summary>
/// Whether the <see cref="Channel"/> is nsfw.
/// </summary>
public Optional<bool?> Nsfw { get; set; }

/// <summary>
/// Amount of seconds a <see cref="User"/> has to wait before sending another message;
/// bots, as well as <see cref="User"/>s with the permission <see cref="Permissions.ManageMessages"/>
/// or <see cref="Permissions.ManageChannels"/>, are unaffected.
/// </summary>
public Optional<int?> RateLimitPerUser { get; set; }

/// <summary>
/// The bitrate (in bits) of the <see cref="VoiceChannel"/>.
/// </summary>
public Optional<int?> Bitrate { get; set; }

/// <summary>
/// The <see cref="User"/> limit of the <see cref="VoiceChannel"/>; 0 refers to no limit,
/// 1 to 99 refers to a <see cref="User"/> limit.
/// </summary>
public Optional<int?> UserLimit { get; set; }

/// <summary>
/// <see cref="Channel"/> or category-specific permissions.
/// </summary>
public Optional<Overwrite[]?> PermissionOverwrites { get; set; }

/// <summary>
/// Id of the new parent <see cref="CategoryChannel"/> for a <see cref="Channel"/>.
/// </summary>
public Optional<Snowflake?> ParentId { get; set; }

/// <summary>
/// <see cref="Channel"/> voice region id, automatic when set to <see langword="null"/>.
/// </summary>
public Optional<string?> RtcRegion { get; set; }

/// <summary>
/// The camera video quality mode of the <see cref="VoiceChannel"/>.
/// </summary>
public Optional<int?> VideoQualityMode { get; set; }

/// <summary>
/// Validates the data.
/// </summary>
public void Validate()
{
Preconditions.NotNullOrWhitespace(Name!, nameof(Name));
Preconditions.LengthAtLeast(Name!, Channel.MinChannelNameLength, nameof(Name));
Preconditions.LengthAtMost(Name!, Channel.MaxChannelNameLength, nameof(Name));
Preconditions.MustBeOneOf(Type, new[] { ChannelType.GuildText, ChannelType.GuildNews }, nameof(Type));
Preconditions.NotNegative(Position, nameof(Position));
Preconditions.NotNull(Topic, nameof(Topic));
Preconditions.LengthAtLeast(Topic, Channel.MinChannelTopicLength, nameof(Topic));
Preconditions.LengthAtMost(Topic, Channel.MaxChannelTopicLength, nameof(Topic));
Preconditions.AtLeast(RateLimitPerUser, Channel.MinRateLimitPerUserDuration, nameof(RateLimitPerUser));
Preconditions.AtMost(RateLimitPerUser, Channel.MaxRateLimitPerUserDuration, nameof(RateLimitPerUser));
Preconditions.AtLeast(Bitrate, Channel.MinBitrate, nameof(Bitrate));
Preconditions.AtMost(Bitrate, Channel.MaxBitrate, nameof(Bitrate));
Preconditions.AtLeast(UserLimit, Channel.MinUserLimit, nameof(UserLimit));
Preconditions.AtMost(UserLimit, Channel.MaxUserLimit, nameof(UserLimit));
Preconditions.NotNull(PermissionOverwrites, nameof(PermissionOverwrites));
}
}
}

+ 57
- 0
src/Rest/Net/Requests/Channels/ModifyThreadChannelParams.cs View File

@@ -0,0 +1,57 @@
using Discord.Net.Models;

namespace Discord.Net.Rest
{
/// <summary>
/// Parameters to include in a request to modify a <see cref="ThreadChannel"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#modify-channel-json-params-guild-channel"/>
/// </remarks>
public record ModifyThreadChannelParams
{
/// <summary>
/// <see cref="ThreadChannel"/> name.
/// </summary>
public Optional<string> Name { get; set; }

/// <summary>
/// Whether the <see cref="ThreadChannel"/> is archived.
/// </summary>
public Optional<bool> Archived { get; set; }

/// <summary>
/// Duration in minutes to automatically archive the <see cref="ThreadChannel"/> after recent activity.
/// </summary>
/// <remarks>
/// Can be set to 60, 1440, 4320, or 10080.
/// </remarks>
public Optional<int> AutoArchiveDuration { get; set; }

/// <summary>
/// When a <see cref="ThreadChannel"/> is locked, only <see cref="User"/>s with
/// <see cref="Permissions.ManageThreads"/> can unarchive it.
/// </summary>
public Optional<bool> Locked { get; set; }

/// <summary>
/// Amount of seconds a <see cref="User"/> has to wait before sending another message;
/// bots, as well as <see cref="User"/>s with the permission <see cref="Permissions.ManageMessages"/>,
/// <see cref="Permissions.ManageThreads"/>, or <see cref="Permissions.ManageChannels"/>, are unaffected.
/// </summary>
public Optional<int?> RateLimitPerUser { get; set; }

/// <summary>
/// Validates the data.
/// </summary>
public void Validate()
{
Preconditions.NotNullOrWhitespace(Name!, nameof(Name));
Preconditions.LengthAtLeast(Name!, Channel.MinChannelNameLength, nameof(Name));
Preconditions.LengthAtMost(Name!, Channel.MaxChannelNameLength, nameof(Name));
Preconditions.MustBeOneOf(AutoArchiveDuration, new[] { 60, 1440, 4320, 10080 }, nameof(AutoArchiveDuration));
Preconditions.AtLeast(RateLimitPerUser, Channel.MinRateLimitPerUserDuration, nameof(RateLimitPerUser));
Preconditions.AtMost(RateLimitPerUser, Channel.MaxRateLimitPerUserDuration, nameof(RateLimitPerUser));
}
}
}

+ 37
- 0
src/Rest/Net/Requests/Channels/StartThreadWithMessageParams.cs View File

@@ -0,0 +1,37 @@
using Discord.Net.Models;

namespace Discord.Net.Rest
{
/// <summary>
/// Parameters to add to the request.
/// </summary>
public record StartThreadWithMessageParams
{
/// <summary>
/// <see cref="ThreadChannel"/> name.
/// </summary>
/// <remarks>
/// Must be between 1-100, inclusive.
/// </remarks>
public string? Name { get; set; } // Required property candidate

/// <summary>
/// Duration in minutes to automatically archive the thread after recent activity.
/// </summary>
/// <remarks>
/// Can be set to: 60, 1440, 4320, 10080.
/// </remarks>
public int AutoArchiveDuration { get; set; }

/// <summary>
/// Validates the data.
/// </summary>
public void Validate()
{
Preconditions.NotNullOrWhitespace(Name!, nameof(Name));
Preconditions.LengthAtLeast(Name!, Channel.MinChannelNameLength, nameof(Name));
Preconditions.LengthAtMost(Name!, Channel.MaxChannelNameLength, nameof(Name));
Preconditions.MustBeOneOf(AutoArchiveDuration, new[] { 60, 1440, 4320, 10080 }, nameof(AutoArchiveDuration));
}
}
}

+ 37
- 0
src/Rest/Net/Requests/Channels/StartThreadWithoutMessageParams.cs View File

@@ -0,0 +1,37 @@
using Discord.Net.Models;

namespace Discord.Net.Rest
{
/// <summary>
/// Parameters to add to the request.
/// </summary>
public record StartThreadWithoutMessageParams
{
/// <summary>
/// <see cref="ThreadChannel"/> name.
/// </summary>
/// <remarks>
/// Must be between 1-100, inclusive.
/// </remarks>
public string? Name { get; set; } // Required property candidate

/// <summary>
/// Duration in minutes to automatically archive the thread after recent activity.
/// </summary>
/// <remarks>
/// Can be set to: 60, 1440, 4320, 10080.
/// </remarks>
public int AutoArchiveDuration { get; set; }

/// <summary>
/// Validates the data.
/// </summary>
public void Validate()
{
Preconditions.NotNullOrWhitespace(Name!, nameof(Name));
Preconditions.LengthAtLeast(Name!, Channel.MinChannelNameLength, nameof(Name));
Preconditions.LengthAtMost(Name!, Channel.MaxChannelNameLength, nameof(Name));
Preconditions.MustBeOneOf(AutoArchiveDuration, new[] { 60, 1440, 4320, 10080 }, nameof(AutoArchiveDuration));
}
}
}

+ 73
- 0
src/Rest/Net/Requests/Invites/CreateChannelInviteParams.cs View File

@@ -0,0 +1,73 @@
using Discord.Net.Models;

namespace Discord.Net.Rest
{
/// <summary>
/// Parameters to add to the request.
/// </summary>
public record CreateChannelInviteParams
{
/// <summary>
/// Duration of <see cref="Invite"/> in seconds before expiry.
/// </summary>
/// <remarks>
/// Must be between 0 and 604800 (7 days), inclusive. Zero means never.
/// </remarks>
public Optional<int> MaxAge { get; set; }

/// <summary>
/// Maximum number of uses.
/// </summary>
/// <remarks>
/// Must be between 0 and 100, inclusive. Zero means unlimited.
/// </remarks>
public Optional<int> MaxUses { get; set; }

/// <summary>
/// Whether this <see cref="Invite"/> only grants temporary membership.
/// </summary>
public Optional<bool> Temporary { get; set; }

/// <summary>
/// If true, don't try to reuse a similar <see cref="Invite"/> (useful for
/// creating many unique one time use invites).
/// </summary>
public Optional<bool> Unique { get; set; }

/// <summary>
/// The type of target for this <see cref="VoiceChannel"/> <see cref="Invite"/>.
/// </summary>
public Optional<InviteTargetType> TargetType { get; set; }

/// <summary>
/// The id of the <see cref="User"/> whose stream to display for this <see cref="Invite"/>.
/// </summary>
/// <remarks>
/// Required if <see cref="TargetType"/> is <see cref="InviteTargetType.Stream"/>,
/// the <see cref="User"/> must be streaming in the <see cref="Channel"/>.
/// </remarks>
public Optional<Snowflake> TargetUserId { get; set; }

/// <summary>
/// The id of the embedded application to open for this <see cref="Invite"/>.
/// </summary>
/// <remarks>
/// Required if <see cref="TargetType"/> is <see cref="InviteTargetType.EmbeddedApplication"/>,
/// the application must have the <see cref="ApplicationFlags.Embedded"/> flag.
/// </remarks>
public Optional<Snowflake> TargetApplicationId { get; set; }

/// <summary>
/// Validates the data.
/// </summary>
public void Validate()
{
Preconditions.AtLeast(MaxAge, InviteWithMetadata.MinAgeTime, nameof(MaxAge));
Preconditions.AtMost(MaxAge, InviteWithMetadata.MaxAgeTime, nameof(MaxAge));
Preconditions.AtLeast(MaxUses, InviteWithMetadata.MinUseCount, nameof(MaxUses));
Preconditions.AtMost(MaxUses, InviteWithMetadata.MaxUseCount, nameof(MaxUses));
Preconditions.NotZero(TargetUserId, nameof(TargetUserId));
Preconditions.NotZero(TargetApplicationId, nameof(TargetApplicationId));
}
}
}

+ 27
- 0
src/Rest/Net/Requests/Messages/BulkDeleteMessagesParams.cs View File

@@ -0,0 +1,27 @@
using Discord.Net.Models;

namespace Discord.Net.Rest
{
/// <summary>
/// Parameters to add to the request.
/// </summary>
public record BulkDeleteMessagesParams
{
/// <summary>
/// An array of <see cref="Message"/> ids to delete.
/// </summary>
/// <remarks>
/// Length must be between 2 and 100, inclusive.
/// </remarks>
public Snowflake[]? Messages { get; set; } // Required property candidate

/// <summary>
/// Validates the data.
/// </summary>
public void Validate()
{
Preconditions.LengthAtLeast(Messages!, Message.MinBulkDeleteAmount, nameof(Messages));
Preconditions.LengthAtMost(Messages!, Message.MaxBulkDeleteAmount, nameof(Messages));
}
}
}

+ 71
- 0
src/Rest/Net/Requests/Messages/CreateMessageParams.cs View File

@@ -0,0 +1,71 @@
using Discord.Net.Models;

namespace Discord.Net.Rest
{
/// <summary>
/// Parameters to include in a request to create a <see cref="Message"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#create-message-jsonform-params"/>
/// </remarks>
public record CreateMessageParams
{
/// <summary>
/// The <see cref="Message"/> content.
/// </summary>
/// <remarks>
/// Up to 2000 characters.
/// </remarks>
public Optional<string> Content { get; set; }

/// <summary>
/// Set the <see cref="Message"/> as a TTS one.
/// </summary>
public Optional<bool> Tts { get; set; }

/// <summary>
/// The contents of the file being sent.
/// </summary>
public Optional<MultipartFile> File { get; set; }

/// <summary>
/// Array of <see cref="Embed"/>s to include with the <see cref="Message"/>.
/// </summary>
/// <remarks>
/// Up to 10 <see cref="Embed"/>s.
/// </remarks>
public Optional<Embed[]> Embeds { get; set; }

/// <summary>
/// JSON encoded body of non-file params.
/// </summary>
/// <remarks>
/// Multipart/form-data only.
/// </remarks>
public Optional<CreateMessageParams> PayloadJson { get; set; } // TODO: Should change this to an easy way to convert to form data.

/// <summary>
/// Allowed mentions for the <see cref="Message"/>.
/// </summary>
public Optional<AllowedMentions> AlloweMentions { get; set; }

/// <summary>
/// Include to make your <see cref="Message"/> a reply.
/// </summary>
public Optional<MessageReference> MessageReference { get; set; }

/// <summary>
/// The <see cref="Component"/>s to include with the <see cref="Message"/>.
/// </summary>
public Optional<Component[]> Components { get; set; }

/// <summary>
/// Validates the data.
/// </summary>
public void Validate()
{
Preconditions.LengthAtMost(Content!, Message.MaxContentLength, nameof(Content));
Preconditions.LengthAtMost(Embeds!, Message.MaxEmbeds, nameof(Embeds));
}
}
}

+ 74
- 0
src/Rest/Net/Requests/Messages/EditMessageParams.cs View File

@@ -0,0 +1,74 @@
using Discord.Net.Models;

namespace Discord.Net.Rest
{
/// <summary>
/// Parameters to include in a request to edit a <see cref="Message"/>.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#edit-message-jsonform-params"/>
/// </remarks>
public record EditMessageParams
{
/// <summary>
/// The <see cref="Message"/> content.
/// </summary>
/// <remarks>
/// Up to 2000 characters.
/// </remarks>
public Optional<string?> Content { get; set; }

/// <summary>
/// Array of <see cref="Embed"/>s to include with the <see cref="Message"/>.
/// </summary>
/// <remarks>
/// Up to 10 <see cref="Embed"/>s.
/// </remarks>
public Optional<Embed[]?> Embeds { get; set; }

/// <summary>
/// Edit the <see cref="MessageFlags"/> of a <see cref="Message"/>.
/// </summary>
/// <remarks>
/// Only <see cref="MessageFlags.SuppressEmbeds"/> can currently be set/unset.
/// </remarks>
public Optional<MessageFlags?> Flags { get; set; }

/// <summary>
/// The contents of the file being sent.
/// </summary>
public Optional<MultipartFile?> File { get; set; }

/// <summary>
/// JSON encoded body of non-file params.
/// </summary>
/// <remarks>
/// Multipart/form-data only.
/// </remarks>
public Optional<CreateMessageParams?> PayloadJson { get; set; } // TODO: Should change this to an easy way to convert to form data.

/// <summary>
/// Attached files to keep.
/// </summary>
public Optional<Attachment[]?> Attachments { get; set; }

/// <summary>
/// Allowed mentions for the <see cref="Message"/>.
/// </summary>
public Optional<AllowedMentions?> AlloweMentions { get; set; }

/// <summary>
/// The <see cref="Component"/>s to include with the <see cref="Message"/>.
/// </summary>
public Optional<Component[]?> Components { get; set; }

/// <summary>
/// Validates the data.
/// </summary>
public void Validate()
{
Preconditions.LengthAtMost(Content!, Message.MaxContentLength, nameof(Content));
Preconditions.LengthAtMost(Embeds, Message.MaxEmbeds, nameof(Embeds));
}
}
}

+ 44
- 0
src/Rest/Net/Requests/Messages/GetChannelMessagesParams.cs View File

@@ -0,0 +1,44 @@
using Discord.Net.Models;

namespace Discord.Net.Rest
{
/// <summary>
/// Parameters to add to the request.
/// </summary>
public record GetChannelMessagesParams
{
/// <summary>
/// Get <see cref="Message"/>s around this <see cref="Message"/> ID.
/// </summary>
public Optional<Snowflake> Around { get; set; }

/// <summary>
/// Get <see cref="Message"/>s before this <see cref="Message"/> ID.
/// </summary>
public Optional<Snowflake> Before { get; set; }

/// <summary>
/// Get <see cref="Message"/>s after this <see cref="Message"/> ID.
/// </summary>
public Optional<Snowflake> After { get; set; }

/// <summary>
/// Maximum number of <see cref="Message"/>s to return.
/// </summary>
/// <remarks>
/// Default: 50. Acceptable range: 1-100, inclusive.
/// </remarks>
public Optional<int> Limit { get; set; }

/// <summary>
/// Validates the data.
/// </summary>
public void Validate()
{
Preconditions.NotZero(Before, nameof(Before));
Preconditions.Exclusive(new[] { Around, Before, After }, new[] { nameof(Around), nameof(Before), nameof(After) });
Preconditions.AtLeast(Limit, Channel.MinGetMessagesAmount, nameof(Limit));
Preconditions.AtMost(Limit, Channel.MaxGetMessagesAmount, nameof(Limit));
}
}
}

+ 33
- 0
src/Rest/Net/Requests/Messages/GetReactionsParams.cs View File

@@ -0,0 +1,33 @@
using Discord.Net.Models;

namespace Discord.Net.Rest
{
/// <summary>
/// Parameters to add to the request.
/// </summary>
public record GetReactionsParams
{
/// <summary>
/// Get <see cref="User"/>s after this <see cref="User"/> ID.
/// </summary>
public Optional<Snowflake> After { get; set; }

/// <summary>
/// Maximum number of <see cref="User"/>s to return.
/// </summary>
/// <remarks>
/// Default: 25. Acceptable range: 1-100, inclusive.
/// </remarks>
public Optional<int> Limit { get; set; }

/// <summary>
/// Validates the data.
/// </summary>
public void Validate()
{
Preconditions.NotZero(After, nameof(After));
Preconditions.AtLeast(Limit, Reaction.MinGetReactionsAmount, nameof(Limit));
Preconditions.AtMost(Limit, Reaction.MaxGetReactionsAmount, nameof(Limit));
}
}
}

+ 20
- 0
src/Rest/Net/Requests/MultipartFile.cs View File

@@ -0,0 +1,20 @@
using System.IO;

namespace Discord.Net.Rest
{
/// <summary>
/// Represents a multipart file to send in a request.
/// </summary>
public record MultipartFile
{
/// <summary>
/// The file stream.
/// </summary>
public Stream? Stream { get; set; } // Required property candidate

/// <summary>
/// The file name.
/// </summary>
public string? Filename { get; set; } // Required property candidate
}
}

+ 30
- 0
src/Rest/Net/Responses/ThreadList.cs View File

@@ -0,0 +1,30 @@
using Discord.Net.Models;

namespace Discord.Net.Rest
{
/// <summary>
/// Represents a response to list <see cref="ThreadChannel"/>s.
/// </summary>
/// <remarks>
/// <see href="https://discord.com/developers/docs/resources/channel#list-active-threads-response-body"/>
/// </remarks>
public record ThreadList
{
/// <summary>
/// Array of <see cref="ThreadChannel"/>s returned.
/// </summary>
public ThreadChannel[]? Threads { get; init; } // Required property candidate

/// <summary>
/// Array of <see cref="ThreadMember"/>s for each returned thread the current
/// user has joined.
/// </summary>
public ThreadMember[]? Members { get; init; } // Required property candidate

/// <summary>
/// Whether there are potentially additional <see cref="ThreadChannel"/>s that
/// could be returned on a subsequent call.
/// </summary>
public bool HasMore { get; init; }
}
}

+ 86
- 16
src/Rest/Preconditions.cs View File

@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace Discord.Net.Rest
{
@@ -6,8 +8,8 @@ namespace Discord.Net.Rest
{
// Objects

public static void NotNull<T>(T obj, string name, string? msg = null) where T : class { if (obj == null) throw CreateNotNullException(name, msg); }
public static void NotNull<T>(Optional<T> obj, string name, string? msg = null) where T : class { if (obj.IsSpecified && obj.Value == null) throw CreateNotNullException(name, msg); }
public static void NotNull<T>(T? obj, string name, string? msg = null) where T : class { if (obj == null) throw CreateNotNullException(name, msg); }
public static void NotNull<T>(Optional<T?> obj, string name, string? msg = null) where T : class { if (obj.IsSpecified && obj.Value == null) throw CreateNotNullException(name, msg); }

private static ArgumentNullException CreateNotNullException(string name, string? msg)
{
@@ -17,6 +19,27 @@ namespace Discord.Net.Rest
return new ArgumentNullException(name, msg);
}

public static void LengthAtLeast<T>(T[]? obj, int value, string name, string? msg = null)
{
if (obj?.Length < value)
{
if (msg == null)
throw new ArgumentException($"Length must be at least {value}", name);
else
throw new ArgumentException(msg, name);
}
}
public static void LengthAtMost<T>(T[]? obj, int value, string name, string? msg = null)
{
if (obj?.Length > value)
{
if (msg == null)
throw new ArgumentException($"Length must be at most {value}", name);
else
throw new ArgumentException(msg, name);
}
}

// Optionals

public static void Exclusive<T>(Optional<T>[] objs, string[] names, string? msg = null)
@@ -36,18 +59,39 @@ namespace Discord.Net.Rest
}
}

public static void LengthAtLeast<T>(Optional<T[]?> obj, int value, string name, string? msg = null)
{
if (obj.IsSpecified && obj.Value?.Length < value)
{
if (msg == null)
throw new ArgumentException($"Length must be at least {value}", name);
else
throw new ArgumentException(msg, name);
}
}
public static void LengthAtMost<T>(Optional<T[]?> obj, int value, string name, string? msg = null)
{
if (obj.IsSpecified && obj.Value?.Length > value)
{
if (msg == null)
throw new ArgumentException($"Length must be at most {value}", name);
else
throw new ArgumentException(msg, name);
}
}

// Strings

public static void NotEmpty(string obj, string name, string? msg = null) { if (obj != null && obj.Length == 0) throw CreateNotEmptyException(name, msg); }
public static void NotEmpty(Optional<string> obj, string name, string? msg = null) { if (obj.IsSpecified && obj.Value != null && obj.Value.Length == 0) throw CreateNotEmptyException(name, msg); }
public static void NotNullOrEmpty(string obj, string name, string? msg = null)
public static void NotEmpty(string? obj, string name, string? msg = null) { if (obj != null && obj.Length == 0) throw CreateNotEmptyException(name, msg); }
public static void NotEmpty(Optional<string?> obj, string name, string? msg = null) { if (obj.IsSpecified && obj.Value != null && obj.Value.Length == 0) throw CreateNotEmptyException(name, msg); }
public static void NotNullOrEmpty(string? obj, string name, string? msg = null)
{
if (obj == null)
throw CreateNotNullException(name, msg);
if (obj.Length == 0)
throw CreateNotEmptyException(name, msg);
}
public static void NotNullOrEmpty(Optional<string> obj, string name, string? msg = null)
public static void NotNullOrEmpty(Optional<string?> obj, string name, string? msg = null)
{
if (obj.IsSpecified)
{
@@ -57,14 +101,14 @@ namespace Discord.Net.Rest
throw CreateNotEmptyException(name, msg);
}
}
public static void NotNullOrWhitespace(string obj, string name, string? msg = null)
public static void NotNullOrWhitespace(string? obj, string name, string? msg = null)
{
if (obj == null)
throw CreateNotNullException(name, msg);
if (obj.Trim().Length == 0)
throw CreateNotEmptyException(name, msg);
}
public static void NotNullOrWhitespace(Optional<string> obj, string name, string? msg = null)
public static void NotNullOrWhitespace(Optional<string?> obj, string name, string? msg = null)
{
if (obj.IsSpecified)
{
@@ -75,7 +119,7 @@ namespace Discord.Net.Rest
}
}

public static void LengthAtLeast(string obj, int value, string name, string? msg = null)
public static void LengthAtLeast(string? obj, int value, string name, string? msg = null)
{
if (obj?.Length < value)
{
@@ -85,7 +129,7 @@ namespace Discord.Net.Rest
throw new ArgumentException(msg, name);
}
}
public static void LengthAtMost(string obj, int value, string name, string? msg = null)
public static void LengthAtMost(string? obj, int value, string name, string? msg = null)
{
if (obj?.Length > value)
{
@@ -95,7 +139,7 @@ namespace Discord.Net.Rest
throw new ArgumentException(msg, name);
}
}
public static void LengthGreaterThan(string obj, int value, string name, string? msg = null)
public static void LengthGreaterThan(string? obj, int value, string name, string? msg = null)
{
if (obj?.Length <= value)
{
@@ -105,7 +149,7 @@ namespace Discord.Net.Rest
throw new ArgumentException(msg, name);
}
}
public static void LengthLessThan(string obj, int value, string name, string? msg = null)
public static void LengthLessThan(string? obj, int value, string name, string? msg = null)
{
if (obj?.Length >= value)
{
@@ -115,25 +159,25 @@ namespace Discord.Net.Rest
throw new ArgumentException(msg, name);
}
}
public static void LengthAtLeast(Optional<string> obj, int value, string name, string? msg = null)
public static void LengthAtLeast(Optional<string?> obj, int value, string name, string? msg = null)
{
if (!obj.IsSpecified)
return;
LengthAtLeast(obj.Value, value, name, msg);
}
public static void LengthAtMost(Optional<string> obj, int value, string name, string? msg = null)
public static void LengthAtMost(Optional<string?> obj, int value, string name, string? msg = null)
{
if (!obj.IsSpecified)
return;
LengthAtMost(obj.Value, value, name, msg);
}
public static void LengthGreaterThan(Optional<string> obj, int value, string name, string? msg = null)
public static void LengthGreaterThan(Optional<string?> obj, int value, string name, string? msg = null)
{
if (!obj.IsSpecified)
return;
LengthGreaterThan(obj.Value, value, name, msg);
}
public static void LengthLessThan(Optional<string> obj, int value, string name, string? msg = null)
public static void LengthLessThan(Optional<string?> obj, int value, string name, string? msg = null)
{
if (!obj.IsSpecified)
return;
@@ -558,5 +602,31 @@ namespace Discord.Net.Rest
throw new ArgumentOutOfRangeException(name, msg);
}
}

// Structs

public static void MustBeOneOf<T>(T value, IEnumerable<T> allowed, string name, string? msg = null)
where T : struct
{
if (!allowed.Contains(value))
throw CreateMustBeOneOfException(name, msg, allowed);
}
public static void MustBeOneOf<T>(Optional<T> value, IEnumerable<T> allowed, string name, string? msg = null)
where T : struct
{
if (value.IsSpecified && !allowed.Contains(value.Value))
throw CreateMustBeOneOfException(name, msg, allowed);
}
private static ArgumentException CreateMustBeOneOfException<T>(string name, string? msg, IEnumerable<T> value)
where T : struct
{
if (msg == null)
{
var options = typeof(T).IsEnum ? string.Join(", ", value.Select(x => Enum.GetName(typeof(T), x))) : string.Join(", ", value);
return new ArgumentException($"Value must be one of the following: {options}", name);
}
else
return new ArgumentException(msg, name);
}
}
}

Loading…
Cancel
Save