Browse Source

feature: Update bans to support pagination (#2223)

* Cacheless impl

* Ignore cache impl

* Update src/Discord.Net.Core/Entities/Channels/Direction.cs

Co-authored-by: Quin Lynch <49576606+quinchs@users.noreply.github.com>

* Update src/Discord.Net.Core/Entities/Channels/Direction.cs

Co-authored-by: Quin Lynch <49576606+quinchs@users.noreply.github.com>

* Update src/Discord.Net.Core/Entities/Channels/Direction.cs

Co-authored-by: Quin Lynch <49576606+quinchs@users.noreply.github.com>

* Update src/Discord.Net.Core/Entities/Guilds/IGuild.cs

Co-authored-by: Quin Lynch <49576606+quinchs@users.noreply.github.com>

* Update src/Discord.Net.Core/Entities/Guilds/IGuild.cs

Co-authored-by: Quin Lynch <49576606+quinchs@users.noreply.github.com>

* Update src/Discord.Net.Core/Entities/Guilds/IGuild.cs

Co-authored-by: Quin Lynch <49576606+quinchs@users.noreply.github.com>

* Update src/Discord.Net.Core/Entities/Guilds/IGuild.cs

Co-authored-by: Quin Lynch <49576606+quinchs@users.noreply.github.com>

* Implement xmldoc consistency

Co-authored-by: Quin Lynch <49576606+quinchs@users.noreply.github.com>
tags/3.5.0
Armano den Boef GitHub 3 years ago
parent
commit
d8757a5afa
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 185 additions and 47 deletions
  1. +7
    -0
      src/Discord.Net.Core/DiscordConfig.cs
  2. +5
    -5
      src/Discord.Net.Core/Entities/Channels/Direction.cs
  3. +59
    -6
      src/Discord.Net.Core/Entities/Guilds/IGuild.cs
  4. +9
    -0
      src/Discord.Net.Rest/API/Rest/GetGuildBansParams.cs
  5. +18
    -2
      src/Discord.Net.Rest/DiscordRestApiClient.cs
  6. +46
    -4
      src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs
  7. +20
    -17
      src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
  8. +21
    -13
      src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs

+ 7
- 0
src/Discord.Net.Core/DiscordConfig.cs View File

@@ -97,6 +97,13 @@ namespace Discord
/// </returns>
public const int MaxUsersPerBatch = 1000;
/// <summary>
/// Returns the max bans allowed to be in a request.
/// </summary>
/// <returns>
/// The maximum number of bans that can be gotten per-batch.
/// </returns>
public const int MaxBansPerBatch = 1000;
/// <summary>
/// Returns the max users allowed to be in a request for guild event users.
/// </summary>
/// <returns>


+ 5
- 5
src/Discord.Net.Core/Entities/Channels/Direction.cs View File

@@ -1,10 +1,10 @@
namespace Discord
{
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// This enum is used to specify the direction for retrieving messages.
/// This enum is used to specify the direction for retrieving entities.
/// <note type="important">
/// At the time of writing, <see cref="Around"/> is not yet implemented into
/// <see cref="IMessageChannel.GetMessagesAsync(int, CacheMode, RequestOptions)"/>.
@@ -15,15 +15,15 @@ namespace Discord
public enum Direction
{
/// <summary>
/// The message(s) should be retrieved before a message.
/// The entity(s) should be retrieved before an entity.
/// </summary>
Before,
/// <summary>
/// The message(s) should be retrieved after a message.
/// The entity(s) should be retrieved after an entity.
/// </summary>
After,
/// <summary>
/// The message(s) should be retrieved around a message.
/// The entity(s) should be retrieved around an entity.
/// </summary>
Around
}


+ 59
- 6
src/Discord.Net.Core/Entities/Guilds/IGuild.cs View File

@@ -409,17 +409,70 @@ namespace Discord
/// A task that represents the asynchronous leave operation.
/// </returns>
Task LeaveAsync(RequestOptions options = null);

/// <summary>
/// Gets a collection of all users banned in this guild.
/// Gets <paramref name="limit"/> amount of bans from the guild ordered by user ID.
/// </summary>
/// <remarks>
/// <note type="important">
/// The returned collection is an asynchronous enumerable object; one must call
/// <see cref="AsyncEnumerableExtensions.FlattenAsync{T}"/> to access the individual messages as a
/// collection.
/// </note>
/// <note type="warning">
/// 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!
/// </note>
/// </remarks>
/// <param name="limit">The amount of bans to get from the guild.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous get operation. The task result contains 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.
/// </returns>
IAsyncEnumerable<IReadOnlyCollection<IBan>> GetBansAsync(int limit = DiscordConfig.MaxBansPerBatch, RequestOptions options = null);
/// <summary>
/// Gets <paramref name="limit"/> amount of bans from the guild starting at the provided <paramref name="fromUserId"/> ordered by user ID.
/// </summary>
/// <remarks>
/// <note type="important">
/// The returned collection is an asynchronous enumerable object; one must call
/// <see cref="AsyncEnumerableExtensions.FlattenAsync{T}"/> to access the individual messages as a
/// collection.
/// </note>
/// <note type="warning">
/// 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!
/// </note>
/// </remarks>
/// <param name="fromUserId">The ID of the user to start to get bans from.</param>
/// <param name="dir">The direction of the bans to be gotten.</param>
/// <param name="limit">The number of bans to get.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A paged collection of bans.
/// </returns>
IAsyncEnumerable<IReadOnlyCollection<IBan>> GetBansAsync(ulong fromUserId, Direction dir, int limit = DiscordConfig.MaxBansPerBatch, RequestOptions options = null);
/// <summary>
/// Gets <paramref name="limit"/> amount of bans from the guild starting at the provided <paramref name="fromUser"/> ordered by user ID.
/// </summary>
/// <remarks>
/// <note type="important">
/// The returned collection is an asynchronous enumerable object; one must call
/// <see cref="AsyncEnumerableExtensions.FlattenAsync{T}"/> to access the individual messages as a
/// collection.
/// </note>
/// <note type="warning">
/// 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!
/// </note>
/// </remarks>
/// <param name="fromUser">The user to start to get bans from.</param>
/// <param name="dir">The direction of the bans to be gotten.</param>
/// <param name="limit">The number of bans to get.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A paged collection of bans.
/// </returns>
Task<IReadOnlyCollection<IBan>> GetBansAsync(RequestOptions options = null);
IAsyncEnumerable<IReadOnlyCollection<IBan>> GetBansAsync(IUser fromUser, Direction dir, int limit = DiscordConfig.MaxBansPerBatch, RequestOptions options = null);
/// <summary>
/// Gets a ban object for a banned user.
/// </summary>


+ 9
- 0
src/Discord.Net.Rest/API/Rest/GetGuildBansParams.cs View File

@@ -0,0 +1,9 @@
namespace Discord.API.Rest
{
internal class GetGuildBansParams
{
public Optional<int> Limit { get; set; }
public Optional<Direction> RelativeDirection { get; set; }
public Optional<ulong> RelativeUserId { get; set; }
}
}

+ 18
- 2
src/Discord.Net.Rest/DiscordRestApiClient.cs View File

@@ -1545,13 +1545,29 @@ namespace Discord.API
#endregion

#region Guild Bans
public async Task<IReadOnlyCollection<Ban>> GetGuildBansAsync(ulong guildId, RequestOptions options = null)
public async Task<IReadOnlyCollection<Ban>> 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<IReadOnlyCollection<Ban>>("GET", () => $"guilds/{guildId}/bans", ids, options: options).ConfigureAwait(false);
Expression<Func<string>> endpoint;
if (relativeId != null)
endpoint = () => $"guilds/{guildId}/bans?limit={limit}&{relativeDir}={relativeId}";
else
endpoint = () => $"guilds/{guildId}/bans?limit={limit}";
return await SendAsync<IReadOnlyCollection<Ban>>("GET", endpoint, ids, options: options).ConfigureAwait(false);
}
public async Task<Ban> GetGuildBanAsync(ulong guildId, ulong userId, RequestOptions options)
{


+ 46
- 4
src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs View File

@@ -142,12 +142,54 @@ namespace Discord.Rest
#endregion

#region Bans
public static async Task<IReadOnlyCollection<RestBan>> GetBansAsync(IGuild guild, BaseDiscordClient client,
RequestOptions options)
public static IAsyncEnumerable<IReadOnlyCollection<RestBan>> 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<RestBan>(
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<RestBan>();

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<RestBan> GetBanAsync(IGuild guild, BaseDiscordClient client, ulong userId, RequestOptions options)
{
var model = await client.ApiClient.GetGuildBanAsync(guild.Id, userId, options).ConfigureAwait(false);


+ 20
- 17
src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs View File

@@ -333,17 +333,18 @@ namespace Discord.Rest
#endregion

#region Bans
/// <summary>
/// Gets a collection of all users banned in this guild.
/// </summary>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous get operation. The task result contains a read-only collection of
/// ban objects that this guild currently possesses, with each object containing the user banned and reason
/// behind the ban.
/// </returns>
public Task<IReadOnlyCollection<RestBan>> GetBansAsync(RequestOptions options = null)
=> GuildHelper.GetBansAsync(this, Discord, options);

/// <inheritdoc cref="IGuild.GetBansAsync(int, RequestOptions)" />
public IAsyncEnumerable<IReadOnlyCollection<RestBan>> GetBansAsync(int limit = DiscordConfig.MaxBansPerBatch, RequestOptions options = null)
=> GuildHelper.GetBansAsync(this, Discord, null, Direction.Before, limit, options);

/// <inheritdoc cref="IGuild.GetBansAsync(ulong, Direction, int, RequestOptions)" />
public IAsyncEnumerable<IReadOnlyCollection<RestBan>> GetBansAsync(ulong fromUserId, Direction dir, int limit = DiscordConfig.MaxBansPerBatch, RequestOptions options = null)
=> GuildHelper.GetBansAsync(this, Discord, fromUserId, dir, limit, options);

/// <inheritdoc cref="IGuild.GetBansAsync(IUser, Direction, int, RequestOptions)" />
public IAsyncEnumerable<IReadOnlyCollection<RestBan>> GetBansAsync(IUser fromUser, Direction dir, int limit = DiscordConfig.MaxBansPerBatch, RequestOptions options = null)
=> GuildHelper.GetBansAsync(this, Discord, fromUser.Id, dir, limit, options);
/// <summary>
/// Gets a ban object for a banned user.
/// </summary>
@@ -1193,22 +1194,24 @@ namespace Discord.Rest
IReadOnlyCollection<IRole> IGuild.Roles => Roles;

IReadOnlyCollection<ICustomSticker> IGuild.Stickers => Stickers;

/// <inheritdoc />
async Task<IGuildScheduledEvent> 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);

/// <inheritdoc />
async Task<IGuildScheduledEvent> IGuild.GetEventAsync(ulong id, RequestOptions options)
=> await GetEventAsync(id, options).ConfigureAwait(false);

/// <inheritdoc />
async Task<IReadOnlyCollection<IGuildScheduledEvent>> IGuild.GetEventsAsync(RequestOptions options)
=> await GetEventsAsync(options).ConfigureAwait(false);

/// <inheritdoc />
async Task<IReadOnlyCollection<IBan>> IGuild.GetBansAsync(RequestOptions options)
=> await GetBansAsync(options).ConfigureAwait(false);
IAsyncEnumerable<IReadOnlyCollection<IBan>> IGuild.GetBansAsync(int limit, RequestOptions options)
=> GetBansAsync(limit, options);
/// <inheritdoc />
IAsyncEnumerable<IReadOnlyCollection<IBan>> IGuild.GetBansAsync(ulong fromUserId, Direction dir, int limit, RequestOptions options)
=> GetBansAsync(fromUserId, dir, limit, options);
/// <inheritdoc />
IAsyncEnumerable<IReadOnlyCollection<IBan>> IGuild.GetBansAsync(IUser fromUser, Direction dir, int limit, RequestOptions options)
=> GetBansAsync(fromUser, dir, limit, options);
/// <inheritdoc/>
async Task<IBan> IGuild.GetBanAsync(IUser user, RequestOptions options)
=> await GetBanAsync(user, options).ConfigureAwait(false);


+ 21
- 13
src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs View File

@@ -621,17 +621,19 @@ namespace Discord.WebSocket
#endregion

#region Bans
/// <summary>
/// Gets a collection of all users banned in this guild.
/// </summary>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous get operation. The task result contains a read-only collection of
/// ban objects that this guild currently possesses, with each object containing the user banned and reason
/// behind the ban.
/// </returns>
public Task<IReadOnlyCollection<RestBan>> GetBansAsync(RequestOptions options = null)
=> GuildHelper.GetBansAsync(this, Discord, options);

/// <inheritdoc cref="IGuild.GetBansAsync(int, RequestOptions)" />
public IAsyncEnumerable<IReadOnlyCollection<RestBan>> GetBansAsync(int limit = DiscordConfig.MaxBansPerBatch, RequestOptions options = null)
=> GuildHelper.GetBansAsync(this, Discord, null, Direction.Before, limit, options);

/// <inheritdoc cref="IGuild.GetBansAsync(ulong, Direction, int, RequestOptions)" />
public IAsyncEnumerable<IReadOnlyCollection<RestBan>> GetBansAsync(ulong fromUserId, Direction dir, int limit = DiscordConfig.MaxBansPerBatch, RequestOptions options = null)
=> GuildHelper.GetBansAsync(this, Discord, fromUserId, dir, limit, options);

/// <inheritdoc cref="IGuild.GetBansAsync(IUser, Direction, int, RequestOptions)" />
public IAsyncEnumerable<IReadOnlyCollection<RestBan>> GetBansAsync(IUser fromUser, Direction dir, int limit = DiscordConfig.MaxBansPerBatch, RequestOptions options = null)
=> GuildHelper.GetBansAsync(this, Discord, fromUser.Id, dir, limit, options);

/// <summary>
/// Gets a ban object for a banned user.
/// </summary>
@@ -1810,8 +1812,14 @@ namespace Discord.WebSocket
async Task<IReadOnlyCollection<IGuildScheduledEvent>> IGuild.GetEventsAsync(RequestOptions options)
=> await GetEventsAsync(options).ConfigureAwait(false);
/// <inheritdoc />
async Task<IReadOnlyCollection<IBan>> IGuild.GetBansAsync(RequestOptions options)
=> await GetBansAsync(options).ConfigureAwait(false);
IAsyncEnumerable<IReadOnlyCollection<IBan>> IGuild.GetBansAsync(int limit, RequestOptions options)
=> GetBansAsync(limit, options);
/// <inheritdoc />
IAsyncEnumerable<IReadOnlyCollection<IBan>> IGuild.GetBansAsync(ulong fromUserId, Direction dir, int limit, RequestOptions options)
=> GetBansAsync(fromUserId, dir, limit, options);
/// <inheritdoc />
IAsyncEnumerable<IReadOnlyCollection<IBan>> IGuild.GetBansAsync(IUser fromUser, Direction dir, int limit, RequestOptions options)
=> GetBansAsync(fromUser, dir, limit, options);
/// <inheritdoc/>
async Task<IBan> IGuild.GetBanAsync(IUser user, RequestOptions options)
=> await GetBanAsync(user, options).ConfigureAwait(false);


Loading…
Cancel
Save