diff --git a/src/Discord.Net.Core/DiscordConfig.cs b/src/Discord.Net.Core/DiscordConfig.cs
index 006a1ca17..067c55225 100644
--- a/src/Discord.Net.Core/DiscordConfig.cs
+++ b/src/Discord.Net.Core/DiscordConfig.cs
@@ -97,6 +97,13 @@ namespace Discord
///
public const int MaxUsersPerBatch = 1000;
///
+ /// Returns the max bans allowed to be in a request.
+ ///
+ ///
+ /// The maximum number of bans that can be gotten per-batch.
+ ///
+ public const int MaxBansPerBatch = 1000;
+ ///
/// Returns the max users allowed to be in a request for guild event users.
///
///
diff --git a/src/Discord.Net.Core/Entities/Channels/Direction.cs b/src/Discord.Net.Core/Entities/Channels/Direction.cs
index efdf4ff42..4149617d8 100644
--- a/src/Discord.Net.Core/Entities/Channels/Direction.cs
+++ b/src/Discord.Net.Core/Entities/Channels/Direction.cs
@@ -1,10 +1,10 @@
namespace Discord
{
///
- /// Specifies the direction of where message(s) should be retrieved from.
+ /// Specifies the direction of where entities (e.g. bans/messages) should be retrieved from.
///
///
- /// This enum is used to specify the direction for retrieving messages.
+ /// This enum is used to specify the direction for retrieving entities.
///
/// At the time of writing, is not yet implemented into
/// .
@@ -15,15 +15,15 @@ namespace Discord
public enum Direction
{
///
- /// The message(s) should be retrieved before a message.
+ /// The entity(s) should be retrieved before an entity.
///
Before,
///
- /// The message(s) should be retrieved after a message.
+ /// The entity(s) should be retrieved after an entity.
///
After,
///
- /// The message(s) should be retrieved around a message.
+ /// The entity(s) should be retrieved around an entity.
///
Around
}
diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs
index b4625abbf..4706b629e 100644
--- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs
+++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs
@@ -409,17 +409,70 @@ namespace Discord
/// A task that represents the asynchronous leave operation.
///
Task LeaveAsync(RequestOptions options = null);
-
///
- /// Gets a collection of all users banned in this guild.
+ /// Gets amount of bans from the guild ordered by user ID.
///
+ ///
+ ///
+ /// The returned collection is an asynchronous enumerable object; one must call
+ /// to access the individual messages as a
+ /// collection.
+ ///
+ ///
+ /// Do not fetch too many bans at once! This may cause unwanted preemptive rate limit or even actual
+ /// rate limit, causing your bot to freeze!
+ ///
+ ///
+ /// The amount of bans to get from the guild.
/// The options to be used when sending the request.
///
- /// A task that represents the asynchronous get operation. The task result contains a read-only collection of
- /// ban objects that this guild currently possesses, with each object containing the user banned and reason
- /// behind the ban.
+ /// A paged collection of bans.
+ ///
+ IAsyncEnumerable> GetBansAsync(int limit = DiscordConfig.MaxBansPerBatch, RequestOptions options = null);
+ ///
+ /// Gets amount of bans from the guild starting at the provided ordered by user ID.
+ ///
+ ///
+ ///
+ /// The returned collection is an asynchronous enumerable object; one must call
+ /// to access the individual messages as a
+ /// collection.
+ ///
+ ///
+ /// Do not fetch too many bans at once! This may cause unwanted preemptive rate limit or even actual
+ /// rate limit, causing your bot to freeze!
+ ///
+ ///
+ /// The ID of the user to start to get bans from.
+ /// The direction of the bans to be gotten.
+ /// The number of bans to get.
+ /// The options to be used when sending the request.
+ ///
+ /// A paged collection of bans.
+ ///
+ IAsyncEnumerable> GetBansAsync(ulong fromUserId, Direction dir, int limit = DiscordConfig.MaxBansPerBatch, RequestOptions options = null);
+ ///
+ /// Gets amount of bans from the guild starting at the provided ordered by user ID.
+ ///
+ ///
+ ///
+ /// The returned collection is an asynchronous enumerable object; one must call
+ /// to access the individual messages as a
+ /// collection.
+ ///
+ ///
+ /// Do not fetch too many bans at once! This may cause unwanted preemptive rate limit or even actual
+ /// rate limit, causing your bot to freeze!
+ ///
+ ///
+ /// The user to start to get bans from.
+ /// The direction of the bans to be gotten.
+ /// The number of bans to get.
+ /// The options to be used when sending the request.
+ ///
+ /// A paged collection of bans.
///
- Task> GetBansAsync(RequestOptions options = null);
+ IAsyncEnumerable> GetBansAsync(IUser fromUser, Direction dir, int limit = DiscordConfig.MaxBansPerBatch, RequestOptions options = null);
///
/// Gets a ban object for a banned user.
///
diff --git a/src/Discord.Net.Rest/API/Rest/GetGuildBansParams.cs b/src/Discord.Net.Rest/API/Rest/GetGuildBansParams.cs
new file mode 100644
index 000000000..6a1e430c3
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Rest/GetGuildBansParams.cs
@@ -0,0 +1,9 @@
+namespace Discord.API.Rest
+{
+ internal class GetGuildBansParams
+ {
+ public Optional Limit { get; set; }
+ public Optional RelativeDirection { get; set; }
+ public Optional RelativeUserId { get; set; }
+ }
+}
diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs
index 645e6711c..3b829ee17 100644
--- a/src/Discord.Net.Rest/DiscordRestApiClient.cs
+++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs
@@ -1545,13 +1545,29 @@ namespace Discord.API
#endregion
#region Guild Bans
- public async Task> GetGuildBansAsync(ulong guildId, RequestOptions options = null)
+ public async Task> GetGuildBansAsync(ulong guildId, GetGuildBansParams args, RequestOptions options = null)
{
Preconditions.NotEqual(guildId, 0, nameof(guildId));
+ Preconditions.NotNull(args, nameof(args));
+ Preconditions.AtLeast(args.Limit, 0, nameof(args.Limit));
+ Preconditions.AtMost(args.Limit, DiscordConfig.MaxBansPerBatch, nameof(args.Limit));
options = RequestOptions.CreateOrClone(options);
+ int limit = args.Limit.GetValueOrDefault(DiscordConfig.MaxBansPerBatch);
+ ulong? relativeId = args.RelativeUserId.IsSpecified ? args.RelativeUserId.Value : (ulong?)null;
+ var relativeDir = args.RelativeDirection.GetValueOrDefault(Direction.Before) switch
+ {
+ Direction.After => "after",
+ Direction.Around => "around",
+ _ => "before",
+ };
var ids = new BucketIds(guildId: guildId);
- return await SendAsync>("GET", () => $"guilds/{guildId}/bans", ids, options: options).ConfigureAwait(false);
+ Expression> endpoint;
+ if (relativeId != null)
+ endpoint = () => $"guilds/{guildId}/bans?limit={limit}&{relativeDir}={relativeId}";
+ else
+ endpoint = () => $"guilds/{guildId}/bans?limit={limit}";
+ return await SendAsync>("GET", endpoint, ids, options: options).ConfigureAwait(false);
}
public async Task GetGuildBanAsync(ulong guildId, ulong userId, RequestOptions options)
{
diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs
index 7dbe20881..469e93db4 100644
--- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs
+++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs
@@ -142,12 +142,54 @@ namespace Discord.Rest
#endregion
#region Bans
- public static async Task> GetBansAsync(IGuild guild, BaseDiscordClient client,
- RequestOptions options)
+ public static IAsyncEnumerable> GetBansAsync(IGuild guild, BaseDiscordClient client,
+ ulong? fromUserId, Direction dir, int limit, RequestOptions options)
{
- var models = await client.ApiClient.GetGuildBansAsync(guild.Id, options).ConfigureAwait(false);
- return models.Select(x => RestBan.Create(client, x)).ToImmutableArray();
+ if (dir == Direction.Around && limit > DiscordConfig.MaxBansPerBatch)
+ {
+ int around = limit / 2;
+ if (fromUserId.HasValue)
+ return GetBansAsync(guild, client, fromUserId.Value + 1, Direction.Before, around + 1, options)
+ .Concat(GetBansAsync(guild, client, fromUserId.Value, Direction.After, around, options));
+ else
+ return GetBansAsync(guild, client, null, Direction.Before, around + 1, options);
+ }
+
+ return new PagedAsyncEnumerable(
+ DiscordConfig.MaxBansPerBatch,
+ async (info, ct) =>
+ {
+ var args = new GetGuildBansParams
+ {
+ RelativeDirection = dir,
+ Limit = info.PageSize
+ };
+ if (info.Position != null)
+ args.RelativeUserId = info.Position.Value;
+
+ var models = await client.ApiClient.GetGuildBansAsync(guild.Id, args, options).ConfigureAwait(false);
+ var builder = ImmutableArray.CreateBuilder();
+
+ foreach (var model in models)
+ builder.Add(RestBan.Create(client, model));
+
+ return builder.ToImmutable();
+ },
+ nextPage: (info, lastPage) =>
+ {
+ if (lastPage.Count != DiscordConfig.MaxMessagesPerBatch)
+ return false;
+ if (dir == Direction.Before)
+ info.Position = lastPage.Min(x => x.User.Id);
+ else
+ info.Position = lastPage.Max(x => x.User.Id);
+ return true;
+ },
+ start: fromUserId,
+ count: limit
+ );
}
+
public static async Task GetBanAsync(IGuild guild, BaseDiscordClient client, ulong userId, RequestOptions options)
{
var model = await client.ApiClient.GetGuildBanAsync(guild.Id, userId, options).ConfigureAwait(false);
diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
index d7ab65a55..92d598466 100644
--- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
+++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
@@ -333,17 +333,18 @@ namespace Discord.Rest
#endregion
#region Bans
- ///
- /// Gets a collection of all users banned in this guild.
- ///
- /// The options to be used when sending the request.
- ///
- /// A task that represents the asynchronous get operation. The task result contains a read-only collection of
- /// ban objects that this guild currently possesses, with each object containing the user banned and reason
- /// behind the ban.
- ///
- public Task> GetBansAsync(RequestOptions options = null)
- => GuildHelper.GetBansAsync(this, Discord, options);
+
+ ///
+ public IAsyncEnumerable> GetBansAsync(int limit = DiscordConfig.MaxBansPerBatch, RequestOptions options = null)
+ => GuildHelper.GetBansAsync(this, Discord, null, Direction.Before, limit, options);
+
+ ///
+ public IAsyncEnumerable> GetBansAsync(ulong fromUserId, Direction dir, int limit = DiscordConfig.MaxBansPerBatch, RequestOptions options = null)
+ => GuildHelper.GetBansAsync(this, Discord, fromUserId, dir, limit, options);
+
+ ///
+ public IAsyncEnumerable> GetBansAsync(IUser fromUser, Direction dir, int limit = DiscordConfig.MaxBansPerBatch, RequestOptions options = null)
+ => GuildHelper.GetBansAsync(this, Discord, fromUser.Id, dir, limit, options);
///
/// Gets a ban object for a banned user.
///
@@ -1193,22 +1194,24 @@ namespace Discord.Rest
IReadOnlyCollection IGuild.Roles => Roles;
IReadOnlyCollection IGuild.Stickers => Stickers;
-
///
async Task IGuild.CreateEventAsync(string name, DateTimeOffset startTime, GuildScheduledEventType type, GuildScheduledEventPrivacyLevel privacyLevel, string description, DateTimeOffset? endTime, ulong? channelId, string location, Image? coverImage, RequestOptions options)
=> await CreateEventAsync(name, startTime, type, privacyLevel, description, endTime, channelId, location, coverImage, options).ConfigureAwait(false);
-
///
async Task IGuild.GetEventAsync(ulong id, RequestOptions options)
=> await GetEventAsync(id, options).ConfigureAwait(false);
-
///
async Task> IGuild.GetEventsAsync(RequestOptions options)
=> await GetEventsAsync(options).ConfigureAwait(false);
-
///
- async Task> IGuild.GetBansAsync(RequestOptions options)
- => await GetBansAsync(options).ConfigureAwait(false);
+ IAsyncEnumerable> IGuild.GetBansAsync(int limit, RequestOptions options)
+ => GetBansAsync(limit, options);
+ ///
+ IAsyncEnumerable> IGuild.GetBansAsync(ulong fromUserId, Direction dir, int limit, RequestOptions options)
+ => GetBansAsync(fromUserId, dir, limit, options);
+ ///
+ IAsyncEnumerable> IGuild.GetBansAsync(IUser fromUser, Direction dir, int limit, RequestOptions options)
+ => GetBansAsync(fromUser, dir, limit, options);
///
async Task IGuild.GetBanAsync(IUser user, RequestOptions options)
=> await GetBanAsync(user, options).ConfigureAwait(false);
diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
index 47bd57552..49d2cd3bd 100644
--- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
+++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
@@ -621,17 +621,19 @@ namespace Discord.WebSocket
#endregion
#region Bans
- ///
- /// Gets a collection of all users banned in this guild.
- ///
- /// The options to be used when sending the request.
- ///
- /// A task that represents the asynchronous get operation. The task result contains a read-only collection of
- /// ban objects that this guild currently possesses, with each object containing the user banned and reason
- /// behind the ban.
- ///
- public Task> GetBansAsync(RequestOptions options = null)
- => GuildHelper.GetBansAsync(this, Discord, options);
+
+ ///
+ public IAsyncEnumerable> GetBansAsync(int limit = DiscordConfig.MaxBansPerBatch, RequestOptions options = null)
+ => GuildHelper.GetBansAsync(this, Discord, null, Direction.Before, limit, options);
+
+ ///
+ public IAsyncEnumerable> GetBansAsync(ulong fromUserId, Direction dir, int limit = DiscordConfig.MaxBansPerBatch, RequestOptions options = null)
+ => GuildHelper.GetBansAsync(this, Discord, fromUserId, dir, limit, options);
+
+ ///
+ public IAsyncEnumerable> GetBansAsync(IUser fromUser, Direction dir, int limit = DiscordConfig.MaxBansPerBatch, RequestOptions options = null)
+ => GuildHelper.GetBansAsync(this, Discord, fromUser.Id, dir, limit, options);
+
///
/// Gets a ban object for a banned user.
///
@@ -1810,8 +1812,14 @@ namespace Discord.WebSocket
async Task> IGuild.GetEventsAsync(RequestOptions options)
=> await GetEventsAsync(options).ConfigureAwait(false);
///
- async Task> IGuild.GetBansAsync(RequestOptions options)
- => await GetBansAsync(options).ConfigureAwait(false);
+ IAsyncEnumerable> IGuild.GetBansAsync(int limit, RequestOptions options)
+ => GetBansAsync(limit, options);
+ ///
+ IAsyncEnumerable> IGuild.GetBansAsync(ulong fromUserId, Direction dir, int limit, RequestOptions options)
+ => GetBansAsync(fromUserId, dir, limit, options);
+ ///
+ IAsyncEnumerable> IGuild.GetBansAsync(IUser fromUser, Direction dir, int limit, RequestOptions options)
+ => GetBansAsync(fromUser, dir, limit, options);
///
async Task IGuild.GetBanAsync(IUser user, RequestOptions options)
=> await GetBanAsync(user, options).ConfigureAwait(false);