* Support filtering audit log entries on user, type, and before id Adds support for filtering audit log entires with GetAuditLogsAsync. Adds the ability to specify a userId and ActionType to filter. Exposes the beforeId filter which was already implemented, yet unused (even when requesting > 100 entries)? Was thinking that this could expose overloads of GetAuditLogAsync that accepts a IUser and IAuditLogEntry, but dealing with all the combinations of these types may be excessive. * use only stringbuilder for args instead of string interpolationtags/2.2.0
| @@ -680,12 +680,16 @@ namespace Discord | |||||
| /// <param name="limit">The number of audit log entries to fetch.</param> | /// <param name="limit">The number of audit log entries to fetch.</param> | ||||
| /// <param name="mode">The <see cref="CacheMode" /> that determines whether the object should be fetched from cache.</param> | /// <param name="mode">The <see cref="CacheMode" /> that determines whether the object should be fetched from cache.</param> | ||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <param name="beforeId">The audit log entry ID to get entries before.</param> | |||||
| /// <param name="actionType">The type of actions to filter.</param> | |||||
| /// <param name="userId">The user ID to filter entries for.</param> | |||||
| /// <returns> | /// <returns> | ||||
| /// A task that represents the asynchronous get operation. The task result contains a read-only collection | /// A task that represents the asynchronous get operation. The task result contains a read-only collection | ||||
| /// of the requested audit log entries. | /// of the requested audit log entries. | ||||
| /// </returns> | /// </returns> | ||||
| Task<IReadOnlyCollection<IAuditLogEntry>> GetAuditLogsAsync(int limit = DiscordConfig.MaxAuditLogEntriesPerBatch, | Task<IReadOnlyCollection<IAuditLogEntry>> GetAuditLogsAsync(int limit = DiscordConfig.MaxAuditLogEntriesPerBatch, | ||||
| CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||||
| CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null, ulong? beforeId = null, ulong? userId = null, | |||||
| ActionType? actionType = null); | |||||
| /// <summary> | /// <summary> | ||||
| /// Gets a webhook found within this guild. | /// Gets a webhook found within this guild. | ||||
| @@ -1,8 +1,10 @@ | |||||
| namespace Discord.API.Rest | |||||
| namespace Discord.API.Rest | |||||
| { | { | ||||
| class GetAuditLogsParams | class GetAuditLogsParams | ||||
| { | { | ||||
| public Optional<int> Limit { get; set; } | public Optional<int> Limit { get; set; } | ||||
| public Optional<ulong> BeforeEntryId { get; set; } | public Optional<ulong> BeforeEntryId { get; set; } | ||||
| public Optional<ulong> UserId { get; set; } | |||||
| public Optional<int> ActionType { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -1321,11 +1321,25 @@ namespace Discord.API | |||||
| var ids = new BucketIds(guildId: guildId); | var ids = new BucketIds(guildId: guildId); | ||||
| Expression<Func<string>> endpoint; | Expression<Func<string>> endpoint; | ||||
| var queryArgs = new StringBuilder(); | |||||
| if (args.BeforeEntryId.IsSpecified) | if (args.BeforeEntryId.IsSpecified) | ||||
| endpoint = () => $"guilds/{guildId}/audit-logs?limit={limit}&before={args.BeforeEntryId.Value}"; | |||||
| else | |||||
| endpoint = () => $"guilds/{guildId}/audit-logs?limit={limit}"; | |||||
| { | |||||
| queryArgs.Append("&before=") | |||||
| .Append(args.BeforeEntryId); | |||||
| } | |||||
| if (args.UserId.IsSpecified) | |||||
| { | |||||
| queryArgs.Append("&user_id=") | |||||
| .Append(args.UserId.Value); | |||||
| } | |||||
| if (args.ActionType.IsSpecified) | |||||
| { | |||||
| queryArgs.Append("&action_type=") | |||||
| .Append(args.ActionType.Value); | |||||
| } | |||||
| // still use string interp for the query w/o params, as this is necessary for CreateBucketId | |||||
| endpoint = () => $"guilds/{guildId}/audit-logs?limit={limit}{queryArgs.ToString()}"; | |||||
| return await SendAsync<AuditLog>("GET", endpoint, ids, options: options).ConfigureAwait(false); | return await SendAsync<AuditLog>("GET", endpoint, ids, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -380,7 +380,7 @@ namespace Discord.Rest | |||||
| // Audit logs | // Audit logs | ||||
| public static IAsyncEnumerable<IReadOnlyCollection<RestAuditLogEntry>> GetAuditLogsAsync(IGuild guild, BaseDiscordClient client, | public static IAsyncEnumerable<IReadOnlyCollection<RestAuditLogEntry>> GetAuditLogsAsync(IGuild guild, BaseDiscordClient client, | ||||
| ulong? from, int? limit, RequestOptions options) | |||||
| ulong? from, int? limit, RequestOptions options, ulong? userId = null, ActionType? actionType = null) | |||||
| { | { | ||||
| return new PagedAsyncEnumerable<RestAuditLogEntry>( | return new PagedAsyncEnumerable<RestAuditLogEntry>( | ||||
| DiscordConfig.MaxAuditLogEntriesPerBatch, | DiscordConfig.MaxAuditLogEntriesPerBatch, | ||||
| @@ -392,6 +392,10 @@ namespace Discord.Rest | |||||
| }; | }; | ||||
| if (info.Position != null) | if (info.Position != null) | ||||
| args.BeforeEntryId = info.Position.Value; | args.BeforeEntryId = info.Position.Value; | ||||
| if (userId.HasValue) | |||||
| args.UserId = userId.Value; | |||||
| if (actionType.HasValue) | |||||
| args.ActionType = (int)actionType.Value; | |||||
| var model = await client.ApiClient.GetAuditLogsAsync(guild.Id, args, options); | var model = await client.ApiClient.GetAuditLogsAsync(guild.Id, args, options); | ||||
| return model.Entries.Select((x) => RestAuditLogEntry.Create(client, model, x)).ToImmutableArray(); | return model.Entries.Select((x) => RestAuditLogEntry.Create(client, model, x)).ToImmutableArray(); | ||||
| }, | }, | ||||
| @@ -627,12 +627,15 @@ namespace Discord.Rest | |||||
| /// </summary> | /// </summary> | ||||
| /// <param name="limit">The number of audit log entries to fetch.</param> | /// <param name="limit">The number of audit log entries to fetch.</param> | ||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <param name="beforeId">The audit log entry ID to get entries before.</param> | |||||
| /// <param name="actionType">The type of actions to filter.</param> | |||||
| /// <param name="userId">The user ID to filter entries for.</param> | |||||
| /// <returns> | /// <returns> | ||||
| /// A task that represents the asynchronous get operation. The task result contains a read-only collection | /// A task that represents the asynchronous get operation. The task result contains a read-only collection | ||||
| /// of the requested audit log entries. | /// of the requested audit log entries. | ||||
| /// </returns> | /// </returns> | ||||
| public IAsyncEnumerable<IReadOnlyCollection<RestAuditLogEntry>> GetAuditLogsAsync(int limit, RequestOptions options = null) | |||||
| => GuildHelper.GetAuditLogsAsync(this, Discord, null, limit, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<RestAuditLogEntry>> 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); | |||||
| //Webhooks | //Webhooks | ||||
| /// <summary> | /// <summary> | ||||
| @@ -866,10 +869,11 @@ namespace Discord.Rest | |||||
| Task IGuild.DownloadUsersAsync() => | Task IGuild.DownloadUsersAsync() => | ||||
| throw new NotSupportedException(); | throw new NotSupportedException(); | ||||
| async Task<IReadOnlyCollection<IAuditLogEntry>> IGuild.GetAuditLogsAsync(int limit, CacheMode cacheMode, RequestOptions options) | |||||
| async Task<IReadOnlyCollection<IAuditLogEntry>> IGuild.GetAuditLogsAsync(int limit, CacheMode cacheMode, RequestOptions options, | |||||
| ulong? beforeId, ulong? userId, ActionType? actionType) | |||||
| { | { | ||||
| if (cacheMode == CacheMode.AllowDownload) | if (cacheMode == CacheMode.AllowDownload) | ||||
| return (await GetAuditLogsAsync(limit, options).FlattenAsync().ConfigureAwait(false)).ToImmutableArray(); | |||||
| return (await GetAuditLogsAsync(limit, options, beforeId: beforeId, userId: userId, actionType: actionType).FlattenAsync().ConfigureAwait(false)).ToImmutableArray(); | |||||
| else | else | ||||
| return ImmutableArray.Create<IAuditLogEntry>(); | return ImmutableArray.Create<IAuditLogEntry>(); | ||||
| } | } | ||||
| @@ -801,12 +801,15 @@ namespace Discord.WebSocket | |||||
| /// </summary> | /// </summary> | ||||
| /// <param name="limit">The number of audit log entries to fetch.</param> | /// <param name="limit">The number of audit log entries to fetch.</param> | ||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <param name="beforeId">The audit log entry ID to filter entries before.</param> | |||||
| /// <param name="actionType">The type of actions to filter.</param> | |||||
| /// <param name="userId">The user ID to filter entries for.</param> | |||||
| /// <returns> | /// <returns> | ||||
| /// A task that represents the asynchronous get operation. The task result contains a read-only collection | /// A task that represents the asynchronous get operation. The task result contains a read-only collection | ||||
| /// of the requested audit log entries. | /// of the requested audit log entries. | ||||
| /// </returns> | /// </returns> | ||||
| public IAsyncEnumerable<IReadOnlyCollection<RestAuditLogEntry>> GetAuditLogsAsync(int limit, RequestOptions options = null) | |||||
| => GuildHelper.GetAuditLogsAsync(this, Discord, null, limit, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<RestAuditLogEntry>> 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); | |||||
| //Webhooks | //Webhooks | ||||
| /// <summary> | /// <summary> | ||||
| @@ -1160,10 +1163,11 @@ namespace Discord.WebSocket | |||||
| => Task.FromResult<IGuildUser>(Owner); | => Task.FromResult<IGuildUser>(Owner); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| async Task<IReadOnlyCollection<IAuditLogEntry>> IGuild.GetAuditLogsAsync(int limit, CacheMode cacheMode, RequestOptions options) | |||||
| async Task<IReadOnlyCollection<IAuditLogEntry>> IGuild.GetAuditLogsAsync(int limit, CacheMode cacheMode, RequestOptions options, | |||||
| ulong? beforeId, ulong? userId, ActionType? actionType) | |||||
| { | { | ||||
| if (cacheMode == CacheMode.AllowDownload) | if (cacheMode == CacheMode.AllowDownload) | ||||
| return (await GetAuditLogsAsync(limit, options).FlattenAsync().ConfigureAwait(false)).ToImmutableArray(); | |||||
| return (await GetAuditLogsAsync(limit, options, beforeId: beforeId, userId: userId, actionType: actionType).FlattenAsync().ConfigureAwait(false)).ToImmutableArray(); | |||||
| else | else | ||||
| return ImmutableArray.Create<IAuditLogEntry>(); | return ImmutableArray.Create<IAuditLogEntry>(); | ||||
| } | } | ||||