From ebfd5af41d940029834b50407bf71d459e999389 Mon Sep 17 00:00:00 2001 From: kask Date: Sat, 4 Mar 2023 02:41:35 +0200 Subject: [PATCH] Support filtering audit log entries by after id Adds support for filtering audit log entires with GetAuditLogsAsync. Adds the ability to specify an afterId as specified on [Discord developers docs](https://discord.com/developers/docs/resources/audit-log#get-guild-audit-log). --- src/Discord.Net.Core/Entities/Guilds/IGuild.cs | 9 +++++---- src/Discord.Net.Rest/API/Rest/GetAuditLogsParams.cs | 1 + src/Discord.Net.Rest/DiscordRestApiClient.cs | 5 +++++ src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs | 10 ++++++---- src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs | 9 +++++---- .../Entities/Guilds/SocketGuild.cs | 9 +++++---- 6 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs index a0b95759d7..1154b1ed3e 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs @@ -414,7 +414,7 @@ public interface IGuild : IDeletable, ISnowflakeEntity /// /// /// - /// The returned collection is an asynchronous enumerable object; one must call + /// The returned collection is an asynchronous enumerable object; one must call /// to access the individual messages as a /// collection. /// @@ -434,7 +434,7 @@ public interface IGuild : IDeletable, ISnowflakeEntity /// /// /// - /// The returned collection is an asynchronous enumerable object; one must call + /// The returned collection is an asynchronous enumerable object; one must call /// to access the individual messages as a /// collection. /// @@ -456,7 +456,7 @@ public interface IGuild : IDeletable, ISnowflakeEntity /// /// /// - /// The returned collection is an asynchronous enumerable object; one must call + /// The returned collection is an asynchronous enumerable object; one must call /// to access the individual messages as a /// collection. /// @@ -988,13 +988,14 @@ public interface IGuild : IDeletable, ISnowflakeEntity /// The audit log entry ID to get entries before. /// The type of actions to filter. /// The user ID to filter entries for. + /// The audit log entry ID to get entries after. /// /// A task that represents the asynchronous get operation. The task result contains a read-only collection /// of the requested audit log entries. /// Task> GetAuditLogsAsync(int limit = DiscordConfig.MaxAuditLogEntriesPerBatch, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null, ulong? beforeId = null, ulong? userId = null, - ActionType? actionType = null); + ActionType? actionType = null, ulong? afterId = null); /// /// Gets a webhook found within this guild. diff --git a/src/Discord.Net.Rest/API/Rest/GetAuditLogsParams.cs b/src/Discord.Net.Rest/API/Rest/GetAuditLogsParams.cs index f136fa7aaa..a697db3132 100644 --- a/src/Discord.Net.Rest/API/Rest/GetAuditLogsParams.cs +++ b/src/Discord.Net.Rest/API/Rest/GetAuditLogsParams.cs @@ -4,6 +4,7 @@ class GetAuditLogsParams { public Optional Limit { get; set; } public Optional BeforeEntryId { get; set; } + public Optional AfterEntryId { get; set; } public Optional UserId { get; set; } public Optional ActionType { get; set; } } diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index 4a5ea19960..c84b543934 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -2331,6 +2331,11 @@ public async Task GetAuditLogsAsync(ulong guildId, GetAuditLogsParams queryArgs.Append("&action_type=") .Append(args.ActionType.Value); } + if (args.AfterEntryId.IsSpecified) + { + queryArgs.Append("&after=") + .Append(args.AfterEntryId); + } // Still use string interpolation for the query w/o params, as this is necessary for CreateBucketId endpoint = () => $"guilds/{guildId}/audit-logs?limit={limit}{queryArgs.ToString()}"; diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs index bbcb2bb5ed..da878f37dc 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs @@ -610,7 +610,7 @@ public static async Task> SearchUsersAsync(IG #region Audit logs public static IAsyncEnumerable> GetAuditLogsAsync(IGuild guild, BaseDiscordClient client, - ulong? from, int? limit, RequestOptions options, ulong? userId = null, ActionType? actionType = null) + ulong? from, int? limit, RequestOptions options, ulong? userId = null, ActionType? actionType = null, ulong? afterId = null) { return new PagedAsyncEnumerable( DiscordConfig.MaxAuditLogEntriesPerBatch, @@ -626,6 +626,8 @@ public static IAsyncEnumerable> GetAuditL args.UserId = userId.Value; if (actionType.HasValue) args.ActionType = (int)actionType.Value; + if (afterId.HasValue) + args.AfterEntryId = afterId.Value; var model = await client.ApiClient.GetAuditLogsAsync(guild.Id, args, options); return model.Entries.Select((x) => RestAuditLogEntry.Create(client, model, x)).ToImmutableArray(); }, @@ -1080,7 +1082,7 @@ public static async Task CreateAutoModRuleAsync(IGuild guild throw new ArgumentException("Name of the rule must not be empty", paramName: nameof(args.Name)); Preconditions.AtLeast(1, args.Actions.GetValueOrDefault(Array.Empty()).Length, nameof(args.Actions), "Auto moderation rule must have at least 1 action"); - + if (args.RegexPatterns.IsSpecified) { if (args.TriggerType.Value is not AutoModTriggerType.Keyword) @@ -1121,10 +1123,10 @@ public static async Task CreateAutoModRuleAsync(IGuild guild throw new ArgumentException(message: $"Allow list entry length must be less than or equal to {AutoModRuleProperties.MaxAllowListEntryLength}.", paramName: nameof(args.AllowList)); } - + if (args.TriggerType.Value is not AutoModTriggerType.KeywordPreset && args.Presets.IsSpecified) throw new ArgumentException(message: $"Keyword presets scan only be used with 'KeywordPreset' trigger type.", paramName: nameof(args.Presets)); - + if (args.MentionLimit.IsSpecified) { if (args.TriggerType.Value is AutoModTriggerType.MentionSpam) diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs index 9ca01ead26..8a50f1dc55 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -906,12 +906,13 @@ public Task> SearchUsersAsync(string query, i /// The audit log entry ID to get entries before. /// The type of actions to filter. /// The user ID to filter entries for. + /// The audit log entry ID to get entries after. /// /// A task that represents the asynchronous get operation. The task result contains a read-only collection /// of the requested audit log entries. /// - public IAsyncEnumerable> GetAuditLogsAsync(int limit, RequestOptions options = null, ulong? beforeId = null, ulong? userId = null, ActionType? actionType = null) - => GuildHelper.GetAuditLogsAsync(this, Discord, beforeId, limit, options, userId: userId, actionType: actionType); + public IAsyncEnumerable> GetAuditLogsAsync(int limit, RequestOptions options = null, ulong? beforeId = null, ulong? userId = null, ActionType? actionType = null, ulong? afterId = null) + => GuildHelper.GetAuditLogsAsync(this, Discord, beforeId, limit, options, userId: userId, actionType: actionType, afterId: afterId); #endregion #region Webhooks @@ -1500,10 +1501,10 @@ async Task> IGuild.SearchUsersAsync(string query } async Task> IGuild.GetAuditLogsAsync(int limit, CacheMode cacheMode, RequestOptions options, - ulong? beforeId, ulong? userId, ActionType? actionType) + ulong? beforeId, ulong? userId, ActionType? actionType, ulong? afterId) { if (cacheMode == CacheMode.AllowDownload) - return (await GetAuditLogsAsync(limit, options, beforeId: beforeId, userId: userId, actionType: actionType).FlattenAsync().ConfigureAwait(false)).ToImmutableArray(); + return (await GetAuditLogsAsync(limit, options, beforeId: beforeId, userId: userId, actionType: actionType, afterId: afterId).FlattenAsync().ConfigureAwait(false)).ToImmutableArray(); else return ImmutableArray.Create(); } diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index 2f54e6105d..17acb9448c 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -1374,12 +1374,13 @@ public Task CreateEventAsync( /// The audit log entry ID to filter entries before. /// The type of actions to filter. /// The user ID to filter entries for. + /// The audit log entry ID to filter entries after. /// /// A task that represents the asynchronous get operation. The task result contains a read-only collection /// of the requested audit log entries. /// - public IAsyncEnumerable> GetAuditLogsAsync(int limit, RequestOptions options = null, ulong? beforeId = null, ulong? userId = null, ActionType? actionType = null) - => GuildHelper.GetAuditLogsAsync(this, Discord, beforeId, limit, options, userId: userId, actionType: actionType); + public IAsyncEnumerable> GetAuditLogsAsync(int limit, RequestOptions options = null, ulong? beforeId = null, ulong? userId = null, ActionType? actionType = null, ulong? afterId = null) + => GuildHelper.GetAuditLogsAsync(this, Discord, beforeId, limit, options, userId: userId, actionType: actionType, afterId: afterId); #endregion #region Webhooks @@ -2068,10 +2069,10 @@ async Task> IGuild.SearchUsersAsync(string query /// async Task> IGuild.GetAuditLogsAsync(int limit, CacheMode cacheMode, RequestOptions options, - ulong? beforeId, ulong? userId, ActionType? actionType) + ulong? beforeId, ulong? userId, ActionType? actionType, ulong? afterId) { if (cacheMode == CacheMode.AllowDownload) - return (await GetAuditLogsAsync(limit, options, beforeId: beforeId, userId: userId, actionType: actionType).FlattenAsync().ConfigureAwait(false)).ToImmutableArray(); + return (await GetAuditLogsAsync(limit, options, beforeId: beforeId, userId: userId, actionType: actionType, afterId: afterId).FlattenAsync().ConfigureAwait(false)).ToImmutableArray(); else return ImmutableArray.Create(); }