diff --git a/src/Models/AuditLogs/AuditLogChangeKey.cs b/src/Models/AuditLogs/AuditLogChangeKey.cs index 2ea22b8fb..65acc9356 100644 --- a/src/Models/AuditLogs/AuditLogChangeKey.cs +++ b/src/Models/AuditLogs/AuditLogChangeKey.cs @@ -147,17 +147,17 @@ namespace Discord.Net.Models // /// - /// Text or voice position changed. + /// or position changed. /// Position, /// - /// Text topic changed. + /// topic changed. /// Topic, /// - /// Voice bitrate changed. + /// bitrate changed. /// Bitrate, @@ -297,7 +297,7 @@ namespace Discord.Net.Models // /// - /// New user limit in a voice . + /// New user limit in a . /// UserLimit, } diff --git a/src/Models/Channels/CategoryChannel.cs b/src/Models/Channels/CategoryChannel.cs new file mode 100644 index 000000000..617c21c6a --- /dev/null +++ b/src/Models/Channels/CategoryChannel.cs @@ -0,0 +1,9 @@ +namespace Discord.Net.Models +{ + /// + /// To be written - just a filler for xml docs for now + /// + public record CategoryChannel : GuildChannel + { + } +} diff --git a/src/Models/Channels/Channel.cs b/src/Models/Channels/Channel.cs index 9526e815f..4ebf077f2 100644 --- a/src/Models/Channels/Channel.cs +++ b/src/Models/Channels/Channel.cs @@ -11,6 +11,76 @@ namespace Discord.Net.Models /// public record Channel { + /// + /// Minimum langth of a channel name. + /// + public const int MinChannelNameLength = 1; + + /// + /// Maximum langth of a channel name. + /// + public const int MaxChannelNameLength = 100; + + /// + /// Minimum langth of a channel topic. + /// + public const int MinChannelTopicLength = 0; + + /// + /// Maximum langth of a channel topic. + /// + public const int MaxChannelTopicLength = 1024; + + /// + /// Minimum langth of a channel topic. + /// + public const int MinRateLimitPerUserDuration = 0; + + /// + /// Maximum langth of a channel topic. + /// + public const int MaxRateLimitPerUserDuration = 1024; + + /// + /// Minimum amount of users in channel. + /// + public const int MinUserLimit = 0; + + /// + /// Maximum amount of users in channel. + /// + public const int MaxUserLimit = 99; + + /// + /// Minimum bitrate for a channel. + /// + public const int MinBitrate = 8000; + + /// + /// Maximum bitrate for a channel. + /// + public const int MaxBitrate = 128000; + + /// + /// Minimum afk timeout duration for a channel. + /// + public const int MinAfkTimeoutDuration = 60; + + /// + /// Maximum afk timeout duration for a channel. + /// + public const int MaxAFkTimeoutDuration = 3600; + + /// + /// Minimum amount of messages to requests in a channel. + /// + public const int MinGetMessagesAmount = 1; + + /// + /// Maximum amount of messages to requests in a channel. + /// + public const int MaxGetMessagesAmount = 100; + /// /// The id of this . /// @@ -73,13 +143,13 @@ namespace Discord.Net.Models public Optional LastMessageId { get; init; } /// - /// The bitrate (in bits) of the voice . + /// The bitrate (in bits) of the . /// [JsonPropertyName("bitrate")] public Optional Bitrate { get; init; } /// - /// The limit of the voice . + /// The limit of the . /// [JsonPropertyName("user_limit")] public Optional UserLimit { get; init; } @@ -135,13 +205,13 @@ namespace Discord.Net.Models public Optional LastPinTimestamp { get; init; } /// - /// Voice region id for the voice , automatic when set to null. + /// Voice region id for the , automatic when set to null. /// [JsonPropertyName("rtc_region")] public Optional RtcRegion { get; init; } /// - /// The camera video quality mode of the voice channel, 1 when not present. + /// The camera video quality mode of the , 1 when not present. /// [JsonPropertyName("video_quality_mode")] public Optional VideoQualityMode { get; init; } diff --git a/src/Models/Channels/DMChannel.cs b/src/Models/Channels/DMChannel.cs new file mode 100644 index 000000000..1c746b90a --- /dev/null +++ b/src/Models/Channels/DMChannel.cs @@ -0,0 +1,9 @@ +namespace Discord.Net.Models +{ + /// + /// To be written - just a filler for xml docs for now + /// + public record DMChannel : PrivateChannel + { + } +} diff --git a/src/Models/Channels/GroupChannel.cs b/src/Models/Channels/GroupChannel.cs new file mode 100644 index 000000000..715078603 --- /dev/null +++ b/src/Models/Channels/GroupChannel.cs @@ -0,0 +1,9 @@ +namespace Discord.Net.Models +{ + /// + /// To be written - just a filler for xml docs for now + /// + public record GroupChannel : Channel + { + } +} diff --git a/src/Models/Channels/GuildChannel.cs b/src/Models/Channels/GuildChannel.cs new file mode 100644 index 000000000..9fc18bbfe --- /dev/null +++ b/src/Models/Channels/GuildChannel.cs @@ -0,0 +1,9 @@ +namespace Discord.Net.Models +{ + /// + /// To be written - just a filler for xml docs for now + /// + public record GuildChannel : Channel + { + } +} diff --git a/src/Models/Channels/GuildTextChannel.cs b/src/Models/Channels/GuildTextChannel.cs new file mode 100644 index 000000000..3f0dfbd09 --- /dev/null +++ b/src/Models/Channels/GuildTextChannel.cs @@ -0,0 +1,9 @@ +namespace Discord.Net.Models +{ + /// + /// To be written - just a filler for xml docs for now + /// + public record GuildTextChannel : GuildChannel + { + } +} diff --git a/src/Models/Channels/PrivateChannel.cs b/src/Models/Channels/PrivateChannel.cs new file mode 100644 index 000000000..688936f13 --- /dev/null +++ b/src/Models/Channels/PrivateChannel.cs @@ -0,0 +1,9 @@ +namespace Discord.Net.Models +{ + /// + /// To be written - just a filler for xml docs for now + /// + public record PrivateChannel : Channel + { + } +} diff --git a/src/Models/Channels/StageChannel.cs b/src/Models/Channels/StageChannel.cs new file mode 100644 index 000000000..35fac87c2 --- /dev/null +++ b/src/Models/Channels/StageChannel.cs @@ -0,0 +1,9 @@ +namespace Discord.Net.Models +{ + /// + /// To be written - just a filler for xml docs for now + /// + public record StageChannel : GuildChannel + { + } +} diff --git a/src/Models/Channels/StageInstance.cs b/src/Models/Channels/StageInstance.cs index 8279cb6f6..5263243b1 100644 --- a/src/Models/Channels/StageInstance.cs +++ b/src/Models/Channels/StageInstance.cs @@ -17,13 +17,13 @@ namespace Discord.Net.Models public Snowflake Id { get; } /// - /// The id of the associated Stage . + /// The id of the associated . /// [JsonPropertyName("guild_id")] public Snowflake GuildId { get; } /// - /// The id of the associated Stage . + /// The id of the associated . /// [JsonPropertyName("channel_id")] public Snowflake ChannelId { get; } diff --git a/src/Models/Channels/ThreadChannel.cs b/src/Models/Channels/ThreadChannel.cs new file mode 100644 index 000000000..1b5980937 --- /dev/null +++ b/src/Models/Channels/ThreadChannel.cs @@ -0,0 +1,9 @@ +namespace Discord.Net.Models +{ + /// + /// To be written - just a filler for xml docs for now + /// + public record ThreadChannel : GuildChannel + { + } +} diff --git a/src/Models/Channels/VoiceChannel.cs b/src/Models/Channels/VoiceChannel.cs new file mode 100644 index 000000000..e18fc0cb4 --- /dev/null +++ b/src/Models/Channels/VoiceChannel.cs @@ -0,0 +1,9 @@ +namespace Discord.Net.Models +{ + /// + /// To be written - just a filler for xml docs for now + /// + public record VoiceChannel : GuildChannel + { + } +} diff --git a/src/Models/Guilds/Guild.cs b/src/Models/Guilds/Guild.cs index 81ce601c1..4eb64b793 100644 --- a/src/Models/Guilds/Guild.cs +++ b/src/Models/Guilds/Guild.cs @@ -159,7 +159,7 @@ namespace Discord.Net.Models public int SystemChannelFlags { get; init; } /// - /// The id of the where Community s + /// The id of the where Community s. /// can display rules and/or guidelines. /// [JsonPropertyName("rules_channel_id")] @@ -190,7 +190,7 @@ namespace Discord.Net.Models public Optional MemberCount { get; init; } /// - /// States of s currently in voice s. + /// States of s currently in s. /// /// /// Lacks the . @@ -211,8 +211,8 @@ namespace Discord.Net.Models public Optional Channels { get; init; } /// - /// All active thread s in the that current - /// has permission to view. + /// All active s in the that the + /// current has permission to view. /// [JsonPropertyName("threads")] public Optional Threads { get; init; } diff --git a/src/Models/Guilds/GuildMember.cs b/src/Models/Guilds/GuildMember.cs index 5fa878c75..f024acb5f 100644 --- a/src/Models/Guilds/GuildMember.cs +++ b/src/Models/Guilds/GuildMember.cs @@ -42,13 +42,13 @@ namespace Discord.Net.Models public Optional PremiumSince { get; init; } /// - /// Whether the user is deafened in voice s. + /// Whether the user is deafened in s. /// [JsonPropertyName("deaf")] public bool Deaf { get; init; } /// - /// Whether the user is muted in voice s. + /// Whether the user is muted in s. /// [JsonPropertyName("mute")] public bool Mute { get; init; } diff --git a/src/Models/Invites/InviteMetadata.cs b/src/Models/Invites/InviteWithMetadata.cs similarity index 65% rename from src/Models/Invites/InviteMetadata.cs rename to src/Models/Invites/InviteWithMetadata.cs index 70aec1f3f..f8b81962b 100644 --- a/src/Models/Invites/InviteMetadata.cs +++ b/src/Models/Invites/InviteWithMetadata.cs @@ -9,8 +9,28 @@ namespace Discord.Net.Models /// /// /// - public record InviteMetadata : Invite + public record InviteWithMetadata : Invite { + /// + /// Minimum age for an invite in seconds. + /// + public const int MinAgeTime = 0; + + /// + /// Maximum age for an invite in seconds. + /// + public const int MaxAgeTime = 604800; + + /// + /// Minimum amount of uses for an invite. + /// + public const int MinUseCount = 0; + + /// + /// Maximum amount of uses for an invite. + /// + public const int MaxUseCount = 100; + /// /// Number of times this has been used. /// @@ -18,7 +38,7 @@ namespace Discord.Net.Models public int Uses { get; init; } /// - /// Max number of times this can be used. + /// Maximum number of times this can be used. /// [JsonPropertyName("max_uses")] public int MaxUses { get; init; } diff --git a/src/Models/Messages/Message.cs b/src/Models/Messages/Message.cs index aaad35b2b..ef9161878 100644 --- a/src/Models/Messages/Message.cs +++ b/src/Models/Messages/Message.cs @@ -11,6 +11,26 @@ namespace Discord.Net.Models /// public record Message { + /// + /// Maximum length of the message content. + /// + public const int MaxContentLength = 2000; + + /// + /// Maximum amount of embeds. + /// + public const int MaxEmbeds = 10; + + /// + /// Minimum amount of messages to bulk delete. + /// + public const int MinBulkDeleteAmount = 2; + + /// + /// Maximum amount of messages to bulk delete. + /// + public const int MaxBulkDeleteAmount = 100; + /// /// Id of the . /// @@ -182,7 +202,7 @@ namespace Discord.Net.Models public Optional Interaction { get; init; } /// - /// The thread that was started from this , + /// The that was started from this , /// includes s. /// [JsonPropertyName("thread")] diff --git a/src/Models/Messages/MessageType.cs b/src/Models/Messages/MessageType.cs index 6265b9f61..1c5fb5788 100644 --- a/src/Models/Messages/MessageType.cs +++ b/src/Models/Messages/MessageType.cs @@ -14,12 +14,12 @@ namespace Discord.Net.Models Default = 0, /// - /// Recipient was added to a group . + /// Recipient was added to a . /// RecipientAdd = 1, /// - /// Recipient was removed from a group . + /// Recipient was removed from a . /// RecipientRemove = 2, @@ -29,12 +29,12 @@ namespace Discord.Net.Models Call = 3, /// - /// Group name was changed. + /// name was changed. /// ChannelNameChange = 4, /// - /// Group icon was changed. + /// icon was changed. /// ChannelIconChange = 5, @@ -96,7 +96,7 @@ namespace Discord.Net.Models GuildDiscoveryGracePeriodFinalWarning = 17, /// - /// A thread was created. + /// A was created. /// ThreadCreated = 18, @@ -111,7 +111,7 @@ namespace Discord.Net.Models ApplicationCommand = 20, /// - /// Starter message for a thread . + /// Starter message for a . /// ThreadStarterMessage = 21, diff --git a/src/Models/Messages/Reaction.cs b/src/Models/Messages/Reaction.cs index 28b5306a0..20f94e211 100644 --- a/src/Models/Messages/Reaction.cs +++ b/src/Models/Messages/Reaction.cs @@ -10,6 +10,16 @@ namespace Discord.Net.Models /// public record Reaction { + /// + /// Minimum amount of users that reacted in a message. + /// + public const int MinGetReactionsAmount = 1; + + /// + /// Maximum amount of users that reacted in a message. + /// + public const int MaxGetReactionsAmount = 100; + /// /// Times this has been used to react. /// diff --git a/src/Rest/DiscordRestClient.cs b/src/Rest/DiscordRestClient.cs index ab5502cb6..fff0069a2 100644 --- a/src/Rest/DiscordRestClient.cs +++ b/src/Rest/DiscordRestClient.cs @@ -28,16 +28,357 @@ namespace Discord.Net #region Audit Log /// - public Task GetGuildAuditLogAsync(Snowflake guildId, GetGuildAuditLogParams? args = null, CancellationToken cancellationToken = default) + public Task 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 + + /// + public Task GetChannelAsync(Snowflake channelId, CancellationToken cancellationToken = default) + { + Preconditions.NotZero(channelId, nameof(channelId)); + return _api.GetChannelAsync(channelId, cancellationToken); + } + + /// + public Task 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); + } + + /// + public Task 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); + } + + /// + public Task 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); + } + + /// + public Task DeleteChannelAsync(Snowflake channelId, CancellationToken cancellationToken = default) + { + Preconditions.NotZero(channelId, nameof(channelId)); + return _api.DeleteChannelAsync(channelId, cancellationToken); + } + + /// + public Task 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); + } + + /// + public Task GetChannelMessageAsync(Snowflake channelId, Snowflake messageId, CancellationToken cancellationToken = default) + { + Preconditions.NotZero(channelId, nameof(channelId)); + Preconditions.NotZero(messageId, nameof(messageId)); + return _api.GetChannelMessageAsync(channelId, messageId, cancellationToken); + } + + /// + public Task 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); + } + + /// + public Task CrosspostMessageAsync(Snowflake channelId, Snowflake messageId, CancellationToken cancellationToken = default) + { + Preconditions.NotZero(channelId, nameof(channelId)); + Preconditions.NotZero(messageId, nameof(messageId)); + return _api.CrosspostMessageAsync(channelId, messageId, cancellationToken); + } + + /// + 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); + } + + /// + 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); + } + + /// + 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); + } + + /// + public Task 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); + } + + /// + 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); + } + + /// + 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); + } + + /// + public Task 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); + } + + /// + 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); + } + + /// + 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); + } + + /// + 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); + } + + /// + public Task GetChannelInvitesAsync(Snowflake channelId, CancellationToken cancellationToken = default) + { + Preconditions.NotZero(channelId, nameof(channelId)); + return _api.GetChannelInvitesAsync(channelId, cancellationToken); + } + + /// + public Task 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); + } + + /// + 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); + } + + /// + public Task 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); + } + + /// + public Task TriggerTypingIndicatorAsync(Snowflake channelId, CancellationToken cancellationToken = default) + { + Preconditions.NotZero(channelId, nameof(channelId)); + return _api.TriggerTypingIndicatorAsync(channelId, cancellationToken); + } + + /// + public Task GetPinnedMessagesAsync(Snowflake channelId, CancellationToken cancellationToken = default) + { + Preconditions.NotZero(channelId, nameof(channelId)); + return _api.GetPinnedMessagesAsync(channelId, cancellationToken); + } + + /// + 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); + } + + /// + 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); + } + + /// + 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); + } + + /// + 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); + } + + /// + public Task 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); + } + + /// + public Task 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); + } + + /// + public Task JoinThreadAsync(Snowflake channelId, CancellationToken cancellationToken = default) + { + Preconditions.NotZero(channelId, nameof(channelId)); + return _api.JoinThreadAsync(channelId, cancellationToken); + } + + /// + 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); + } + + /// + public Task LeaveThreadAsync(Snowflake channelId, CancellationToken cancellationToken = default) + { + Preconditions.NotZero(channelId, nameof(channelId)); + return _api.LeaveThreadAsync(channelId, cancellationToken); + } + + /// + 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); + } + + /// + public Task ListThreadMembersAsync(Snowflake channelId, CancellationToken cancellationToken = default) + { + Preconditions.NotZero(channelId, nameof(channelId)); + return _api.ListThreadMembersAsync(channelId, cancellationToken); + } + + /// + public Task ListActiveThreadsAsync(Snowflake channelId, CancellationToken cancellationToken = default) + { + Preconditions.NotZero(channelId, nameof(channelId)); + return _api.ListActiveThreadsAsync(channelId, cancellationToken); + } + + /// + public Task 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); + } + + /// + public Task 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); + } + + /// + public Task 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 } } diff --git a/src/Rest/DiscordRestException.cs b/src/Rest/DiscordRestException.cs new file mode 100644 index 000000000..1a6e9b840 --- /dev/null +++ b/src/Rest/DiscordRestException.cs @@ -0,0 +1,57 @@ +using System; +using System.Net; + +namespace Discord.Net.Rest +{ + /// + /// Represents errors received in response to rest requests. + /// + public class DiscordRestException : Exception + { + /// + /// HTTP status code returned. + /// + public HttpStatusCode HttpCode { get; } + + /// + /// Discord JSON error code. + /// + /// + /// + /// + public int? DiscordCode { get; } + + /// + /// Reason of this error. + /// + public string? Reason { get; } + + /// + /// Creates a with the provided error data. + /// + /// + /// HTTP status code. + /// + /// + /// Discord JSON error code. + /// + /// + /// Reason of this error. + /// + 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}"; + } + } +} diff --git a/src/Rest/Net/IDiscordRestApi.cs b/src/Rest/Net/IDiscordRestApi.cs index dc631785f..b980561a8 100644 --- a/src/Rest/Net/IDiscordRestApi.cs +++ b/src/Rest/Net/IDiscordRestApi.cs @@ -13,13 +13,13 @@ namespace Discord.Net.Rest #region Audit Log /// - /// Returns an audit log object for the guild. + /// Gets an for the . /// /// - /// Requires the permission. + /// /// /// - /// The guild identifier. + /// The identifier. /// /// /// Parameters to include in the request. @@ -27,8 +27,843 @@ namespace Discord.Net.Rest /// /// Cancellation token for the request. /// - Task GetGuildAuditLogAsync(Snowflake guildId, GetGuildAuditLogParams? args = null, CancellationToken cancellationToken = default); + /// + /// A task that contains an . + /// + Task GetGuildAuditLogAsync(Snowflake guildId, GetGuildAuditLogParams args, CancellationToken cancellationToken = default); #endregion + + #region Channel + + /// + /// Gets a by their identifier. + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that contains a if it exists; + /// otherwise, . + /// + Task GetChannelAsync(Snowflake channelId, CancellationToken cancellationToken = default); + + /// + /// Updates a 's settings. + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// Parameters to include in the request. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that contains the updated . + /// + /// + /// Thrown when invalid parameters are supplied in . + /// + Task ModifyChannelAsync(Snowflake channelId, ModifyGroupChannelParams args, CancellationToken cancellationToken = default); + + /// + /// Updates a 's settings. + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// Parameters to include in the request. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that contains the updated . + /// + /// + /// Thrown when invalid parameters are supplied in . + /// + Task ModifyChannelAsync(Snowflake channelId, ModifyGuildChannelParams args, CancellationToken cancellationToken = default); + + /// + /// Updates a 's settings. + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// Parameters to include in the request. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that contains the updated . + /// + /// + /// Thrown when invalid parameters are supplied in . + /// + Task ModifyChannelAsync(Snowflake channelId, ModifyThreadChannelParams args, CancellationToken cancellationToken = default); + + /// + /// Deletes a , or closes a . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that contains the deleted . + /// + Task DeleteChannelAsync(Snowflake channelId, CancellationToken cancellationToken = default); + + /// + /// Gets an array of s from a . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// Parameters to include in the request. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that contains an array of s from the . + /// + Task GetChannelMessagesAsync(Snowflake channelId, GetChannelMessagesParams args, CancellationToken cancellationToken = default); + + /// + /// Gets a specific from a . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// The identifier. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that contains the specified or . + /// + Task GetChannelMessageAsync(Snowflake channelId, Snowflake messageId, CancellationToken cancellationToken = default); + + /// + /// Sends a to a . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// Parameters to include in the request. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that contains the created. + /// + Task CreateMessageAsync(Snowflake channelId, CreateMessageParams args, CancellationToken cancellationToken = default); + + /// + /// Crossposts a in a News to following channels. + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// The identifier. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that contains the crossposted . + /// + Task CrosspostMessageAsync(Snowflake channelId, Snowflake messageId, CancellationToken cancellationToken = default); + + /// + /// Creates a for a . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// The identifier. + /// + /// + /// An . + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that represents this asynchronous operation. + /// + Task CreateReactionAsync(Snowflake channelId, Snowflake messageId, Emoji emoji, CancellationToken cancellationToken = default); + + /// + /// Deletes a the current user has made for the . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// The identifier. + /// + /// + /// An . + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that represents this asynchronous operation. + /// + Task DeleteOwnReactionAsync(Snowflake channelId, Snowflake messageId, Emoji emoji, CancellationToken cancellationToken = default); + + /// + /// Deletes another 's . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// The identifier. + /// + /// + /// The identifier. + /// + /// + /// An . + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that represents this asynchronous operation. + /// + Task DeleteUserReactionAsync(Snowflake channelId, Snowflake messageId, Snowflake userId, Emoji emoji, CancellationToken cancellationToken = default); + + /// + /// Gets an array of s that reacted with this . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// The identifier. + /// + /// + /// An . + /// + /// + /// Parameters to include in the request. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that contains an array of s that reacted with + /// the provided . + /// + Task GetReactionsAsync(Snowflake channelId, Snowflake messageId, Emoji emoji, GetReactionsParams args, CancellationToken cancellationToken = default); + + /// + /// Deletes all s on a . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// The identifier. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that represents this asynchronous operation. + /// + Task DeleteAllReactionsAsync(Snowflake channelId, Snowflake messageId, CancellationToken cancellationToken = default); + + /// + /// Deletes all the s for a given + /// on a . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// The identifier. + /// + /// + /// An . + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that represents this asynchronous operation. + /// + Task DeleteAllReactionsforEmojiAsync(Snowflake channelId, Snowflake messageId, Emoji emoji, CancellationToken cancellationToken = default); + + /// + /// Edits a previously sent . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// The identifier. + /// + /// + /// Parameters to include in the request. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that contains the updated . + /// + Task EditMessageAsync(Snowflake channelId, Snowflake messageId, EditMessageParams args, CancellationToken cancellationToken = default); + + /// + /// Deletes a message. + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// The identifier. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that represents this asynchronous operation. + /// + Task DeleteMessageAsync(Snowflake channelId, Snowflake messageId, CancellationToken cancellationToken = default); + + /// + /// Deletes multiple s in a single request. + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// Parameters to include in the request. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that represents this asynchronous operation. + /// + /// + /// Thrown when invalid parameters are supplied in . + /// + Task BulkDeleteMessagesAsync(Snowflake channelId, BulkDeleteMessagesParams args, CancellationToken cancellationToken = default); + + /// + /// Edits the permission s for a or . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// The identifier. + /// + /// + /// Parameters to include in the request. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that represents this asynchronous operation. + /// + Task EditChannelPermissionsAsync(Snowflake channelId, Snowflake overwriteId, EditChannelPermissionsParams args, CancellationToken cancellationToken = default); + + /// + /// Gets an array of s for a . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that contains an array of s. + /// + Task GetChannelInvitesAsync(Snowflake channelId, CancellationToken cancellationToken = default); + + /// + /// Creates a new for the . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// Parameters to include in the request. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that contains the created . + /// + Task CreateChannelInviteAsync(Snowflake channelId, CreateChannelInviteParams args, CancellationToken cancellationToken = default); + + /// + /// Deletes a permission for a or . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// The identifier. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that represents this asynchronous operation. + /// + Task DeleteChannelPermissionAsync(Snowflake channelId, Snowflake overwriteId, CancellationToken cancellationToken = default); + + /// + /// Follow a News to send s to a target . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// Parameters to include in the request. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that contains the . + /// + Task FollowNewsChannelAsync(Snowflake channelId, FollowNewsChannelParams args, CancellationToken cancellationToken = default); + + /// + /// Posts a typing indicator for the specified . + /// + /// + /// The identifier. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that represents this asynchronous operation. + /// + Task TriggerTypingIndicatorAsync(Snowflake channelId, CancellationToken cancellationToken = default); + + /// + /// Gets all pinned s in the . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that contains an array of all pinned s. + /// + Task GetPinnedMessagesAsync(Snowflake channelId, CancellationToken cancellationToken = default); + + /// + /// Pins a in a . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// The identifier. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that represents this asynchronous operation. + /// + Task PinMessageAsync(Snowflake channelId, Snowflake messageId, CancellationToken cancellationToken = default); + + /// + /// Unpins a in a . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// The identifier. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that represents this asynchronous operation. + /// + Task UnpinMessageAsync(Snowflake channelId, Snowflake messageId, CancellationToken cancellationToken = default); + + /// + /// Adds a recipient to a using their access token. + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// The identifier. + /// + /// + /// Parameters to include in the request. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that represents this asynchronous operation. + /// + Task GroupDMAddRecipientAsync(Snowflake channelId, Snowflake userId, GroupDMAddRecipientParams args, CancellationToken cancellationToken = default); + + /// + /// Removes a recipient from a . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// The identifier. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that represents this asynchronous operation. + /// + Task GroupDMRemoveRecipientAsync(Snowflake channelId, Snowflake userId, CancellationToken cancellationToken = default); + + /// + /// Creates a new from an existing . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// The identifier. + /// + /// + /// Parameters to include in the request. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that contains the created . + /// + /// + /// Thrown when invalid parameters are supplied in . + /// + Task StartThreadAsync(Snowflake channelId, Snowflake messageId, StartThreadWithMessageParams args, CancellationToken cancellationToken = default); + + /// + /// Creates a new private that is not connected to an existing . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// Parameters to include in the request. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that contains the created . + /// + /// + /// Thrown when invalid parameters are supplied in . + /// + Task StartThreadAsync(Snowflake channelId, StartThreadWithoutMessageParams args, CancellationToken cancellationToken = default); + + /// + /// Adds the current user to a . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that represents this asynchronous operation. + /// + Task JoinThreadAsync(Snowflake channelId, CancellationToken cancellationToken = default); + + /// + /// Adds another to a . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// The identifier. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that represents this asynchronous operation. + /// + Task AddThreadMemberAsync(Snowflake channelId, Snowflake userId, CancellationToken cancellationToken = default); + + /// + /// Removes the current user from a . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that represents this asynchronous operation. + /// + Task LeaveThreadAsync(Snowflake channelId, CancellationToken cancellationToken = default); + + /// + /// Removes another from a . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// The identifier. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that represents this asynchronous operation. + /// + Task RemoveThreadMemberAsync(Snowflake channelId, Snowflake userId, CancellationToken cancellationToken = default); + + /// + /// Gets an array of s that are part of the . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that contains an array of s that are part of the + /// specified . + /// + Task ListThreadMembersAsync(Snowflake channelId, CancellationToken cancellationToken = default); + + /// + /// Gets all active s in the . + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that contains a . + /// + Task ListActiveThreadsAsync(Snowflake channelId, CancellationToken cancellationToken = default); + + /// + /// Gets archived s in the that are public. + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// Parameters to include in the request. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that contains a . + /// + Task ListPublicArchivedThreadsAsync(Snowflake channelId, ListPublicArchivedThreadsParams args, CancellationToken cancellationToken = default); + + /// + /// Gets archived s in the that are private. + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// Parameters to include in the request. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that contains a . + /// + Task ListPrivateArchivedThreadsAsync(Snowflake channelId, ListPrivateArchivedThreadsParams args, CancellationToken cancellationToken = default); + + /// + /// Gets archived s in the that are + /// private and the current user has joined. + /// + /// + /// + /// + /// + /// The identifier. + /// + /// + /// Parameters to include in the request. + /// + /// + /// Cancellation token for the request. + /// + /// + /// A task that contains a . + /// + Task ListJoinedPrivateArchivedThreadsAsync(Snowflake channelId, ListJoinedPrivateArchivedThreadsParams args, CancellationToken cancellationToken = default); + + #endregion Channel + + } } diff --git a/src/Rest/Net/Requests/AuditLog/GetGuildAuditLogParams.cs b/src/Rest/Net/Requests/AuditLogs/GetGuildAuditLogParams.cs similarity index 90% rename from src/Rest/Net/Requests/AuditLog/GetGuildAuditLogParams.cs rename to src/Rest/Net/Requests/AuditLogs/GetGuildAuditLogParams.cs index b8a3e325d..170d1689b 100644 --- a/src/Rest/Net/Requests/AuditLog/GetGuildAuditLogParams.cs +++ b/src/Rest/Net/Requests/AuditLogs/GetGuildAuditLogParams.cs @@ -8,7 +8,7 @@ namespace Discord.Net.Rest public record GetGuildAuditLogParams { /// - /// Filter the log for actions made by a user. + /// Filter the log for actions made by a . /// public Optional 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)); } diff --git a/src/Rest/Net/Requests/Channels/EditChannelPermissionsParams.cs b/src/Rest/Net/Requests/Channels/EditChannelPermissionsParams.cs new file mode 100644 index 000000000..69f41b862 --- /dev/null +++ b/src/Rest/Net/Requests/Channels/EditChannelPermissionsParams.cs @@ -0,0 +1,32 @@ +using Discord.Net.Models; + +namespace Discord.Net.Rest +{ + /// + /// Parameters to add to the request. + /// + public record EditChannelPermissionsParams + { + /// + /// The bitwise value of all allowed permissions. + /// + public Permissions Allow { get; set; } + + /// + /// The bitwise value of all disallowed permissions. + /// + public Permissions Deny { get; set; } + + /// + /// Type of overwrite. + /// + public OverwriteType Type { get; set; } + + /// + /// Validates the data. + /// + public void Validate() + { + } + } +} diff --git a/src/Rest/Net/Requests/Channels/FollowNewsChannelParams.cs b/src/Rest/Net/Requests/Channels/FollowNewsChannelParams.cs new file mode 100644 index 000000000..172849f4f --- /dev/null +++ b/src/Rest/Net/Requests/Channels/FollowNewsChannelParams.cs @@ -0,0 +1,23 @@ +using Discord.Net.Models; + +namespace Discord.Net.Rest +{ + /// + /// Parameters to add to the request. + /// + public record FollowNewsChannelParams + { + /// + /// Id of target . + /// + public Snowflake WebhookChannelId { get; set; } + + /// + /// Validates the data. + /// + public void Validate() + { + Preconditions.NotZero(WebhookChannelId, nameof(WebhookChannelId)); + } + } +} diff --git a/src/Rest/Net/Requests/Channels/GroupDMAddRecipientParams.cs b/src/Rest/Net/Requests/Channels/GroupDMAddRecipientParams.cs new file mode 100644 index 000000000..428cce93c --- /dev/null +++ b/src/Rest/Net/Requests/Channels/GroupDMAddRecipientParams.cs @@ -0,0 +1,28 @@ +using Discord.Net.Models; + +namespace Discord.Net.Rest +{ + /// + /// Parameters to add to the request. + /// + public record GroupDMAddRecipientParams + { + /// + /// Access token of a that has granted your app the gdm.join scope. + /// + public string? AccessToken { get; set; } // Required property candidate + + /// + /// Nickname of the being added. + /// + public string? Nick { get; set; } // Required property candidate + + /// + /// Validates the data. + /// + public void Validate() + { + Preconditions.NotNullOrEmpty(AccessToken, nameof(AccessToken)); + } + } +} diff --git a/src/Rest/Net/Requests/Channels/ListJoinedPrivateArchivedThreadsParams.cs b/src/Rest/Net/Requests/Channels/ListJoinedPrivateArchivedThreadsParams.cs new file mode 100644 index 000000000..2f207ad35 --- /dev/null +++ b/src/Rest/Net/Requests/Channels/ListJoinedPrivateArchivedThreadsParams.cs @@ -0,0 +1,27 @@ +namespace Discord.Net.Rest +{ + /// + /// Parameters to add to the request. + /// + public record ListJoinedPrivateArchivedThreadsParams + { + /// + /// Returns threads before this id. + /// + public Optional Before { get; set; } + + /// + /// Maximum number of threads to return. + /// + public Optional Limit { get; set; } + + /// + /// Validates the data. + /// + public void Validate() + { + Preconditions.NotZero(Before, nameof(Before)); + Preconditions.NotZero(Limit, nameof(Limit)); + } + } +} diff --git a/src/Rest/Net/Requests/Channels/ListPrivateArchivedThreadsParams.cs b/src/Rest/Net/Requests/Channels/ListPrivateArchivedThreadsParams.cs new file mode 100644 index 000000000..73bd3f60f --- /dev/null +++ b/src/Rest/Net/Requests/Channels/ListPrivateArchivedThreadsParams.cs @@ -0,0 +1,28 @@ +using System; + +namespace Discord.Net.Rest +{ + /// + /// Parameters to add to the request. + /// + public record ListPrivateArchivedThreadsParams + { + /// + /// Returns threads before this timestamp. + /// + public Optional Before { get; set; } + + /// + /// Maximum number of threads to return. + /// + public Optional Limit { get; set; } + + /// + /// Validates the data. + /// + public void Validate() + { + Preconditions.NotZero(Limit, nameof(Limit)); + } + } +} diff --git a/src/Rest/Net/Requests/Channels/ListPublicArchivedThreadsParams.cs b/src/Rest/Net/Requests/Channels/ListPublicArchivedThreadsParams.cs new file mode 100644 index 000000000..70a0d0564 --- /dev/null +++ b/src/Rest/Net/Requests/Channels/ListPublicArchivedThreadsParams.cs @@ -0,0 +1,28 @@ +using System; + +namespace Discord.Net.Rest +{ + /// + /// Parameters to add to the request. + /// + public record ListPublicArchivedThreadsParams + { + /// + /// Returns threads before this timestamp. + /// + public Optional Before { get; set; } + + /// + /// Maximum number of threads to return. + /// + public Optional Limit { get; set; } + + /// + /// Validates the data. + /// + public void Validate() + { + Preconditions.NotZero(Limit, nameof(Limit)); + } + } +} diff --git a/src/Rest/Net/Requests/Channels/ModifyGroupChannelParams.cs b/src/Rest/Net/Requests/Channels/ModifyGroupChannelParams.cs new file mode 100644 index 000000000..5c8b04d5c --- /dev/null +++ b/src/Rest/Net/Requests/Channels/ModifyGroupChannelParams.cs @@ -0,0 +1,33 @@ +using Discord.Net.Models; + +namespace Discord.Net.Rest +{ + /// + /// Parameters to include in a request to modify a . + /// + /// + /// + /// + public record ModifyGroupChannelParams + { + /// + /// name. + /// + public Optional Name { get; set; } + + /// + /// Base64 encoded icon. + /// + public Optional Icon { get; set; } + + /// + /// Validates the data. + /// + public void Validate() + { + Preconditions.NotNullOrWhitespace(Name!, nameof(Name)); + Preconditions.LengthAtLeast(Name!, Channel.MinChannelNameLength, nameof(Name)); + Preconditions.LengthAtMost(Name!, Channel.MaxChannelNameLength, nameof(Name)); + } + } +} diff --git a/src/Rest/Net/Requests/Channels/ModifyGuildChannelParams.cs b/src/Rest/Net/Requests/Channels/ModifyGuildChannelParams.cs new file mode 100644 index 000000000..10859be77 --- /dev/null +++ b/src/Rest/Net/Requests/Channels/ModifyGuildChannelParams.cs @@ -0,0 +1,101 @@ +using Discord.Net.Models; + +namespace Discord.Net.Rest +{ + /// + /// Parameters to include in a request to modify a . + /// + /// + /// + /// + public record ModifyGuildChannelParams + { + /// + /// name. + /// + public Optional Name { get; set; } + + /// + /// The type of . + /// + /// + /// Only conversion between text and news is supported and only in guilds with the "NEWS" feature. + /// + public Optional Type { get; set; } + + /// + /// The position of the in the left-hand listing. + /// + public Optional Position { get; set; } + + /// + /// topic. + /// + public Optional Topic { get; set; } + + /// + /// Whether the is nsfw. + /// + public Optional Nsfw { get; set; } + + /// + /// Amount of seconds a has to wait before sending another message; + /// bots, as well as s with the permission + /// or , are unaffected. + /// + public Optional RateLimitPerUser { get; set; } + + /// + /// The bitrate (in bits) of the . + /// + public Optional Bitrate { get; set; } + + /// + /// The limit of the ; 0 refers to no limit, + /// 1 to 99 refers to a limit. + /// + public Optional UserLimit { get; set; } + + /// + /// or category-specific permissions. + /// + public Optional PermissionOverwrites { get; set; } + + /// + /// Id of the new parent for a . + /// + public Optional ParentId { get; set; } + + /// + /// voice region id, automatic when set to . + /// + public Optional RtcRegion { get; set; } + + /// + /// The camera video quality mode of the . + /// + public Optional VideoQualityMode { get; set; } + + /// + /// Validates the data. + /// + 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)); + } + } +} diff --git a/src/Rest/Net/Requests/Channels/ModifyThreadChannelParams.cs b/src/Rest/Net/Requests/Channels/ModifyThreadChannelParams.cs new file mode 100644 index 000000000..e66951d26 --- /dev/null +++ b/src/Rest/Net/Requests/Channels/ModifyThreadChannelParams.cs @@ -0,0 +1,57 @@ +using Discord.Net.Models; + +namespace Discord.Net.Rest +{ + /// + /// Parameters to include in a request to modify a . + /// + /// + /// + /// + public record ModifyThreadChannelParams + { + /// + /// name. + /// + public Optional Name { get; set; } + + /// + /// Whether the is archived. + /// + public Optional Archived { get; set; } + + /// + /// Duration in minutes to automatically archive the after recent activity. + /// + /// + /// Can be set to 60, 1440, 4320, or 10080. + /// + public Optional AutoArchiveDuration { get; set; } + + /// + /// When a is locked, only s with + /// can unarchive it. + /// + public Optional Locked { get; set; } + + /// + /// Amount of seconds a has to wait before sending another message; + /// bots, as well as s with the permission , + /// , or , are unaffected. + /// + public Optional RateLimitPerUser { get; set; } + + /// + /// Validates the data. + /// + 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)); + } + } +} diff --git a/src/Rest/Net/Requests/Channels/StartThreadWithMessageParams.cs b/src/Rest/Net/Requests/Channels/StartThreadWithMessageParams.cs new file mode 100644 index 000000000..9befa0a3f --- /dev/null +++ b/src/Rest/Net/Requests/Channels/StartThreadWithMessageParams.cs @@ -0,0 +1,37 @@ +using Discord.Net.Models; + +namespace Discord.Net.Rest +{ + /// + /// Parameters to add to the request. + /// + public record StartThreadWithMessageParams + { + /// + /// name. + /// + /// + /// Must be between 1-100, inclusive. + /// + public string? Name { get; set; } // Required property candidate + + /// + /// Duration in minutes to automatically archive the thread after recent activity. + /// + /// + /// Can be set to: 60, 1440, 4320, 10080. + /// + public int AutoArchiveDuration { get; set; } + + /// + /// Validates the data. + /// + 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)); + } + } +} diff --git a/src/Rest/Net/Requests/Channels/StartThreadWithoutMessageParams.cs b/src/Rest/Net/Requests/Channels/StartThreadWithoutMessageParams.cs new file mode 100644 index 000000000..b3b9bd363 --- /dev/null +++ b/src/Rest/Net/Requests/Channels/StartThreadWithoutMessageParams.cs @@ -0,0 +1,37 @@ +using Discord.Net.Models; + +namespace Discord.Net.Rest +{ + /// + /// Parameters to add to the request. + /// + public record StartThreadWithoutMessageParams + { + /// + /// name. + /// + /// + /// Must be between 1-100, inclusive. + /// + public string? Name { get; set; } // Required property candidate + + /// + /// Duration in minutes to automatically archive the thread after recent activity. + /// + /// + /// Can be set to: 60, 1440, 4320, 10080. + /// + public int AutoArchiveDuration { get; set; } + + /// + /// Validates the data. + /// + 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)); + } + } +} diff --git a/src/Rest/Net/Requests/Invites/CreateChannelInviteParams.cs b/src/Rest/Net/Requests/Invites/CreateChannelInviteParams.cs new file mode 100644 index 000000000..75f057113 --- /dev/null +++ b/src/Rest/Net/Requests/Invites/CreateChannelInviteParams.cs @@ -0,0 +1,73 @@ +using Discord.Net.Models; + +namespace Discord.Net.Rest +{ + /// + /// Parameters to add to the request. + /// + public record CreateChannelInviteParams + { + /// + /// Duration of in seconds before expiry. + /// + /// + /// Must be between 0 and 604800 (7 days), inclusive. Zero means never. + /// + public Optional MaxAge { get; set; } + + /// + /// Maximum number of uses. + /// + /// + /// Must be between 0 and 100, inclusive. Zero means unlimited. + /// + public Optional MaxUses { get; set; } + + /// + /// Whether this only grants temporary membership. + /// + public Optional Temporary { get; set; } + + /// + /// If true, don't try to reuse a similar (useful for + /// creating many unique one time use invites). + /// + public Optional Unique { get; set; } + + /// + /// The type of target for this . + /// + public Optional TargetType { get; set; } + + /// + /// The id of the whose stream to display for this . + /// + /// + /// Required if is , + /// the must be streaming in the . + /// + public Optional TargetUserId { get; set; } + + /// + /// The id of the embedded application to open for this . + /// + /// + /// Required if is , + /// the application must have the flag. + /// + public Optional TargetApplicationId { get; set; } + + /// + /// Validates the data. + /// + 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)); + } + } +} diff --git a/src/Rest/Net/Requests/Messages/BulkDeleteMessagesParams.cs b/src/Rest/Net/Requests/Messages/BulkDeleteMessagesParams.cs new file mode 100644 index 000000000..c14c1c370 --- /dev/null +++ b/src/Rest/Net/Requests/Messages/BulkDeleteMessagesParams.cs @@ -0,0 +1,27 @@ +using Discord.Net.Models; + +namespace Discord.Net.Rest +{ + /// + /// Parameters to add to the request. + /// + public record BulkDeleteMessagesParams + { + /// + /// An array of ids to delete. + /// + /// + /// Length must be between 2 and 100, inclusive. + /// + public Snowflake[]? Messages { get; set; } // Required property candidate + + /// + /// Validates the data. + /// + public void Validate() + { + Preconditions.LengthAtLeast(Messages!, Message.MinBulkDeleteAmount, nameof(Messages)); + Preconditions.LengthAtMost(Messages!, Message.MaxBulkDeleteAmount, nameof(Messages)); + } + } +} diff --git a/src/Rest/Net/Requests/Messages/CreateMessageParams.cs b/src/Rest/Net/Requests/Messages/CreateMessageParams.cs new file mode 100644 index 000000000..ef99e22ac --- /dev/null +++ b/src/Rest/Net/Requests/Messages/CreateMessageParams.cs @@ -0,0 +1,71 @@ +using Discord.Net.Models; + +namespace Discord.Net.Rest +{ + /// + /// Parameters to include in a request to create a . + /// + /// + /// + /// + public record CreateMessageParams + { + /// + /// The content. + /// + /// + /// Up to 2000 characters. + /// + public Optional Content { get; set; } + + /// + /// Set the as a TTS one. + /// + public Optional Tts { get; set; } + + /// + /// The contents of the file being sent. + /// + public Optional File { get; set; } + + /// + /// Array of s to include with the . + /// + /// + /// Up to 10 s. + /// + public Optional Embeds { get; set; } + + /// + /// JSON encoded body of non-file params. + /// + /// + /// Multipart/form-data only. + /// + public Optional PayloadJson { get; set; } // TODO: Should change this to an easy way to convert to form data. + + /// + /// Allowed mentions for the . + /// + public Optional AlloweMentions { get; set; } + + /// + /// Include to make your a reply. + /// + public Optional MessageReference { get; set; } + + /// + /// The s to include with the . + /// + public Optional Components { get; set; } + + /// + /// Validates the data. + /// + public void Validate() + { + Preconditions.LengthAtMost(Content!, Message.MaxContentLength, nameof(Content)); + Preconditions.LengthAtMost(Embeds!, Message.MaxEmbeds, nameof(Embeds)); + } + } +} diff --git a/src/Rest/Net/Requests/Messages/EditMessageParams.cs b/src/Rest/Net/Requests/Messages/EditMessageParams.cs new file mode 100644 index 000000000..81823f939 --- /dev/null +++ b/src/Rest/Net/Requests/Messages/EditMessageParams.cs @@ -0,0 +1,74 @@ +using Discord.Net.Models; + +namespace Discord.Net.Rest +{ + /// + /// Parameters to include in a request to edit a . + /// + /// + /// + /// + public record EditMessageParams + { + /// + /// The content. + /// + /// + /// Up to 2000 characters. + /// + public Optional Content { get; set; } + + /// + /// Array of s to include with the . + /// + /// + /// Up to 10 s. + /// + public Optional Embeds { get; set; } + + /// + /// Edit the of a . + /// + /// + /// Only can currently be set/unset. + /// + public Optional Flags { get; set; } + + /// + /// The contents of the file being sent. + /// + public Optional File { get; set; } + + /// + /// JSON encoded body of non-file params. + /// + /// + /// Multipart/form-data only. + /// + public Optional PayloadJson { get; set; } // TODO: Should change this to an easy way to convert to form data. + + /// + /// Attached files to keep. + /// + public Optional Attachments { get; set; } + + /// + /// Allowed mentions for the . + /// + public Optional AlloweMentions { get; set; } + + /// + /// The s to include with the . + /// + public Optional Components { get; set; } + + /// + /// Validates the data. + /// + public void Validate() + { + Preconditions.LengthAtMost(Content!, Message.MaxContentLength, nameof(Content)); + Preconditions.LengthAtMost(Embeds, Message.MaxEmbeds, nameof(Embeds)); + } + } +} diff --git a/src/Rest/Net/Requests/Messages/GetChannelMessagesParams.cs b/src/Rest/Net/Requests/Messages/GetChannelMessagesParams.cs new file mode 100644 index 000000000..a87cb8ca3 --- /dev/null +++ b/src/Rest/Net/Requests/Messages/GetChannelMessagesParams.cs @@ -0,0 +1,44 @@ +using Discord.Net.Models; + +namespace Discord.Net.Rest +{ + /// + /// Parameters to add to the request. + /// + public record GetChannelMessagesParams + { + /// + /// Get s around this ID. + /// + public Optional Around { get; set; } + + /// + /// Get s before this ID. + /// + public Optional Before { get; set; } + + /// + /// Get s after this ID. + /// + public Optional After { get; set; } + + /// + /// Maximum number of s to return. + /// + /// + /// Default: 50. Acceptable range: 1-100, inclusive. + /// + public Optional Limit { get; set; } + + /// + /// Validates the data. + /// + 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)); + } + } +} diff --git a/src/Rest/Net/Requests/Messages/GetReactionsParams.cs b/src/Rest/Net/Requests/Messages/GetReactionsParams.cs new file mode 100644 index 000000000..2f0db0bea --- /dev/null +++ b/src/Rest/Net/Requests/Messages/GetReactionsParams.cs @@ -0,0 +1,33 @@ +using Discord.Net.Models; + +namespace Discord.Net.Rest +{ + /// + /// Parameters to add to the request. + /// + public record GetReactionsParams + { + /// + /// Get s after this ID. + /// + public Optional After { get; set; } + + /// + /// Maximum number of s to return. + /// + /// + /// Default: 25. Acceptable range: 1-100, inclusive. + /// + public Optional Limit { get; set; } + + /// + /// Validates the data. + /// + public void Validate() + { + Preconditions.NotZero(After, nameof(After)); + Preconditions.AtLeast(Limit, Reaction.MinGetReactionsAmount, nameof(Limit)); + Preconditions.AtMost(Limit, Reaction.MaxGetReactionsAmount, nameof(Limit)); + } + } +} diff --git a/src/Rest/Net/Requests/MultipartFile.cs b/src/Rest/Net/Requests/MultipartFile.cs new file mode 100644 index 000000000..ecbd21058 --- /dev/null +++ b/src/Rest/Net/Requests/MultipartFile.cs @@ -0,0 +1,20 @@ +using System.IO; + +namespace Discord.Net.Rest +{ + /// + /// Represents a multipart file to send in a request. + /// + public record MultipartFile + { + /// + /// The file stream. + /// + public Stream? Stream { get; set; } // Required property candidate + + /// + /// The file name. + /// + public string? Filename { get; set; } // Required property candidate + } +} diff --git a/src/Rest/Net/Responses/ThreadList.cs b/src/Rest/Net/Responses/ThreadList.cs new file mode 100644 index 000000000..db009298f --- /dev/null +++ b/src/Rest/Net/Responses/ThreadList.cs @@ -0,0 +1,30 @@ +using Discord.Net.Models; + +namespace Discord.Net.Rest +{ + /// + /// Represents a response to list s. + /// + /// + /// + /// + public record ThreadList + { + /// + /// Array of s returned. + /// + public ThreadChannel[]? Threads { get; init; } // Required property candidate + + /// + /// Array of s for each returned thread the current + /// user has joined. + /// + public ThreadMember[]? Members { get; init; } // Required property candidate + + /// + /// Whether there are potentially additional s that + /// could be returned on a subsequent call. + /// + public bool HasMore { get; init; } + } +} diff --git a/src/Rest/Preconditions.cs b/src/Rest/Preconditions.cs index b6267663d..49f2664d2 100644 --- a/src/Rest/Preconditions.cs +++ b/src/Rest/Preconditions.cs @@ -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 obj, string name, string? msg = null) where T : class { if (obj == null) throw CreateNotNullException(name, msg); } - public static void NotNull(Optional obj, string name, string? msg = null) where T : class { if (obj.IsSpecified && obj.Value == null) throw CreateNotNullException(name, msg); } + public static void NotNull(T? obj, string name, string? msg = null) where T : class { if (obj == null) throw CreateNotNullException(name, msg); } + public static void NotNull(Optional 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[]? 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[]? 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(Optional[] objs, string[] names, string? msg = null) @@ -36,18 +59,39 @@ namespace Discord.Net.Rest } } + public static void LengthAtLeast(Optional 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(Optional 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 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 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 obj, string name, string? msg = null) + public static void NotNullOrEmpty(Optional 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 obj, string name, string? msg = null) + public static void NotNullOrWhitespace(Optional 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 obj, int value, string name, string? msg = null) + public static void LengthAtLeast(Optional obj, int value, string name, string? msg = null) { if (!obj.IsSpecified) return; LengthAtLeast(obj.Value, value, name, msg); } - public static void LengthAtMost(Optional obj, int value, string name, string? msg = null) + public static void LengthAtMost(Optional obj, int value, string name, string? msg = null) { if (!obj.IsSpecified) return; LengthAtMost(obj.Value, value, name, msg); } - public static void LengthGreaterThan(Optional obj, int value, string name, string? msg = null) + public static void LengthGreaterThan(Optional obj, int value, string name, string? msg = null) { if (!obj.IsSpecified) return; LengthGreaterThan(obj.Value, value, name, msg); } - public static void LengthLessThan(Optional obj, int value, string name, string? msg = null) + public static void LengthLessThan(Optional 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 value, IEnumerable allowed, string name, string? msg = null) + where T : struct + { + if (!allowed.Contains(value)) + throw CreateMustBeOneOfException(name, msg, allowed); + } + public static void MustBeOneOf(Optional value, IEnumerable 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(string name, string? msg, IEnumerable 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); + } } }