diff --git a/src/Discord.Net.Core/Entities/Guilds/BulkBanResult.cs b/src/Discord.Net.Core/Entities/Guilds/BulkBanResult.cs new file mode 100644 index 00000000..8e702f0c --- /dev/null +++ b/src/Discord.Net.Core/Entities/Guilds/BulkBanResult.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; + +namespace Discord; + +/// +/// Represents a result of a bulk ban. +/// +public readonly struct BulkBanResult +{ + /// + /// Gets the collection of user IDs that were successfully banned. + /// + public IReadOnlyCollection BannedUsers { get; } + + /// + /// Gets the collection of user IDs that failed to be banned. + /// + public IReadOnlyCollection FailedUsers { get; } + + internal BulkBanResult(IReadOnlyCollection bannedUsers, IReadOnlyCollection failedUsers) + { + BannedUsers = bannedUsers; + FailedUsers = failedUsers; + } +} diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs index 143efd61..20bf3a7f 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs @@ -1418,5 +1418,16 @@ namespace Discord /// A task that represents the asynchronous creation operation. The task result contains the modified . /// Task ModifyIncidentActionsAsync(Action props, RequestOptions options = null); + + /// + /// Executes a bulk ban on the specified users. + /// + /// A collection of user ids to ban. + /// The number of seconds to delete messages for. Max 604800. + /// The options to be used when sending the request. + /// + /// A task that represents the asynchronous creation operation. The task result contains a . + /// + Task BulkBanAsync(IEnumerable userIds, int? deleteMessageSeconds = null, RequestOptions options = null); } } diff --git a/src/Discord.Net.Rest/API/Common/BulkBanResult.cs b/src/Discord.Net.Rest/API/Common/BulkBanResult.cs new file mode 100644 index 00000000..51e387e7 --- /dev/null +++ b/src/Discord.Net.Rest/API/Common/BulkBanResult.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Discord.API; + +internal class BulkBanResult +{ + [JsonProperty("banned_users")] + public ulong[] BannedUsers { get; set; } + + [JsonProperty("failed_users")] + public ulong[] FailedUsers { get; set; } +} diff --git a/src/Discord.Net.Rest/API/Rest/BulkBanParams.cs b/src/Discord.Net.Rest/API/Rest/BulkBanParams.cs new file mode 100644 index 00000000..e7c03378 --- /dev/null +++ b/src/Discord.Net.Rest/API/Rest/BulkBanParams.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Discord.API.Rest; + +internal class BulkBanParams +{ + [JsonProperty("user_ids")] + public ulong[] UserIds { get; set; } + + [JsonProperty("delete_message_seconds")] + public Optional DeleteMessageSeconds { get; set; } +} diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index c5f13314..4778f3ed 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -1771,6 +1771,23 @@ namespace Discord.API var ids = new BucketIds(guildId: guildId); return SendAsync("DELETE", () => $"guilds/{guildId}/bans/{userId}", ids, options: options); } + + public Task BulkBanAsync(ulong guildId, ulong[] userIds, int? deleteMessagesSeconds = null, RequestOptions options = null) + { + Preconditions.NotEqual(userIds.Length, 0, nameof(userIds)); + Preconditions.AtMost(userIds.Length, 200, nameof(userIds)); + Preconditions.AtMost(deleteMessagesSeconds ?? 0, 604800, nameof(deleteMessagesSeconds)); + + options = RequestOptions.CreateOrClone(options); + + var data = new BulkBanParams + { + DeleteMessageSeconds = deleteMessagesSeconds ?? Optional.Unspecified, + UserIds = userIds + }; + + return SendJsonAsync("POST", () => $"guilds/{guildId}/bulk-ban", data, new BucketIds(guildId), options: options); + } #endregion #region Guild Widget diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs index 407c52f8..d05352a0 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs @@ -244,6 +244,13 @@ namespace Discord.Rest public static Task RemoveBanAsync(IGuild guild, BaseDiscordClient client, ulong userId, RequestOptions options) => client.ApiClient.RemoveGuildBanAsync(guild.Id, userId, options); + + public static async Task BulkBanAsync(IGuild guild, BaseDiscordClient client, ulong[] userIds, int? deleteMessageSeconds, RequestOptions options) + { + var model = await client.ApiClient.BulkBanAsync(guild.Id, userIds, deleteMessageSeconds, options); + return new(model.BannedUsers?.ToImmutableArray() ?? ImmutableArray.Empty, + model.FailedUsers?.ToImmutableArray() ?? ImmutableArray.Empty); + } #endregion #region Channels diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs index 86dcafde..3ae3d17d 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -401,6 +401,10 @@ namespace Discord.Rest /// public Task RemoveBanAsync(ulong userId, RequestOptions options = null) => GuildHelper.RemoveBanAsync(this, Discord, userId, options); + + /// + public Task BulkBanAsync(IEnumerable userIds, int? deleteMessageSeconds = null, RequestOptions options = null) + => GuildHelper.BulkBanAsync(this, Discord, userIds.ToArray(), deleteMessageSeconds, options); #endregion #region Channels diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index 463ee7cd..b2b1e661 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -722,6 +722,10 @@ namespace Discord.WebSocket /// public Task RemoveBanAsync(ulong userId, RequestOptions options = null) => GuildHelper.RemoveBanAsync(this, Discord, userId, options); + + /// + public Task BulkBanAsync(IEnumerable userIds, int? deleteMessageSeconds = null, RequestOptions options = null) + => GuildHelper.BulkBanAsync(this, Discord, userIds.ToArray(), deleteMessageSeconds, options); #endregion #region Channels