[Feature] Audit Log Created gateway event support (#2627)

* add event to socket client

* make `AuditLog` param optional

* Some WIP audit log changes

* api models

* moar models

* complete models

* modelsss

* forgot to push

* oh lol forgot this too

* api & rest guild method

* revert VS being VS & formatting to file scoped namespace

* socket entities

* some work eh

* moar stuff

- switched to d.net's attribute for audit log deserialization

* working socket guild updated event + reworked rest GuildInfo creation

* a bit of refactoring + new models

* +channel created

* channel updated & channel deleted

* refactor rest channel updated log + some nullable fixes

* rest channel created + deleted logs

* user banned socket log

* kick + unban

* moar log modelsssss

* fixes & 4 more log types

* overwrite logs

* role logs

* invite logs

* webhook logs

* switch to `ISocketAuditLogData` for socket log data

* emote logs

* move stuff around

* move more stuff around

* audit logs cache

* scheduled event logs

* thread logs

* command permission update audit log

* fetch scheduled event data from log

* integration audit logs

* sticker audit log data

* stage instance audit logs

* auto mod rule audit logs

* fix

* forgot couple props

* command perm updated data from options

* final automod ones

* debugging goes away :(

* merge cringe

* ...

* yup

* fix xml doc

* onboarding audit logs

* changes

---------

Co-authored-by: cat <lumitydev@gmail.com>
This commit is contained in:
Misha133
2023-04-15 02:13:02 +03:00
committed by GitHub
parent 3a8f76c4b1
commit dff6a57a92
181 changed files with 8992 additions and 2805 deletions

View File

@@ -0,0 +1,83 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
namespace Discord.WebSocket;
internal class AuditLogCache
{
private readonly ConcurrentDictionary<ulong, SocketAuditLogEntry> _entries;
private readonly ConcurrentQueue<ulong> _orderedEntries;
private readonly int _size;
public IReadOnlyCollection<SocketAuditLogEntry> AuditLogs => _entries.ToReadOnlyCollection();
public AuditLogCache(DiscordSocketClient client)
{
_size = client.AuditLogCacheSize;
_entries = new ConcurrentDictionary<ulong, SocketAuditLogEntry>(ConcurrentHashSet.DefaultConcurrencyLevel, (int)(_size * 1.05));
_orderedEntries = new ConcurrentQueue<ulong>();
}
public void Add(SocketAuditLogEntry entry)
{
if (_entries.TryAdd(entry.Id, entry))
{
_orderedEntries.Enqueue(entry.Id);
while (_orderedEntries.Count > _size && _orderedEntries.TryDequeue(out var entryId))
_entries.TryRemove(entryId, out _);
}
}
public SocketAuditLogEntry Remove(ulong id)
{
_entries.TryRemove(id, out var entry);
return entry;
}
public SocketAuditLogEntry Get(ulong id)
=> _entries.TryGetValue(id, out var result) ? result : null;
/// <exception cref="ArgumentOutOfRangeException"><paramref name="limit"/> is less than 0.</exception>
public IReadOnlyCollection<SocketAuditLogEntry> GetMany(ulong? fromEntryId, Direction dir, int limit = DiscordConfig.MaxAuditLogEntriesPerBatch, ActionType ? action = null)
{
if (limit < 0)
throw new ArgumentOutOfRangeException(nameof(limit));
if (limit == 0)
return ImmutableArray<SocketAuditLogEntry>.Empty;
IEnumerable<ulong> cachedEntriesIds;
if (fromEntryId == null)
cachedEntriesIds = _orderedEntries;
else if (dir == Direction.Before)
cachedEntriesIds = _orderedEntries.Where(x => x < fromEntryId.Value);
else if (dir == Direction.After)
cachedEntriesIds = _orderedEntries.Where(x => x > fromEntryId.Value);
else //Direction.Around
{
if (!_entries.TryGetValue(fromEntryId.Value, out var entry))
return ImmutableArray<SocketAuditLogEntry>.Empty;
var around = limit / 2;
var before = GetMany(fromEntryId, Direction.Before, around, action);
var after = GetMany(fromEntryId, Direction.After, around, action).Reverse();
return after.Concat(new [] { entry }).Concat(before).ToImmutableArray();
}
if (dir == Direction.Before)
cachedEntriesIds = cachedEntriesIds.Reverse();
if (dir == Direction.Around)
limit = limit / 2 + 1;
return cachedEntriesIds
.Select(x => _entries.TryGetValue(x, out var entry) ? entry : null)
.Where(x => x != null && (action is null || x.Action == action))
.Take(limit)
.ToImmutableArray();
}
}

View File

@@ -0,0 +1,37 @@
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to message getting blocked by automod.
/// </summary>
public class SocketAutoModBlockedMessageAuditLogData : ISocketAuditLogData
{
internal SocketAutoModBlockedMessageAuditLogData(ulong channelId, string autoModRuleName, AutoModTriggerType autoModRuleTriggerType)
{
ChannelId = channelId;
AutoModRuleName = autoModRuleName;
AutoModRuleTriggerType = autoModRuleTriggerType;
}
internal static SocketAutoModBlockedMessageAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
return new(entry.Options.ChannelId!.Value, entry.Options.AutoModRuleName,
entry.Options.AutoModRuleTriggerType!.Value);
}
/// <summary>
/// Gets the channel the message was sent in.
/// </summary>
public ulong ChannelId { get; set; }
/// <summary>
/// Gets the name of the auto moderation rule that got triggered.
/// </summary>
public string AutoModRuleName { get; set; }
/// <summary>
/// Gets the trigger type of the auto moderation rule that got triggered.
/// </summary>
public AutoModTriggerType AutoModRuleTriggerType { get; set; }
}

View File

@@ -0,0 +1,37 @@
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to message getting flagged by automod.
/// </summary>
public class SocketAutoModFlaggedMessageAuditLogData : ISocketAuditLogData
{
internal SocketAutoModFlaggedMessageAuditLogData(ulong channelId, string autoModRuleName, AutoModTriggerType autoModRuleTriggerType)
{
ChannelId = channelId;
AutoModRuleName = autoModRuleName;
AutoModRuleTriggerType = autoModRuleTriggerType;
}
internal static SocketAutoModFlaggedMessageAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
return new(entry.Options.ChannelId!.Value, entry.Options.AutoModRuleName,
entry.Options.AutoModRuleTriggerType!.Value);
}
/// <summary>
/// Gets the channel the message was sent in.
/// </summary>
public ulong ChannelId { get; set; }
/// <summary>
/// Gets the name of the auto moderation rule that got triggered.
/// </summary>
public string AutoModRuleName { get; set; }
/// <summary>
/// Gets the trigger type of the auto moderation rule that got triggered.
/// </summary>
public AutoModTriggerType AutoModRuleTriggerType { get; set; }
}

View File

@@ -0,0 +1,30 @@
using Discord.API.AuditLogs;
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to an auto moderation rule creation.
/// </summary>
public class SocketAutoModRuleCreatedAuditLogData : ISocketAuditLogData
{
private SocketAutoModRuleCreatedAuditLogData(SocketAutoModRuleInfo data)
{
Data = data;
}
internal static SocketAutoModRuleCreatedAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (_, data) = AuditLogHelper.CreateAuditLogEntityInfo<AutoModRuleInfoAuditLogModel>(changes, discord);
return new SocketAutoModRuleCreatedAuditLogData(new (data));
}
/// <summary>
/// Gets the auto moderation rule information after the changes.
/// </summary>
public SocketAutoModRuleInfo Data { get; }
}

View File

@@ -0,0 +1,30 @@
using Discord.API.AuditLogs;
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to an auto moderation rule removal.
/// </summary>
public class SocketAutoModRuleDeletedAuditLogData : ISocketAuditLogData
{
private SocketAutoModRuleDeletedAuditLogData(SocketAutoModRuleInfo data)
{
Data = data;
}
internal static SocketAutoModRuleDeletedAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (data, _) = AuditLogHelper.CreateAuditLogEntityInfo<AutoModRuleInfoAuditLogModel>(changes, discord);
return new SocketAutoModRuleDeletedAuditLogData(new (data));
}
/// <summary>
/// Gets the auto moderation rule information before the changes.
/// </summary>
public SocketAutoModRuleInfo Data { get; }
}

View File

@@ -0,0 +1,113 @@
using Discord.API.AuditLogs;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
namespace Discord.WebSocket;
/// <summary>
/// Represents information for an auto moderation rule.
/// </summary>
public class SocketAutoModRuleInfo
{
internal SocketAutoModRuleInfo(AutoModRuleInfoAuditLogModel model)
{
Actions = model.Actions?.Select(x => new AutoModRuleAction(
x.Type,
x.Metadata.GetValueOrDefault()?.ChannelId.ToNullable(),
x.Metadata.GetValueOrDefault()?.DurationSeconds.ToNullable(),
x.Metadata.IsSpecified
? x.Metadata.Value.CustomMessage.IsSpecified
? x.Metadata.Value.CustomMessage.Value
: null
: null
)).ToImmutableArray();
KeywordFilter = model.TriggerMetadata?.KeywordFilter.GetValueOrDefault(Array.Empty<string>())?.ToImmutableArray();
Presets = model.TriggerMetadata?.Presets.GetValueOrDefault(Array.Empty<KeywordPresetTypes>())?.ToImmutableArray();
RegexPatterns = model.TriggerMetadata?.RegexPatterns.GetValueOrDefault(Array.Empty<string>())?.ToImmutableArray();
AllowList = model.TriggerMetadata?.AllowList.GetValueOrDefault(Array.Empty<string>())?.ToImmutableArray();
MentionTotalLimit = model.TriggerMetadata?.MentionLimit.IsSpecified ?? false
? model.TriggerMetadata?.MentionLimit.Value
: null;
Name = model.Name;
Enabled = model.Enabled;
ExemptRoles = model.ExemptRoles?.ToImmutableArray();
ExemptChannels = model.ExemptChannels?.ToImmutableArray();
TriggerType = model.TriggerType;
EventType = model.EventType;
}
/// <inheritdoc cref="IAutoModRule.Name"/>
/// <remarks>
/// <see langword="null"/> if this property is not mentioned in this entry.
/// </remarks>
public string Name { get; set; }
/// <inheritdoc cref="IAutoModRule.EventType"/>
/// <remarks>
/// <see langword="null"/> if this property is not mentioned in this entry.
/// </remarks>
public AutoModEventType? EventType { get; set; }
/// <inheritdoc cref="IAutoModRule.TriggerType"/>
/// <remarks>
/// <see langword="null"/> if this property is not mentioned in this entry.
/// </remarks>
public AutoModTriggerType? TriggerType { get; set; }
/// <inheritdoc cref="IAutoModRule.Enabled"/>
/// <remarks>
/// <see langword="null"/> if this property is not mentioned in this entry.
/// </remarks>
public bool? Enabled { get; set; }
/// <inheritdoc cref="IAutoModRule.ExemptRoles"/>
/// <remarks>
/// <see langword="null"/> if this property is not mentioned in this entry.
/// </remarks>
public IReadOnlyCollection<ulong> ExemptRoles { get; set; }
/// <inheritdoc cref="IAutoModRule.ExemptChannels"/>
/// <remarks>
/// <see langword="null"/> if this property is not mentioned in this entry.
/// </remarks>
public IReadOnlyCollection<ulong> ExemptChannels { get; set; }
/// <inheritdoc cref="IAutoModRule.KeywordFilter"/>
/// <remarks>
/// <see langword="null"/> if this property is not mentioned in this entry.
/// </remarks>
public IReadOnlyCollection<string> KeywordFilter { get; }
/// <inheritdoc cref="IAutoModRule.RegexPatterns"/>
/// <remarks>
/// <see langword="null"/> if this property is not mentioned in this entry.
/// </remarks>
public IReadOnlyCollection<string> RegexPatterns { get; }
/// <inheritdoc cref="IAutoModRule.AllowList"/>
/// <remarks>
/// <see langword="null"/> if this property is not mentioned in this entry.
/// </remarks>
public IReadOnlyCollection<string> AllowList { get; }
/// <inheritdoc cref="IAutoModRule.Presets"/>
/// <remarks>
/// <see langword="null"/> if this property is not mentioned in this entry.
/// </remarks>
public IReadOnlyCollection<KeywordPresetTypes> Presets { get; }
/// <inheritdoc cref="IAutoModRule.MentionTotalLimit"/>
/// <remarks>
/// <see langword="null"/> if this property is not mentioned in this entry.
/// </remarks>
public int? MentionTotalLimit { get; }
/// <inheritdoc cref="IAutoModRule.Actions"/>
/// <remarks>
/// <see langword="null"/> if this property is not mentioned in this entry.
/// </remarks>
public IReadOnlyCollection<AutoModRuleAction> Actions { get; private set; }
}

View File

@@ -0,0 +1,36 @@
using Discord.API.AuditLogs;
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to an auto moderation rule update.
/// </summary>
public class AutoModRuleUpdatedAuditLogData : ISocketAuditLogData
{
private AutoModRuleUpdatedAuditLogData(SocketAutoModRuleInfo before, SocketAutoModRuleInfo after)
{
Before = before;
After = after;
}
internal static AutoModRuleUpdatedAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (before, after) = AuditLogHelper.CreateAuditLogEntityInfo<AutoModRuleInfoAuditLogModel>(changes, discord);
return new AutoModRuleUpdatedAuditLogData(new(before), new(after));
}
/// <summary>
/// Gets the auto moderation rule information before the changes.
/// </summary>
public SocketAutoModRuleInfo Before { get; }
/// <summary>
/// Gets the auto moderation rule information after the changes.
/// </summary>
public SocketAutoModRuleInfo After { get; }
}

View File

@@ -0,0 +1,37 @@
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to user getting in timeout by automod.
/// </summary>
public class SocketAutoModTimeoutUserAuditLogData : ISocketAuditLogData
{
internal SocketAutoModTimeoutUserAuditLogData(ulong channelId, string autoModRuleName, AutoModTriggerType autoModRuleTriggerType)
{
ChannelId = channelId;
AutoModRuleName = autoModRuleName;
AutoModRuleTriggerType = autoModRuleTriggerType;
}
internal static SocketAutoModTimeoutUserAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
return new(entry.Options.ChannelId!.Value, entry.Options.AutoModRuleName,
entry.Options.AutoModRuleTriggerType!.Value);
}
/// <summary>
/// Gets the channel the message was sent in.
/// </summary>
public ulong ChannelId { get; set; }
/// <summary>
/// Gets the name of the auto moderation rule that got triggered.
/// </summary>
public string AutoModRuleName { get; set; }
/// <summary>
/// Gets the trigger type of the auto moderation rule that got triggered.
/// </summary>
public AutoModTriggerType AutoModRuleTriggerType { get; set; }
}

View File

@@ -0,0 +1,43 @@
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a ban.
/// </summary>
public class SocketBanAuditLogData : ISocketAuditLogData
{
private SocketBanAuditLogData(Cacheable<SocketUser, RestUser, IUser, ulong> user)
{
Target = user;
}
internal static SocketBanAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var cachedUser = discord.GetUser(entry.TargetId!.Value);
var cacheableUser = new Cacheable<SocketUser, RestUser, IUser, ulong>(
cachedUser,
entry.TargetId.Value,
cachedUser is not null,
async () =>
{
var user = await discord.ApiClient.GetUserAsync(entry.TargetId.Value);
return user is not null ? RestUser.Create(discord, user) : null;
});
return new SocketBanAuditLogData(cacheableUser);
}
/// <summary>
/// Gets the user that was banned.
/// </summary>
/// <remarks>
/// Download method may return <see langword="null"/> if the user is a 'Deleted User#....'
/// because Discord does send user data for deleted users.
/// </remarks>
/// <returns>
/// A cacheable user object representing the banned user.
/// </returns>
public Cacheable<SocketUser, RestUser, IUser, ulong> Target { get; }
}

View File

@@ -0,0 +1,42 @@
using Discord.Rest;
using System.Linq;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a adding a bot to a guild.
/// </summary>
public class SocketBotAddAuditLogData : ISocketAuditLogData
{
private SocketBotAddAuditLogData(Cacheable<SocketUser, RestUser, IUser, ulong> bot)
{
Target = bot;
}
internal static SocketBotAddAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var cachedUser = discord.GetUser(entry.TargetId!.Value);
var cacheableUser = new Cacheable<SocketUser, RestUser, IUser, ulong>(
cachedUser,
entry.TargetId.Value,
cachedUser is not null,
async () =>
{
var user = await discord.ApiClient.GetUserAsync(entry.TargetId.Value);
return user is not null ? RestUser.Create(discord, user) : null;
});
return new SocketBotAddAuditLogData(cacheableUser);
}
/// <summary>
/// Gets the bot that was added.
/// </summary>
/// <remarks>
/// Will be <see langword="null"/> if the bot is a 'Deleted User#....' because Discord does send user data for deleted users.
/// </remarks>
/// <returns>
/// A cacheable user object representing the bot.
/// </returns>
public Cacheable<SocketUser, RestUser, IUser, ulong> Target { get; }
}

View File

@@ -0,0 +1,165 @@
using Discord.Rest;
using Discord.API.AuditLogs;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a channel creation.
/// </summary>
public class SocketChannelCreateAuditLogData : ISocketAuditLogData
{
private SocketChannelCreateAuditLogData(ChannelInfoAuditLogModel model, EntryModel entry)
{
ChannelId = entry.TargetId!.Value;
ChannelName = model.Name;
ChannelType = model.Type!.Value;
SlowModeInterval = model.RateLimitPerUser;
IsNsfw = model.IsNsfw;
Bitrate = model.Bitrate;
Topic = model.Topic;
AutoArchiveDuration = model.AutoArchiveDuration;
DefaultSlowModeInterval = model.DefaultThreadRateLimitPerUser;
DefaultAutoArchiveDuration = model.DefaultArchiveDuration;
AvailableTags = model.AvailableTags?.Select(x => new ForumTag(x.Id,
x.Name,
x.EmojiId.GetValueOrDefault(null),
x.EmojiName.GetValueOrDefault(null),
x.Moderated)).ToImmutableArray();
if (model.DefaultEmoji is not null)
{
if (model.DefaultEmoji.EmojiId.HasValue && model.DefaultEmoji.EmojiId.Value != 0)
DefaultReactionEmoji = new Emote(model.DefaultEmoji.EmojiId.GetValueOrDefault(), null, false);
else if (model.DefaultEmoji.EmojiName.IsSpecified)
DefaultReactionEmoji = new Emoji(model.DefaultEmoji.EmojiName.Value);
else
DefaultReactionEmoji = null;
}
else
DefaultReactionEmoji = null;
VideoQualityMode = model.VideoQualityMode;
RtcRegion = model.Region;
Flags = model.Flags;
UserLimit = model.UserLimit;
}
internal static SocketChannelCreateAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (_, data) = AuditLogHelper.CreateAuditLogEntityInfo<ChannelInfoAuditLogModel>(changes, discord);
return new SocketChannelCreateAuditLogData(data, entry);
}
/// <summary>
/// Gets the snowflake ID of the created channel.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier for the created channel.
/// </returns>
public ulong ChannelId { get; }
/// <summary>
/// Gets the name of the created channel.
/// </summary>
/// <returns>
/// A string containing the name of the created channel.
/// </returns>
public string ChannelName { get; }
/// <summary>
/// Gets the type of the created channel.
/// </summary>
/// <returns>
/// The type of channel that was created.
/// </returns>
public ChannelType ChannelType { get; }
/// <summary>
/// Gets the current slow-mode delay of the created channel.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the time in seconds required before the user can send another
/// message; <c>0</c> if disabled.
/// <c>null</c> if this is not mentioned in this entry.
/// </returns>
public int? SlowModeInterval { get; }
/// <summary>
/// Gets the value that indicates whether the created channel is NSFW.
/// </summary>
/// <returns>
/// <c>true</c> if the created channel has the NSFW flag enabled; otherwise <c>false</c>.
/// <c>null</c> if this is not mentioned in this entry.
/// </returns>
public bool? IsNsfw { get; }
/// <summary>
/// Gets the bit-rate that the clients in the created voice channel are requested to use.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the bit-rate (bps) that the created voice channel defines and requests the
/// client(s) to use.
/// <c>null</c> if this is not mentioned in this entry.
/// </returns>
public int? Bitrate { get; }
/// <summary>
/// Gets the thread archive duration that was set in the created channel.
/// </summary>
public ThreadArchiveDuration? AutoArchiveDuration { get; }
/// <summary>
/// Gets the default thread archive duration that was set in the created channel.
/// </summary>
public ThreadArchiveDuration? DefaultAutoArchiveDuration { get; }
/// <summary>
/// Gets the default slow mode interval that will be set in child threads in the channel.
/// </summary>
public int? DefaultSlowModeInterval { get; }
/// <summary>
/// Gets the topic that was set in the created channel.
/// </summary>
public string Topic { get; }
/// <summary>
/// Gets tags available in the created forum channel.
/// </summary>
public IReadOnlyCollection<ForumTag> AvailableTags { get; }
/// <summary>
/// Gets the default reaction added to posts in the created forum channel.
/// </summary>
public IEmote DefaultReactionEmoji { get; }
/// <summary>
/// Gets the user limit configured in the created voice channel.
/// </summary>
public int? UserLimit { get; }
/// <summary>
/// Gets the video quality mode configured in the created voice channel.
/// </summary>
public VideoQualityMode? VideoQualityMode { get; }
/// <summary>
/// Gets the region configured in the created voice channel.
/// </summary>
public string RtcRegion { get; }
/// <summary>
/// Gets channel flags configured for the created channel.
/// </summary>
public ChannelFlags? Flags { get; }
}

View File

@@ -0,0 +1,182 @@
using Discord.Rest;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Model = Discord.API.AuditLogs.ChannelInfoAuditLogModel;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a channel deletion.
/// </summary>
public class SocketChannelDeleteAuditLogData : ISocketAuditLogData
{
private SocketChannelDeleteAuditLogData(Model model, EntryModel entry)
{
ChannelId = entry.TargetId!.Value;
ChannelType = model.Type;
ChannelName = model.Name;
Topic = model.Topic;
IsNsfw = model.IsNsfw;
Bitrate = model.Bitrate;
DefaultArchiveDuration = model.DefaultArchiveDuration;
SlowModeInterval = model.RateLimitPerUser;
ForumTags = model.AvailableTags?.Select(
x => new ForumTag(x.Id,
x.Name,
x.EmojiId.GetValueOrDefault(null),
x.EmojiName.GetValueOrDefault(null),
x.Moderated)).ToImmutableArray();
if (model.DefaultEmoji is not null)
{
if (model.DefaultEmoji.EmojiId.HasValue && model.DefaultEmoji.EmojiId.Value != 0)
DefaultReactionEmoji = new Emote(model.DefaultEmoji.EmojiId.GetValueOrDefault(), null, false);
else if (model.DefaultEmoji.EmojiName.IsSpecified)
DefaultReactionEmoji = new Emoji(model.DefaultEmoji.EmojiName.Value);
else
DefaultReactionEmoji = null;
}
else
DefaultReactionEmoji = null;
AutoArchiveDuration = model.AutoArchiveDuration;
DefaultSlowModeInterval = model.DefaultThreadRateLimitPerUser;
VideoQualityMode = model.VideoQualityMode;
RtcRegion = model.Region;
Flags = model.Flags;
UserLimit = model.UserLimit;
Overwrites = model.Overwrites?.Select(x
=> new Overwrite(x.TargetId,
x.TargetType,
new OverwritePermissions(x.Allow, x.Deny))).ToImmutableArray();
}
internal static SocketChannelDeleteAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (data, _) = AuditLogHelper.CreateAuditLogEntityInfo<Model>(changes, discord);
return new SocketChannelDeleteAuditLogData(data, entry);
}
/// <summary>
/// Gets the snowflake ID of the deleted channel.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier for the deleted channel.
/// </returns>
public ulong ChannelId { get; }
/// <summary>
/// Gets the name of the deleted channel.
/// </summary>
/// <returns>
/// A string containing the name of the deleted channel.
/// </returns>
public string ChannelName { get; }
/// <summary>
/// Gets the type of the deleted channel.
/// </summary>
/// <returns>
/// The type of channel that was deleted.
/// </returns>
public ChannelType? ChannelType { get; }
/// <summary>
/// Gets the slow-mode delay of the deleted channel.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the time in seconds required before the user can send another
/// message; <c>0</c> if disabled.
/// <c>null</c> if this is not mentioned in this entry.
/// </returns>
public int? SlowModeInterval { get; }
/// <summary>
/// Gets the value that indicates whether the deleted channel was NSFW.
/// </summary>
/// <returns>
/// <c>true</c> if this channel had the NSFW flag enabled; otherwise <c>false</c>.
/// <c>null</c> if this is not mentioned in this entry.
/// </returns>
public bool? IsNsfw { get; }
/// <summary>
/// Gets the bit-rate of this channel if applicable.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the bit-rate set of the voice channel.
/// <c>null</c> if this is not mentioned in this entry.
/// </returns>
public int? Bitrate { get; }
/// <summary>
/// Gets a collection of permission overwrites that was assigned to the deleted channel.
/// </summary>
/// <returns>
/// A collection of permission <see cref="Overwrite"/>.
/// </returns>
public IReadOnlyCollection<Overwrite> Overwrites { get; }
/// <summary>
/// Gets the user limit configured in the created voice channel.
/// </summary>
public int? UserLimit { get; }
/// <summary>
/// Gets the video quality mode configured in the created voice channel.
/// </summary>
public VideoQualityMode? VideoQualityMode { get; }
/// <summary>
/// Gets the region configured in the created voice channel.
/// </summary>
public string RtcRegion { get; }
/// <summary>
/// Gets channel flags configured for the created channel.
/// </summary>
public ChannelFlags? Flags { get; }
/// <summary>
/// Gets the thread archive duration that was configured for the created channel.
/// </summary>
public ThreadArchiveDuration? AutoArchiveDuration { get; }
/// <summary>
/// Gets the default slow mode interval that was configured for the channel.
/// </summary>
public int? DefaultSlowModeInterval { get; }
/// <inheritdoc cref="ITextChannel.DefaultArchiveDuration"/>
/// <remarks>
/// <see langword="null" /> if the value was not specified in this entry..
/// </remarks>
public ThreadArchiveDuration? DefaultArchiveDuration { get; }
/// <inheritdoc cref="IForumChannel.Tags"/>
/// <remarks>
/// <see langword="null" /> if the value was not specified in this entry..
/// </remarks>
public IReadOnlyCollection<ForumTag> ForumTags { get; }
/// <inheritdoc cref="ITextChannel.Topic"/>
/// <remarks>
/// <see langword="null" /> if the value was not specified in this entry..
/// </remarks>
public string Topic { get; }
/// <inheritdoc cref="IForumChannel.DefaultReactionEmoji"/>
/// <remarks>
/// <see langword="null" /> if the value was not specified in this entry..
/// </remarks>
public IEmote DefaultReactionEmoji { get; }
}

View File

@@ -0,0 +1,142 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Model = Discord.API.AuditLogs.ChannelInfoAuditLogModel;
namespace Discord.WebSocket;
/// <summary>
/// Represents information for a channel.
/// </summary>
public struct SocketChannelInfo
{
internal SocketChannelInfo(Model model)
{
Name = model.Name;
Topic = model.Topic;
IsNsfw = model.IsNsfw;
Bitrate = model.Bitrate;
DefaultArchiveDuration = model.DefaultArchiveDuration;
ChannelType = model.Type;
SlowModeInterval = model.RateLimitPerUser;
ForumTags = model.AvailableTags?.Select(
x => new ForumTag(x.Id,
x.Name,
x.EmojiId.GetValueOrDefault(null),
x.EmojiName.GetValueOrDefault(null),
x.Moderated)).ToImmutableArray();
if (model.DefaultEmoji is not null)
{
if (model.DefaultEmoji.EmojiId.HasValue && model.DefaultEmoji.EmojiId.Value != 0)
DefaultReactionEmoji = new Emote(model.DefaultEmoji.EmojiId.GetValueOrDefault(), null, false);
else if (model.DefaultEmoji.EmojiName.IsSpecified)
DefaultReactionEmoji = new Emoji(model.DefaultEmoji.EmojiName.Value);
else
DefaultReactionEmoji = null;
}
else
DefaultReactionEmoji = null;
AutoArchiveDuration = model.AutoArchiveDuration;
DefaultSlowModeInterval = model.DefaultThreadRateLimitPerUser;
VideoQualityMode = model.VideoQualityMode;
RTCRegion = model.Region;
Flags = model.Flags;
UserLimit = model.UserLimit;
}
/// <inheritdoc cref="IChannel.Name"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public string Name { get; }
/// <inheritdoc cref="ITextChannel.Topic"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public string Topic { get; }
/// <inheritdoc cref="ITextChannel.SlowModeInterval"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public int? SlowModeInterval { get; }
/// <inheritdoc cref="ITextChannel.IsNsfw"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public bool? IsNsfw { get; }
/// <inheritdoc cref="IVoiceChannel.Bitrate"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public int? Bitrate { get; }
/// <summary>
/// Gets the type of this channel.
/// </summary>
/// <returns>
/// The channel type of this channel; <c>null</c> if not applicable.
/// </returns>
public ChannelType? ChannelType { get; }
/// <inheritdoc cref="ITextChannel.DefaultArchiveDuration"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public ThreadArchiveDuration? DefaultArchiveDuration { get; }
/// <inheritdoc cref="IForumChannel.Tags"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public IReadOnlyCollection<ForumTag> ForumTags { get; }
/// <inheritdoc cref="IForumChannel.DefaultReactionEmoji"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public IEmote DefaultReactionEmoji { get; }
/// <inheritdoc cref="IVoiceChannel.UserLimit"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public int? UserLimit { get; }
/// <inheritdoc cref="IVoiceChannel.VideoQualityMode"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public VideoQualityMode? VideoQualityMode { get; }
/// <inheritdoc cref="IAudioChannel.RTCRegion"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public string RTCRegion { get; }
/// <inheritdoc cref="IGuildChannel.Flags"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public ChannelFlags? Flags { get; }
/// <inheritdoc cref="IThreadChannel.AutoArchiveDuration"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public ThreadArchiveDuration? AutoArchiveDuration { get; }
/// <inheritdoc cref="IForumChannel.DefaultSlowModeInterval"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public int? DefaultSlowModeInterval { get; }
}

View File

@@ -0,0 +1,53 @@
using Discord.Rest;
using Discord.API.AuditLogs;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket
{
/// <summary>
/// Contains a piece of audit log data related to a channel update.
/// </summary>
public class SocketChannelUpdateAuditLogData : ISocketAuditLogData
{
private SocketChannelUpdateAuditLogData(ulong id, SocketChannelInfo before, SocketChannelInfo after)
{
ChannelId = id;
Before = before;
After = after;
}
internal static SocketChannelUpdateAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (before, after) = AuditLogHelper.CreateAuditLogEntityInfo<ChannelInfoAuditLogModel>(changes, discord);
return new SocketChannelUpdateAuditLogData(entry.TargetId!.Value, new(before), new(after));
}
/// <summary>
/// Gets the snowflake ID of the updated channel.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier for the updated channel.
/// </returns>
public ulong ChannelId { get; }
/// <summary>
/// Gets the channel information before the changes.
/// </summary>
/// <returns>
/// An information object containing the original channel information before the changes were made.
/// </returns>
public SocketChannelInfo Before { get; }
/// <summary>
/// Gets the channel information after the changes.
/// </summary>
/// <returns>
/// An information object containing the channel information after the changes were made.
/// </returns>
public SocketChannelInfo After { get; }
}
}

View File

@@ -0,0 +1,63 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to an application command permission update.
/// </summary>
public class SocketCommandPermissionUpdateAuditLogData : ISocketAuditLogData
{
internal SocketCommandPermissionUpdateAuditLogData(IReadOnlyCollection<ApplicationCommandPermission> before, IReadOnlyCollection<ApplicationCommandPermission> after,
ulong commandId, ulong appId)
{
Before = before;
After = after;
ApplicationId = appId;
CommandId = commandId;
}
internal static SocketCommandPermissionUpdateAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var before = new List<ApplicationCommandPermission>();
var after = new List<ApplicationCommandPermission>();
foreach (var change in changes)
{
var oldValue = change.OldValue?.ToObject<API.ApplicationCommandPermissions>();
var newValue = change.NewValue?.ToObject<API.ApplicationCommandPermissions>();
if (oldValue is not null)
before.Add(new ApplicationCommandPermission(oldValue.Id, oldValue.Type, oldValue.Permission));
if (newValue is not null)
after.Add(new ApplicationCommandPermission(newValue.Id, newValue.Type, newValue.Permission));
}
return new(before.ToImmutableArray(), after.ToImmutableArray(), entry.TargetId!.Value, entry.Options.ApplicationId!.Value);
}
/// <summary>
/// Gets the ID of the app whose permissions were targeted.
/// </summary>
public ulong ApplicationId { get; set; }
/// <summary>
/// Gets the id of the application command which permissions were updated.
/// </summary>
public ulong CommandId { get; }
/// <summary>
/// Gets values of the permissions before the change if available.
/// </summary>
public IReadOnlyCollection<ApplicationCommandPermission> Before { get; }
/// <summary>
/// Gets values of the permissions after the change if available.
/// </summary>
public IReadOnlyCollection<ApplicationCommandPermission> After { get; }
}

View File

@@ -0,0 +1,41 @@
using System.Linq;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to an emoji creation.
/// </summary>
public class SocketEmoteCreateAuditLogData : ISocketAuditLogData
{
private SocketEmoteCreateAuditLogData(ulong id, string name)
{
EmoteId = id;
Name = name;
}
internal static SocketEmoteCreateAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var change = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "name");
var emoteName = change.NewValue?.ToObject<string>(discord.ApiClient.Serializer);
return new SocketEmoteCreateAuditLogData(entry.TargetId!.Value, emoteName);
}
/// <summary>
/// Gets the snowflake ID of the created emoji.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier for the created emoji.
/// </returns>
public ulong EmoteId { get; }
/// <summary>
/// Gets the name of the created emoji.
/// </summary>
/// <returns>
/// A string containing the name of the created emoji.
/// </returns>
public string Name { get; }
}

View File

@@ -0,0 +1,41 @@
using System.Linq;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to an emoji deletion.
/// </summary>
public class SocketEmoteDeleteAuditLogData : ISocketAuditLogData
{
private SocketEmoteDeleteAuditLogData(ulong id, string name)
{
EmoteId = id;
Name = name;
}
internal static SocketEmoteDeleteAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var change = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "name");
var emoteName = change.OldValue?.ToObject<string>(discord.ApiClient.Serializer);
return new SocketEmoteDeleteAuditLogData(entry.TargetId!.Value, emoteName);
}
/// <summary>
/// Gets the snowflake ID of the deleted emoji.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier for the deleted emoji.
/// </returns>
public ulong EmoteId { get; }
/// <summary>
/// Gets the name of the deleted emoji.
/// </summary>
/// <returns>
/// A string containing the name of the deleted emoji.
/// </returns>
public string Name { get; }
}

View File

@@ -0,0 +1,52 @@
using System.Linq;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to an emoji update.
/// </summary>
public class SocketEmoteUpdateAuditLogData : ISocketAuditLogData
{
private SocketEmoteUpdateAuditLogData(ulong id, string oldName, string newName)
{
EmoteId = id;
OldName = oldName;
NewName = newName;
}
internal static SocketEmoteUpdateAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var change = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "name");
var newName = change.NewValue?.ToObject<string>(discord.ApiClient.Serializer);
var oldName = change.OldValue?.ToObject<string>(discord.ApiClient.Serializer);
return new SocketEmoteUpdateAuditLogData(entry.TargetId!.Value, oldName, newName);
}
/// <summary>
/// Gets the snowflake ID of the updated emoji.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier of the updated emoji.
/// </returns>
public ulong EmoteId { get; }
/// <summary>
/// Gets the new name of the updated emoji.
/// </summary>
/// <returns>
/// A string containing the new name of the updated emoji.
/// </returns>
public string NewName { get; }
/// <summary>
/// Gets the old name of the updated emoji.
/// </summary>
/// <returns>
/// A string containing the old name of the updated emoji.
/// </returns>
public string OldName { get; }
}

View File

@@ -0,0 +1,191 @@
using Model = Discord.API.AuditLogs.GuildInfoAuditLogModel;
namespace Discord.WebSocket;
/// <summary>
/// Represents information for a guild.
/// </summary>
public class SocketGuildInfo
{
internal static SocketGuildInfo Create(Model model)
{
return new()
{
Name = model.Name,
AfkTimeout = model.AfkTimeout.GetValueOrDefault(),
IsEmbeddable = model.IsEmbeddable,
DefaultMessageNotifications = model.DefaultMessageNotifications,
MfaLevel = model.MfaLevel,
Description = model.Description,
PreferredLocale = model.PreferredLocale,
IconHash = model.IconHash,
OwnerId = model.OwnerId,
AfkChannelId = model.AfkChannelId,
ApplicationId = model.ApplicationId,
BannerId = model.Banner,
DiscoverySplashId = model.DiscoverySplash,
EmbedChannelId = model.EmbeddedChannelId,
ExplicitContentFilter = model.ExplicitContentFilterLevel,
IsBoostProgressBarEnabled = model.ProgressBarEnabled,
NsfwLevel = model.NsfwLevel,
PublicUpdatesChannelId = model.PublicUpdatesChannelId,
RegionId = model.RegionId,
RulesChannelId = model.RulesChannelId,
SplashId = model.Splash,
SystemChannelFlags = model.SystemChannelFlags,
SystemChannelId = model.SystemChannelId,
VanityURLCode = model.VanityUrl,
VerificationLevel = model.VerificationLevel
};
}
/// <inheritdoc cref="IGuild.Name"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public string Name { get; private set; }
/// <inheritdoc cref="IGuild.AFKTimeout"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public int? AfkTimeout { get; private set; }
/// <inheritdoc cref="IGuild.IsWidgetEnabled"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public bool? IsEmbeddable { get; private set; }
/// <inheritdoc cref="IGuild.DefaultMessageNotifications"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public DefaultMessageNotifications? DefaultMessageNotifications { get; private set; }
/// <inheritdoc cref="IGuild.MfaLevel"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public MfaLevel? MfaLevel { get; private set; }
/// <inheritdoc cref="IGuild.VerificationLevel"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public VerificationLevel? VerificationLevel { get; private set; }
/// <inheritdoc cref="IGuild.ExplicitContentFilter"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public ExplicitContentFilterLevel? ExplicitContentFilter { get; private set; }
/// <inheritdoc cref="IGuild.IconId"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public string IconHash { get; private set; }
/// <inheritdoc cref="IGuild.SplashId"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public string SplashId { get; private set; }
/// <inheritdoc cref="IGuild.DiscoverySplashId"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public string DiscoverySplashId { get; private set; }
/// <inheritdoc cref="IGuild.AFKChannelId"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public ulong? AfkChannelId { get; private set; }
/// <inheritdoc cref="IGuild.WidgetChannelId"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public ulong? EmbedChannelId { get; private set; }
/// <inheritdoc cref="IGuild.SystemChannelId"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public ulong? SystemChannelId { get; private set; }
/// <inheritdoc cref="IGuild.RulesChannelId"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public ulong? RulesChannelId { get; private set; }
/// <inheritdoc cref="IGuild.PublicUpdatesChannelId"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public ulong? PublicUpdatesChannelId { get; private set; }
/// <inheritdoc cref="IGuild.OwnerId"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public ulong? OwnerId { get; private set; }
/// <inheritdoc cref="IGuild.ApplicationId"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public ulong? ApplicationId { get; private set; }
/// <inheritdoc cref="IGuild.VoiceRegionId"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public string RegionId { get; private set; }
/// <inheritdoc cref="IGuild.BannerId"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public string BannerId { get; private set; }
/// <inheritdoc cref="IGuild.VanityURLCode"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public string VanityURLCode { get; private set; }
/// <inheritdoc cref="IGuild.SystemChannelFlags"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public SystemChannelMessageDeny? SystemChannelFlags { get; private set; }
/// <inheritdoc cref="IGuild.Description"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public string Description { get; private set; }
/// <inheritdoc cref="IGuild.PreferredLocale"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public string PreferredLocale { get; private set; }
/// <inheritdoc cref="IGuild.NsfwLevel"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public NsfwLevel? NsfwLevel { get; private set; }
/// <inheritdoc cref="IGuild.IsBoostProgressBarEnabled"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public bool? IsBoostProgressBarEnabled { get; private set; }
}

View File

@@ -0,0 +1,42 @@
using System.Linq;
using EntryModel = Discord.API.AuditLogEntry;
using InfoModel = Discord.API.AuditLogs.GuildInfoAuditLogModel;
namespace Discord.WebSocket
{
/// <summary>
/// Contains a piece of audit log data related to a guild update.
/// </summary>
public class SocketGuildUpdateAuditLogData : ISocketAuditLogData
{
private SocketGuildUpdateAuditLogData(SocketGuildInfo before, SocketGuildInfo after)
{
Before = before;
After = after;
}
internal static SocketGuildUpdateAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var info = Rest.AuditLogHelper.CreateAuditLogEntityInfo<InfoModel>(entry.Changes, discord);
var data = new SocketGuildUpdateAuditLogData(SocketGuildInfo.Create(info.Item1), SocketGuildInfo.Create(info.Item2));
return data;
}
/// <summary>
/// Gets the guild information before the changes.
/// </summary>
/// <returns>
/// An information object containing the original guild information before the changes were made.
/// </returns>
public SocketGuildInfo Before { get; }
/// <summary>
/// Gets the guild information after the changes.
/// </summary>
/// <returns>
/// An information object containing the guild information after the changes were made.
/// </returns>
public SocketGuildInfo After { get; }
}
}

View File

@@ -0,0 +1,30 @@
using Discord.API.AuditLogs;
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to an integration authorization.
/// </summary>
public class SocketIntegrationCreatedAuditLogData : ISocketAuditLogData
{
internal SocketIntegrationCreatedAuditLogData(SocketIntegrationInfo info)
{
Data = info;
}
internal static SocketIntegrationCreatedAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (_, data) = AuditLogHelper.CreateAuditLogEntityInfo<IntegrationInfoAuditLogModel>(changes, discord);
return new(new SocketIntegrationInfo(data));
}
/// <summary>
/// Gets the integration information after the changes.
/// </summary>
public SocketIntegrationInfo Data { get; }
}

View File

@@ -0,0 +1,30 @@
using Discord.API.AuditLogs;
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to an integration removal.
/// </summary>
public class SocketIntegrationDeletedAuditLogData : ISocketAuditLogData
{
internal SocketIntegrationDeletedAuditLogData(SocketIntegrationInfo info)
{
Data = info;
}
internal static SocketIntegrationDeletedAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (data, _) = AuditLogHelper.CreateAuditLogEntityInfo<IntegrationInfoAuditLogModel>(changes, discord);
return new(new SocketIntegrationInfo(data));
}
/// <summary>
/// Gets the integration information before the changes.
/// </summary>
public SocketIntegrationInfo Data { get; }
}

View File

@@ -0,0 +1,69 @@
using Discord.API.AuditLogs;
using System.Collections.Generic;
using System.Collections.Immutable;
namespace Discord.WebSocket;
/// <summary>
/// Represents information for an integration.
/// </summary>
public class SocketIntegrationInfo
{
internal SocketIntegrationInfo(IntegrationInfoAuditLogModel model)
{
Name = model.Name;
Type = model.Type;
EnableEmojis = model.EnableEmojis;
Enabled = model.Enabled;
Scopes = model.Scopes?.ToImmutableArray();
ExpireBehavior = model.ExpireBehavior;
ExpireGracePeriod = model.ExpireGracePeriod;
Syncing = model.Syncing;
RoleId = model.RoleId;
}
/// <summary>
/// Gets the name of the integration. <see landword="null"/> if the property was not mentioned in this audit log.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Gets the type of the integration. <see landword="null"/> if the property was not mentioned in this audit log.
/// </summary>
public string Type { get; set; }
/// <summary>
/// Gets if the integration is enabled. <see landword="null"/> if the property was not mentioned in this audit log.
/// </summary>
public bool? Enabled { get; set; }
/// <summary>
/// Gets if syncing is enabled for this integration. <see landword="null"/> if the property was not mentioned in this audit log.
/// </summary>
public bool? Syncing { get; set; }
/// <summary>
/// Gets the id of the role that this integration uses for subscribers. <see landword="null"/> if the property was not mentioned in this audit log.
/// </summary>
public ulong? RoleId { get; set; }
/// <summary>
/// Gets whether emoticons should be synced for this integration. <see landword="null"/> if the property was not mentioned in this audit log.
/// </summary>
public bool? EnableEmojis { get; set; }
/// <summary>
/// Gets the behavior of expiring subscribers. <see landword="null"/> if the property was not mentioned in this audit log.
/// </summary>
public IntegrationExpireBehavior? ExpireBehavior { get; set; }
/// <summary>
/// Gets the grace period (in days) before expiring subscribers. <see landword="null"/> if the property was not mentioned in this audit log.
/// </summary>
public int? ExpireGracePeriod { get; set; }
/// <summary>
/// Gets the scopes the application has been authorized for. <see landword="null"/> if the property was not mentioned in this audit log.
/// </summary>
public IReadOnlyCollection<string> Scopes { get; set; }
}

View File

@@ -0,0 +1,36 @@
using Discord.API.AuditLogs;
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to an integration update.
/// </summary>
public class SocketIntegrationUpdatedAuditLogData : ISocketAuditLogData
{
internal SocketIntegrationUpdatedAuditLogData(SocketIntegrationInfo before, SocketIntegrationInfo after)
{
Before = before;
After = after;
}
internal static SocketIntegrationUpdatedAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (before, after) = AuditLogHelper.CreateAuditLogEntityInfo<IntegrationInfoAuditLogModel>(changes, discord);
return new(new SocketIntegrationInfo(before), new SocketIntegrationInfo(after));
}
/// <summary>
/// Gets the integration information before the changes.
/// </summary>
public SocketIntegrationInfo Before { get; }
/// <summary>
/// Gets the integration information after the changes.
/// </summary>
public SocketIntegrationInfo After { get; }
}

View File

@@ -0,0 +1,110 @@
using Discord.API.AuditLogs;
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to an invite creation.
/// </summary>
public class SocketInviteCreateAuditLogData : ISocketAuditLogData
{
private SocketInviteCreateAuditLogData(InviteInfoAuditLogModel model, Cacheable<SocketUser, RestUser, IUser, ulong>? inviter)
{
MaxAge = model.MaxAge!.Value;
Code = model.Code;
Temporary = model.Temporary!.Value;
ChannelId = model.ChannelId!.Value;
Uses = model.Uses!.Value;
MaxUses = model.MaxUses!.Value;
Creator = inviter;
}
internal static SocketInviteCreateAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (_, data) = AuditLogHelper.CreateAuditLogEntityInfo<InviteInfoAuditLogModel>(changes, discord);
Cacheable<SocketUser, RestUser, IUser, ulong>? cacheableUser = null;
if (data.InviterId is not null)
{
var cachedUser = discord.GetUser(data.InviterId.Value);
cacheableUser = new Cacheable<SocketUser, RestUser, IUser, ulong>(
cachedUser,
data.InviterId.Value,
cachedUser is not null,
async () =>
{
var user = await discord.ApiClient.GetUserAsync(data.InviterId.Value);
return user is not null ? RestUser.Create(discord, user) : null;
});
}
return new SocketInviteCreateAuditLogData(data, cacheableUser);
}
/// <summary>
/// Gets the time (in seconds) until the invite expires.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the time in seconds until this invite expires.
/// </returns>
public int MaxAge { get; }
/// <summary>
/// Gets the unique identifier for this invite.
/// </summary>
/// <returns>
/// A string containing the invite code (e.g. <c>FTqNnyS</c>).
/// </returns>
public string Code { get; }
/// <summary>
/// Gets a value that determines whether the invite is a temporary one.
/// </summary>
/// <returns>
/// <c>true</c> if users accepting this invite will be removed from the guild when they log off; otherwise
/// <c>false</c>.
/// </returns>
public bool Temporary { get; }
/// <summary>
/// Gets the user that created this invite if available.
/// </summary>
/// <remarks>
/// Will be <see langword="null"/> if the user is a 'Deleted User#....' because Discord does send user data for deleted users.
/// </remarks>
/// <returns>
/// A user that created this invite or <see langword="null"/>.
/// </returns>
public Cacheable<SocketUser, RestUser, IUser, ulong>? Creator { get; }
/// <summary>
/// Gets the ID of the channel this invite is linked to.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the channel snowflake identifier that the invite points to.
/// </returns>
public ulong ChannelId { get; }
/// <summary>
/// Gets the number of times this invite has been used.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the number of times this invite was used.
/// </returns>
public int Uses { get; }
/// <summary>
/// Gets the max number of uses this invite may have.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the number of uses this invite may be accepted until it is removed
/// from the guild; <c>null</c> if none is set.
/// </returns>
public int MaxUses { get; }
}

View File

@@ -0,0 +1,109 @@
using Discord.API.AuditLogs;
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to an invite removal.
/// </summary>
public class SocketInviteDeleteAuditLogData : ISocketAuditLogData
{
private SocketInviteDeleteAuditLogData(InviteInfoAuditLogModel model, Cacheable<SocketUser, RestUser, IUser, ulong>? inviter)
{
MaxAge = model.MaxAge!.Value;
Code = model.Code;
Temporary = model.Temporary!.Value;
Creator = inviter;
ChannelId = model.ChannelId!.Value;
Uses = model.Uses!.Value;
MaxUses = model.MaxUses!.Value;
}
internal static SocketInviteDeleteAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (data, _) = AuditLogHelper.CreateAuditLogEntityInfo<InviteInfoAuditLogModel>(changes, discord);
Cacheable<SocketUser, RestUser, IUser, ulong>? cacheableUser = null;
if (data.InviterId != null)
{
var cachedUser = discord.GetUser(data.InviterId.Value);
cacheableUser = new Cacheable<SocketUser, RestUser, IUser, ulong>(
cachedUser,
data.InviterId.Value,
cachedUser is not null,
async () =>
{
var user = await discord.ApiClient.GetUserAsync(data.InviterId.Value);
return user is not null ? RestUser.Create(discord, user) : null;
});
}
return new SocketInviteDeleteAuditLogData(data, cacheableUser);
}
/// <summary>
/// Gets the time (in seconds) until the invite expires.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the time in seconds until this invite expires.
/// </returns>
public int MaxAge { get; }
/// <summary>
/// Gets the unique identifier for this invite.
/// </summary>
/// <returns>
/// A string containing the invite code (e.g. <c>FTqNnyS</c>).
/// </returns>
public string Code { get; }
/// <summary>
/// Gets a value that indicates whether the invite is a temporary one.
/// </summary>
/// <returns>
/// <c>true</c> if users accepting this invite will be removed from the guild when they log off; otherwise
/// <c>false</c>.
/// </returns>
public bool Temporary { get; }
/// <summary>
/// Gets the user that created this invite if available.
/// </summary>
/// <remarks>
/// Will be <see langword="null"/> if the user is a 'Deleted User#....' because Discord does send user data for deleted users.
/// </remarks>
/// <returns>
/// A user that created this invite or <see langword="null"/>.
/// </returns>
public Cacheable<SocketUser, RestUser, IUser, ulong>? Creator { get; }
/// <summary>
/// Gets the ID of the channel this invite is linked to.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the channel snowflake identifier that the invite points to.
/// </returns>
public ulong ChannelId { get; }
/// <summary>
/// Gets the number of times this invite has been used.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the number of times this invite has been used.
/// </returns>
public int Uses { get; }
/// <summary>
/// Gets the max number of uses this invite may have.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the number of uses this invite may be accepted until it is removed
/// from the guild; <c>null</c> if none is set.
/// </returns>
public int MaxUses { get; }
}

View File

@@ -0,0 +1,68 @@
using Model = Discord.API.AuditLogs.InviteInfoAuditLogModel;
namespace Discord.WebSocket;
/// <summary>
/// Represents information for an invite.
/// </summary>
public struct SocketInviteInfo
{
internal SocketInviteInfo(Model model)
{
MaxAge = model.MaxAge;
Code = model.Code;
Temporary = model.Temporary;
ChannelId = model.ChannelId;
MaxUses = model.MaxUses;
CreatorId = model.InviterId;
}
/// <summary>
/// Gets the time (in seconds) until the invite expires.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the time in seconds until this invite expires; <c>null</c> if this
/// invite never expires or not specified.
/// </returns>
public int? MaxAge { get; }
/// <summary>
/// Gets the unique identifier for this invite.
/// </summary>
/// <returns>
/// A string containing the invite code (e.g. <c>FTqNnyS</c>).
/// </returns>
public string Code { get; }
/// <summary>
/// Gets a value that indicates whether the invite is a temporary one.
/// </summary>
/// <returns>
/// <c>true</c> if users accepting this invite will be removed from the guild when they log off,
/// <c>false</c> if not; <c>null</c> if not specified.
/// </returns>
public bool? Temporary { get; }
/// <summary>
/// Gets the ID of the channel this invite is linked to.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the channel snowflake identifier that the invite points to;
/// <c>null</c> if not specified.
/// </returns>
public ulong? ChannelId { get; }
/// <summary>
/// Gets the max number of uses this invite may have.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the number of uses this invite may be accepted until it is removed
/// from the guild; <c>null</c> if none is specified.
/// </returns>
public int? MaxUses { get; }
/// <summary>
/// Gets the id of the user created this invite.
/// </summary>
public ulong? CreatorId { get; }
}

View File

@@ -0,0 +1,42 @@
using Discord.API.AuditLogs;
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data relating to an invite update.
/// </summary>
public class SocketInviteUpdateAuditLogData : ISocketAuditLogData
{
private SocketInviteUpdateAuditLogData(SocketInviteInfo before, SocketInviteInfo after)
{
Before = before;
After = after;
}
internal static SocketInviteUpdateAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (before, after) = AuditLogHelper.CreateAuditLogEntityInfo<InviteInfoAuditLogModel>(changes, discord);
return new SocketInviteUpdateAuditLogData(new(before), new(after));
}
/// <summary>
/// Gets the invite information before the changes.
/// </summary>
/// <returns>
/// An information object containing the original invite information before the changes were made.
/// </returns>
public SocketInviteInfo Before { get; }
/// <summary>
/// Gets the invite information after the changes.
/// </summary>
/// <returns>
/// An information object containing the invite information after the changes were made.
/// </returns>
public SocketInviteInfo After { get; }
}

View File

@@ -0,0 +1,43 @@
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a kick.
/// </summary>
public class SocketKickAuditLogData : ISocketAuditLogData
{
private SocketKickAuditLogData(Cacheable<SocketUser, RestUser, IUser, ulong> user)
{
Target = user;
}
internal static SocketKickAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var cachedUser = discord.GetUser(entry.Id);
var cacheableUser = new Cacheable<SocketUser, RestUser, IUser, ulong>(
cachedUser,
entry.Id,
cachedUser is not null,
async () =>
{
var user = await discord.ApiClient.GetUserAsync(entry.TargetId!.Value);
return user is not null ? RestUser.Create(discord, user) : null;
});
return new SocketKickAuditLogData(cacheableUser);
}
/// <summary>
/// Gets the user that was kicked.
/// </summary>
/// <remarks>
/// Download method may return <see langword="null"/> if the user is a 'Deleted User#....'
/// because Discord does send user data for deleted users.
/// </remarks>
/// <returns>
/// A cacheable user object representing the kicked user.
/// </returns>
public Cacheable<SocketUser, RestUser, IUser, ulong> Target { get; }
}

View File

@@ -0,0 +1,27 @@
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to disconnecting members from voice channels.
/// </summary>
public class SocketMemberDisconnectAuditLogData : ISocketAuditLogData
{
private SocketMemberDisconnectAuditLogData(int count)
{
MemberCount = count;
}
internal static SocketMemberDisconnectAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
return new SocketMemberDisconnectAuditLogData(entry.Options.Count!.Value);
}
/// <summary>
/// Gets the number of members that were disconnected.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the number of members that were disconnected from a voice channel.
/// </returns>
public int MemberCount { get; }
}

View File

@@ -0,0 +1,36 @@
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to moving members between voice channels.
/// </summary>
public class SocketMemberMoveAuditLogData : ISocketAuditLogData
{
private SocketMemberMoveAuditLogData(ulong channelId, int count)
{
ChannelId = channelId;
MemberCount = count;
}
internal static SocketMemberMoveAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
return new SocketMemberMoveAuditLogData(entry.Options.ChannelId!.Value, entry.Options.Count!.Value);
}
/// <summary>
/// Gets the ID of the channel that the members were moved to.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier for the channel that the members were moved to.
/// </returns>
public ulong ChannelId { get; }
/// <summary>
/// Gets the number of members that were moved.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the number of members that were moved to another voice channel.
/// </returns>
public int MemberCount { get; }
}

View File

@@ -0,0 +1,58 @@
using Discord.Rest;
using System.Collections.Generic;
using System.Linq;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a change in a guild member's roles.
/// </summary>
public class SocketMemberRoleAuditLogData : ISocketAuditLogData
{
private SocketMemberRoleAuditLogData(IReadOnlyCollection<SocketMemberRoleEditInfo> roles, Cacheable<SocketUser, RestUser, IUser, ulong> target)
{
Roles = roles;
Target = target;
}
internal static SocketMemberRoleAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var roleInfos = changes.SelectMany(x => x.NewValue.ToObject<API.Role[]>(discord.ApiClient.Serializer),
(model, role) => new { model.ChangedProperty, Role = role })
.Select(x => new SocketMemberRoleEditInfo(x.Role.Name, x.Role.Id, x.ChangedProperty == "$add"))
.ToList();
var cachedUser = discord.GetUser(entry.TargetId!.Value);
var cacheableUser = new Cacheable<SocketUser, RestUser, IUser, ulong>(
cachedUser,
entry.TargetId.Value,
cachedUser is not null,
async () =>
{
var user = await discord.ApiClient.GetUserAsync(entry.TargetId.Value);
return user is not null ? RestUser.Create(discord, user) : null;
});
return new SocketMemberRoleAuditLogData(roleInfos.ToReadOnlyCollection(), cacheableUser);
}
/// <summary>
/// Gets a collection of role changes that were performed on the member.
/// </summary>
/// <returns>
/// A read-only collection of <see cref="SocketMemberRoleEditInfo"/>, containing the roles that were changed on
/// the member.
/// </returns>
public IReadOnlyCollection<SocketMemberRoleEditInfo> Roles { get; }
/// <summary>
/// Gets the user that the roles changes were performed on.
/// </summary>
/// <returns>
/// A cacheable user object representing the user that the role changes were performed on.
/// </returns>
public Cacheable<SocketUser, RestUser, IUser, ulong> Target { get; }
}

View File

@@ -0,0 +1,36 @@
namespace Discord.WebSocket;
/// <summary>
/// An information object representing a change in one of a guild member's roles.
/// </summary>
public struct SocketMemberRoleEditInfo
{
internal SocketMemberRoleEditInfo(string name, ulong roleId, bool added)
{
Name = name;
RoleId = roleId;
Added = added;
}
/// <summary>
/// Gets the name of the role that was changed.
/// </summary>
/// <returns>
/// A string containing the name of the role that was changed.
/// </returns>
public string Name { get; }
/// <summary>
/// Gets the ID of the role that was changed.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier of the role that was changed.
/// </returns>
public ulong RoleId { get; }
/// <summary>
/// Gets a value that indicates whether the role was added to the user.
/// </summary>
/// <returns>
/// <c>true</c> if the role was added to the user; otherwise <c>false</c>.
/// </returns>
public bool Added { get; }
}

View File

@@ -0,0 +1,65 @@
using Discord.API.AuditLogs;
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a change in a guild member.
/// </summary>
public class SocketMemberUpdateAuditLogData : ISocketAuditLogData
{
private SocketMemberUpdateAuditLogData(Cacheable<SocketUser, RestUser, IUser, ulong> target, MemberInfo before, MemberInfo after)
{
Target = target;
Before = before;
After = after;
}
internal static SocketMemberUpdateAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (before, after) = AuditLogHelper.CreateAuditLogEntityInfo<MemberInfoAuditLogModel>(changes, discord);
var cachedUser = discord.GetUser(entry.TargetId!.Value);
var cacheableUser = new Cacheable<SocketUser, RestUser, IUser, ulong>(
cachedUser,
entry.TargetId.Value,
cachedUser is not null,
async () =>
{
var user = await discord.ApiClient.GetUserAsync(entry.TargetId.Value);
return user is not null ? RestUser.Create(discord, user) : null;
});
return new SocketMemberUpdateAuditLogData(cacheableUser, new MemberInfo(before), new MemberInfo(after));
}
/// <summary>
/// Gets the user that the changes were performed on.
/// </summary>
/// <remarks>
/// Will be <see langword="null"/> if the user is a 'Deleted User#....' because Discord does send user data for deleted users.
/// </remarks>
/// <returns>
/// A user object representing the user who the changes were performed on.
/// </returns>
public Cacheable<SocketUser, RestUser, IUser, ulong> Target { get; }
/// <summary>
/// Gets the member information before the changes.
/// </summary>
/// <returns>
/// An information object containing the original member information before the changes were made.
/// </returns>
public MemberInfo Before { get; }
/// <summary>
/// Gets the member information after the changes.
/// </summary>
/// <returns>
/// An information object containing the member information after the changes were made.
/// </returns>
public MemberInfo After { get; }
}

View File

@@ -0,0 +1,37 @@
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to message deletion(s).
/// </summary>
public class SocketMessageBulkDeleteAuditLogData : ISocketAuditLogData
{
private SocketMessageBulkDeleteAuditLogData(ulong channelId, int count)
{
ChannelId = channelId;
MessageCount = count;
}
internal static SocketMessageBulkDeleteAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
return new SocketMessageBulkDeleteAuditLogData(entry.TargetId!.Value, entry.Options.Count!.Value);
}
/// <summary>
/// Gets the ID of the channel that the messages were deleted from.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier for the channel that the messages were
/// deleted from.
/// </returns>
public ulong ChannelId { get; }
/// <summary>
/// Gets the number of messages that were deleted.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the number of messages that were deleted from the channel.
/// </returns>
public int MessageCount { get; }
}

View File

@@ -0,0 +1,62 @@
using Discord.Rest;
using System;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to message deletion(s).
/// </summary>
public class SocketMessageDeleteAuditLogData : ISocketAuditLogData
{
private SocketMessageDeleteAuditLogData(ulong channelId, int count, Cacheable<SocketUser, RestUser, IUser, ulong> user)
{
ChannelId = channelId;
MessageCount = count;
Target = user;
}
internal static SocketMessageDeleteAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var cachedUser = discord.GetUser(entry.TargetId!.Value);
var cacheableUser = new Cacheable<SocketUser, RestUser, IUser, ulong>(
cachedUser,
entry.TargetId.Value,
cachedUser is not null,
async () =>
{
var user = await discord.ApiClient.GetUserAsync(entry.TargetId.Value);
return user is not null ? RestUser.Create(discord, user) : null;
});
return new SocketMessageDeleteAuditLogData(entry.Options.ChannelId!.Value, entry.Options.Count!.Value, cacheableUser);
}
/// <summary>
/// Gets the number of messages that were deleted.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the number of messages that were deleted from the channel.
/// </returns>
public int MessageCount { get; }
/// <summary>
/// Gets the ID of the channel that the messages were deleted from.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier for the channel that the messages were
/// deleted from.
/// </returns>
public ulong ChannelId { get; }
/// <summary>
/// Gets the user of the messages that were deleted.
/// </summary>
/// <remarks>
/// Will be <see langword="null"/> if the user is a 'Deleted User#....' because Discord does send user data for deleted users.
/// </remarks>
/// <returns>
/// A user object representing the user that created the deleted messages.
/// </returns>
public Cacheable<SocketUser, RestUser, IUser, ulong> Target { get; }
}

View File

@@ -0,0 +1,65 @@
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a pinned message.
/// </summary>
public class SocketMessagePinAuditLogData : ISocketAuditLogData
{
private SocketMessagePinAuditLogData(ulong messageId, ulong channelId, Cacheable<SocketUser, RestUser, IUser, ulong>? user)
{
MessageId = messageId;
ChannelId = channelId;
Target = user;
}
internal static SocketMessagePinAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
Cacheable<SocketUser, RestUser, IUser, ulong>? cacheableUser = null;
if (entry.TargetId.HasValue)
{
var cachedUser = discord.GetUser(entry.TargetId.Value);
cacheableUser = new Cacheable<SocketUser, RestUser, IUser, ulong>(
cachedUser,
entry.TargetId.Value,
cachedUser is not null,
async () =>
{
var user = await discord.ApiClient.GetUserAsync(entry.TargetId.Value);
return user is not null ? RestUser.Create(discord, user) : null;
});
}
return new SocketMessagePinAuditLogData(entry.Options.MessageId!.Value, entry.Options.ChannelId!.Value, cacheableUser);
}
/// <summary>
/// Gets the ID of the messages that was pinned.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier for the messages that was pinned.
/// </returns>
public ulong MessageId { get; }
/// <summary>
/// Gets the ID of the channel that the message was pinned from.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier for the channel that the message was pinned from.
/// </returns>
public ulong ChannelId { get; }
/// <summary>
/// Gets the user of the message that was pinned if available.
/// </summary>
/// <remarks>
/// Will be <see langword="null"/> if the user is a 'Deleted User#....' because Discord does send user data for deleted users.
/// </remarks>
/// <returns>
/// A user object representing the user that created the pinned message or <see langword="null"/>.
/// </returns>
public Cacheable<SocketUser, RestUser, IUser, ulong>? Target { get; }
}

View File

@@ -0,0 +1,64 @@
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to an unpinned message.
/// </summary>
public class SocketMessageUnpinAuditLogData : ISocketAuditLogData
{
private SocketMessageUnpinAuditLogData(ulong messageId, ulong channelId, Cacheable<SocketUser, RestUser, IUser, ulong>? user)
{
MessageId = messageId;
ChannelId = channelId;
Target = user;
}
internal static SocketMessageUnpinAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
Cacheable<SocketUser, RestUser, IUser, ulong>? cacheableUser = null;
if (entry.TargetId.HasValue)
{
var cachedUser = discord.GetUser(entry.TargetId.Value);
cacheableUser = new Cacheable<SocketUser, RestUser, IUser, ulong>(
cachedUser,
entry.TargetId.Value,
cachedUser is not null,
async () =>
{
var user = await discord.ApiClient.GetUserAsync(entry.TargetId.Value);
return user is not null ? RestUser.Create(discord, user) : null;
});
}
return new SocketMessageUnpinAuditLogData(entry.Options.MessageId!.Value, entry.Options.ChannelId!.Value, cacheableUser);
}
/// <summary>
/// Gets the ID of the messages that was unpinned.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier for the messages that was unpinned.
/// </returns>
public ulong MessageId { get; }
/// <summary>
/// Gets the ID of the channel that the message was unpinned from.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier for the channel that the message was unpinned from.
/// </returns>
public ulong ChannelId { get; }
/// <summary>
/// Gets the user of the message that was unpinned if available.
/// </summary>
/// <remarks>
/// Will be <see langword="null"/> if the user is a 'Deleted User#....' because Discord does send user data for deleted users.
/// </remarks>
/// <returns>
/// A user object representing the user that created the unpinned message or <see langword="null"/>.
/// </returns>
public Cacheable<SocketUser, RestUser, IUser, ulong>? Target { get; }
}

View File

@@ -0,0 +1,35 @@
using Discord.API.AuditLogs;
using Discord.Rest;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
namespace Discord.WebSocket;
public class SocketOnboardingInfo
{
internal SocketOnboardingInfo(OnboardingAuditLogModel model, DiscordSocketClient discord)
{
Prompts = model.Prompts?.Select(x => new RestGuildOnboardingPrompt(discord, x.Id, x)).ToImmutableArray();
DefaultChannelIds = model.DefaultChannelIds;
IsEnabled = model.Enabled;
}
/// <inheritdoc cref="IGuildOnboarding.Prompts"/>
/// <remarks>
/// <see langword="null"/> if this property is not mentioned in this entry.
/// </remarks>
IReadOnlyCollection<IGuildOnboardingPrompt> Prompts { get; }
/// <inheritdoc cref="IGuildOnboarding.DefaultChannelIds"/>
/// <remarks>
/// <see langword="null"/> if this property is not mentioned in this entry.
/// </remarks>
IReadOnlyCollection<ulong> DefaultChannelIds { get; }
/// <inheritdoc cref="IGuildOnboarding.IsEnabled"/>
/// <remarks>
/// <see langword="null"/> if this property is not mentioned in this entry.
/// </remarks>
bool? IsEnabled { get; }
}

View File

@@ -0,0 +1,30 @@
using Discord.API.AuditLogs;
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to an onboarding prompt creation.
/// </summary>
public class SocketOnboardingPromptCreatedAuditLogData : ISocketAuditLogData
{
internal SocketOnboardingPromptCreatedAuditLogData(SocketOnboardingPromptInfo data)
{
Data = data;
}
internal static SocketOnboardingPromptCreatedAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (_, data) = AuditLogHelper.CreateAuditLogEntityInfo<OnboardingPromptAuditLogModel>(changes, discord);
return new SocketOnboardingPromptCreatedAuditLogData(new(data, discord));
}
/// <summary>
/// Gets the onboarding prompt information after the changes.
/// </summary>
SocketOnboardingPromptInfo Data { get; set; }
}

View File

@@ -0,0 +1,56 @@
using Discord.API.AuditLogs;
using Discord.Rest;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
namespace Discord.WebSocket;
public class SocketOnboardingPromptInfo
{
internal SocketOnboardingPromptInfo(OnboardingPromptAuditLogModel model, DiscordSocketClient discord)
{
Title = model.Title;
IsSingleSelect = model.IsSingleSelect;
IsRequired = model.IsRequired;
IsInOnboarding = model.IsInOnboarding;
Type = model.Type;
Options = model.Options?.Select(x => new RestGuildOnboardingPromptOption(discord, x.Id, x)).ToImmutableArray();
}
/// <inheritdoc cref="IGuildOnboardingPrompt.Title"/>
/// <remarks>
/// <see langword="null"/> if this property is not mentioned in this entry.
/// </remarks>
string Title { get; }
/// <inheritdoc cref="IGuildOnboardingPrompt.IsSingleSelect"/>
/// <remarks>
/// <see langword="null"/> if this property is not mentioned in this entry.
/// </remarks>
bool? IsSingleSelect { get; }
/// <inheritdoc cref="IGuildOnboardingPrompt.IsRequired"/>
/// <remarks>
/// <see langword="null"/> if this property is not mentioned in this entry.
/// </remarks>
bool? IsRequired { get; }
/// <inheritdoc cref="IGuildOnboardingPrompt.IsInOnboarding"/>
/// <remarks>
/// <see langword="null"/> if this property is not mentioned in this entry.
/// </remarks>
bool? IsInOnboarding { get; }
/// <inheritdoc cref="IGuildOnboardingPrompt.Type"/>
/// <remarks>
/// <see langword="null"/> if this property is not mentioned in this entry.
/// </remarks>
GuildOnboardingPromptType? Type { get; }
/// <inheritdoc cref="IGuildOnboardingPrompt.Options"/>
/// <remarks>
/// <see langword="null"/> if this property is not mentioned in this entry.
/// </remarks>
IReadOnlyCollection<IGuildOnboardingPromptOption> Options { get; }
}

View File

@@ -0,0 +1,38 @@
using Discord.Rest;
using Discord.API.AuditLogs;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to an onboarding prompt update.
/// </summary>
public class SocketOnboardingPromptUpdatedAuditLogData : ISocketAuditLogData
{
internal SocketOnboardingPromptUpdatedAuditLogData(SocketOnboardingPromptInfo before, SocketOnboardingPromptInfo after)
{
Before = before;
After = after;
}
internal static SocketOnboardingPromptUpdatedAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (before, after) = AuditLogHelper.CreateAuditLogEntityInfo<OnboardingPromptAuditLogModel>(changes, discord);
return new SocketOnboardingPromptUpdatedAuditLogData(new(before, discord), new(after, discord));
}
/// <summary>
/// Gets the onboarding prompt information after the changes.
/// </summary>
SocketOnboardingPromptInfo After { get; set; }
/// <summary>
/// Gets the onboarding prompt information before the changes.
/// </summary>
SocketOnboardingPromptInfo Before { get; set; }
}

View File

@@ -0,0 +1,37 @@
namespace Discord.WebSocket;
using Discord.Rest;
using Discord.API.AuditLogs;
using EntryModel = Discord.API.AuditLogEntry;
/// <summary>
/// Contains a piece of audit log data related to a guild update.
/// </summary>
public class SocketOnboardingUpdatedAuditLogData : ISocketAuditLogData
{
internal SocketOnboardingUpdatedAuditLogData(SocketOnboardingInfo before, SocketOnboardingInfo after)
{
Before = before;
After = after;
}
internal static SocketOnboardingUpdatedAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (before, after) = AuditLogHelper.CreateAuditLogEntityInfo<OnboardingAuditLogModel>(changes, discord);
return new SocketOnboardingUpdatedAuditLogData(new(before, discord), new(after, discord));
}
/// <summary>
/// Gets the onboarding information after the changes.
/// </summary>
SocketOnboardingInfo After { get; set; }
/// <summary>
/// Gets the onboarding information before the changes.
/// </summary>
SocketOnboardingInfo Before { get; set; }
}

View File

@@ -0,0 +1,52 @@
using System.Linq;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data for a permissions overwrite creation.
/// </summary>
public class SocketOverwriteCreateAuditLogData : ISocketAuditLogData
{
private SocketOverwriteCreateAuditLogData(ulong channelId, Overwrite overwrite)
{
ChannelId = channelId;
Overwrite = overwrite;
}
internal static SocketOverwriteCreateAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var denyModel = changes.FirstOrDefault(x => x.ChangedProperty == "deny");
var allowModel = changes.FirstOrDefault(x => x.ChangedProperty == "allow");
var deny = denyModel.NewValue.ToObject<ulong>(discord.ApiClient.Serializer);
var allow = allowModel.NewValue.ToObject<ulong>(discord.ApiClient.Serializer);
var permissions = new OverwritePermissions(allow, deny);
var id = entry.Options.OverwriteTargetId.Value;
var type = entry.Options.OverwriteType;
return new SocketOverwriteCreateAuditLogData(entry.TargetId.Value, new Overwrite(id, type, permissions));
}
/// <summary>
/// Gets the ID of the channel that the overwrite was created from.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier for the channel that the overwrite was
/// created from.
/// </returns>
public ulong ChannelId { get; }
/// <summary>
/// Gets the permission overwrite object that was created.
/// </summary>
/// <returns>
/// An <see cref="Overwrite"/> object representing the overwrite that was created.
/// </returns>
public Overwrite Overwrite { get; }
}

View File

@@ -0,0 +1,51 @@
using System.Linq;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to the deletion of a permission overwrite.
/// </summary>
public class SocketOverwriteDeleteAuditLogData : ISocketAuditLogData
{
private SocketOverwriteDeleteAuditLogData(ulong channelId, Overwrite deletedOverwrite)
{
ChannelId = channelId;
Overwrite = deletedOverwrite;
}
internal static SocketOverwriteDeleteAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var denyModel = changes.FirstOrDefault(x => x.ChangedProperty == "deny");
var allowModel = changes.FirstOrDefault(x => x.ChangedProperty == "allow");
var deny = denyModel.OldValue.ToObject<ulong>(discord.ApiClient.Serializer);
var allow = allowModel.OldValue.ToObject<ulong>(discord.ApiClient.Serializer);
var permissions = new OverwritePermissions(allow, deny);
var id = entry.Options.OverwriteTargetId.Value;
var type = entry.Options.OverwriteType;
return new SocketOverwriteDeleteAuditLogData(entry.TargetId!.Value, new Overwrite(id, type, permissions));
}
/// <summary>
/// Gets the ID of the channel that the overwrite was deleted from.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier for the channel that the overwrite was
/// deleted from.
/// </returns>
public ulong ChannelId { get; }
/// <summary>
/// Gets the permission overwrite object that was deleted.
/// </summary>
/// <returns>
/// An <see cref="Overwrite"/> object representing the overwrite that was deleted.
/// </returns>
public Overwrite Overwrite { get; }
}

View File

@@ -0,0 +1,83 @@
using System.Linq;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to the update of a permission overwrite.
/// </summary>
public class SocketOverwriteUpdateAuditLogData : ISocketAuditLogData
{
private SocketOverwriteUpdateAuditLogData(ulong channelId, OverwritePermissions before, OverwritePermissions after, ulong targetId, PermissionTarget targetType)
{
ChannelId = channelId;
OldPermissions = before;
NewPermissions = after;
OverwriteTargetId = targetId;
OverwriteType = targetType;
}
internal static SocketOverwriteUpdateAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var denyModel = changes.FirstOrDefault(x => x.ChangedProperty == "deny");
var allowModel = changes.FirstOrDefault(x => x.ChangedProperty == "allow");
var beforeAllow = allowModel?.OldValue?.ToObject<ulong>(discord.ApiClient.Serializer);
var afterAllow = allowModel?.NewValue?.ToObject<ulong>(discord.ApiClient.Serializer);
var beforeDeny = denyModel?.OldValue?.ToObject<ulong>(discord.ApiClient.Serializer);
var afterDeny = denyModel?.NewValue?.ToObject<ulong>(discord.ApiClient.Serializer);
var beforePermissions = new OverwritePermissions(beforeAllow ?? 0, beforeDeny ?? 0);
var afterPermissions = new OverwritePermissions(afterAllow ?? 0, afterDeny ?? 0);
var type = entry.Options.OverwriteType;
return new SocketOverwriteUpdateAuditLogData(entry.TargetId.Value, beforePermissions, afterPermissions, entry.Options.OverwriteTargetId.Value, type);
}
/// <summary>
/// Gets the ID of the channel that the overwrite was updated from.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier for the channel that the overwrite was
/// updated from.
/// </returns>
public ulong ChannelId { get; }
/// <summary>
/// Gets the overwrite permissions before the changes.
/// </summary>
/// <returns>
/// An overwrite permissions object representing the overwrite permissions that the overwrite had before
/// the changes were made.
/// </returns>
public OverwritePermissions OldPermissions { get; }
/// <summary>
/// Gets the overwrite permissions after the changes.
/// </summary>
/// <returns>
/// An overwrite permissions object representing the overwrite permissions that the overwrite had after the
/// changes.
/// </returns>
public OverwritePermissions NewPermissions { get; }
/// <summary>
/// Gets the ID of the overwrite that was updated.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier of the overwrite that was updated.
/// </returns>
public ulong OverwriteTargetId { get; }
/// <summary>
/// Gets the target of the updated permission overwrite.
/// </summary>
/// <returns>
/// The target of the updated permission overwrite.
/// </returns>
public PermissionTarget OverwriteType { get; }
}

View File

@@ -0,0 +1,39 @@
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a guild prune.
/// </summary>
public class SocketPruneAuditLogData : ISocketAuditLogData
{
private SocketPruneAuditLogData(int pruneDays, int membersRemoved)
{
PruneDays = pruneDays;
MembersRemoved = membersRemoved;
}
internal static SocketPruneAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
return new SocketPruneAuditLogData(entry.Options.PruneDeleteMemberDays!.Value, entry.Options.PruneMembersRemoved!.Value);
}
/// <summary>
/// Gets the threshold for a guild member to not be kicked.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the amount of days that a member must have been seen in the server,
/// to avoid being kicked. (i.e. If a user has not been seen for more than <paramref cref="PruneDays"/>, they will be
/// kicked from the server)
/// </returns>
public int PruneDays { get; }
/// <summary>
/// Gets the number of members that were kicked during the purge.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the number of members that were removed from this guild for having
/// not been seen within <paramref cref="PruneDays"/>.
/// </returns>
public int MembersRemoved { get; }
}

View File

@@ -0,0 +1,44 @@
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
using Model = Discord.API.AuditLogs.RoleInfoAuditLogModel;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a role creation.
/// </summary>
public class SocketRoleCreateAuditLogData : ISocketAuditLogData
{
private SocketRoleCreateAuditLogData(ulong id, SocketRoleEditInfo props)
{
RoleId = id;
Properties = props;
}
internal static SocketRoleCreateAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (_, data) = AuditLogHelper.CreateAuditLogEntityInfo<Model>(changes, discord);
return new SocketRoleCreateAuditLogData(entry.TargetId!.Value,
new SocketRoleEditInfo(data));
}
/// <summary>
/// Gets the ID of the role that was created.
/// </summary>
/// <return>
/// A <see cref="ulong"/> representing the snowflake identifier to the role that was created.
/// </return>
public ulong RoleId { get; }
/// <summary>
/// Gets the role information that was created.
/// </summary>
/// <return>
/// An information object representing the properties of the role that was created.
/// </return>
public SocketRoleEditInfo Properties { get; }
}

View File

@@ -0,0 +1,43 @@
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
using Model = Discord.API.AuditLogs.RoleInfoAuditLogModel;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data relating to a role deletion.
/// </summary>
public class SocketRoleDeleteAuditLogData : ISocketAuditLogData
{
private SocketRoleDeleteAuditLogData(ulong id, SocketRoleEditInfo props)
{
RoleId = id;
Properties = props;
}
internal static SocketRoleDeleteAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (data, _) = AuditLogHelper.CreateAuditLogEntityInfo<Model>(changes, discord);
return new SocketRoleDeleteAuditLogData(entry.TargetId!.Value,
new SocketRoleEditInfo(data));
}
/// <summary>
/// Gets the ID of the role that was deleted.
/// </summary>
/// <return>
/// A <see cref="ulong"/> representing the snowflake identifier to the role that was deleted.
/// </return>
public ulong RoleId { get; }
/// <summary>
/// Gets the role information that was deleted.
/// </summary>
/// <return>
/// An information object representing the properties of the role that was deleted.
/// </return>
public SocketRoleEditInfo Properties { get; }
}

View File

@@ -0,0 +1,79 @@
using Model = Discord.API.AuditLogs.RoleInfoAuditLogModel;
namespace Discord.WebSocket;
/// <summary>
/// Represents information for a role edit.
/// </summary>
public struct SocketRoleEditInfo
{
internal SocketRoleEditInfo(Model model)
{
if (model.Color is not null)
Color = new Color(model.Color.Value);
else
Color = null;
Mentionable = model.IsMentionable;
Hoist = model.Hoist;
Name = model.Name;
if(model.Permissions is not null)
Permissions = new GuildPermissions(model.Permissions.Value);
else
Permissions = null;
IconId = model.IconHash;
}
/// <summary>
/// Gets the color of this role.
/// </summary>
/// <returns>
/// A color object representing the color assigned to this role; <c>null</c> if this role does not have a
/// color.
/// </returns>
public Color? Color { get; }
/// <summary>
/// Gets a value that indicates whether this role is mentionable.
/// </summary>
/// <returns>
/// <c>true</c> if other members can mention this role in a text channel; otherwise <c>false</c>;
/// <c>null</c> if this is not mentioned in this entry.
/// </returns>
public bool? Mentionable { get; }
/// <summary>
/// Gets a value that indicates whether this role is hoisted (i.e. its members will appear in a separate
/// section on the user list).
/// </summary>
/// <returns>
/// <c>true</c> if this role's members will appear in a separate section in the user list; otherwise
/// <c>false</c>; <c>null</c> if this is not mentioned in this entry.
/// </returns>
public bool? Hoist { get; }
/// <summary>
/// Gets the name of this role.
/// </summary>
/// <returns>
/// A string containing the name of this role.
/// </returns>
public string Name { get; }
/// <summary>
/// Gets the permissions assigned to this role.
/// </summary>
/// <returns>
/// A guild permissions object representing the permissions that have been assigned to this role; <c>null</c>
/// if no permissions have been assigned.
/// </returns>
public GuildPermissions? Permissions { get; }
/// <inheritdoc cref="IRole.Icon"/>
/// <remarks>
/// <see langword="null" /> if the value was not updated in this entry.
/// </remarks>
public string IconId { get; }
}

View File

@@ -0,0 +1,51 @@
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
using Model = Discord.API.AuditLogs.RoleInfoAuditLogModel;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a role update.
/// </summary>
public class SocketRoleUpdateAuditLogData : ISocketAuditLogData
{
private SocketRoleUpdateAuditLogData(ulong id, SocketRoleEditInfo oldProps, SocketRoleEditInfo newProps)
{
RoleId = id;
Before = oldProps;
After = newProps;
}
internal static SocketRoleUpdateAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (before, after) = AuditLogHelper.CreateAuditLogEntityInfo<Model>(changes, discord);
return new SocketRoleUpdateAuditLogData(entry.TargetId!.Value, new(before), new(after));
}
/// <summary>
/// Gets the ID of the role that was changed.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier of the role that was changed.
/// </returns>
public ulong RoleId { get; }
/// <summary>
/// Gets the role information before the changes.
/// </summary>
/// <returns>
/// A role information object containing the role information before the changes were made.
/// </returns>
public SocketRoleEditInfo Before { get; }
/// <summary>
/// Gets the role information after the changes.
/// </summary>
/// <returns>
/// A role information object containing the role information after the changes were made.
/// </returns>
public SocketRoleEditInfo After { get; }
}

View File

@@ -0,0 +1,88 @@
using Discord.API.AuditLogs;
using Discord.Rest;
using System;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a scheduled event creation.
/// </summary>
public class SocketScheduledEventCreateAuditLogData : ISocketAuditLogData
{
private SocketScheduledEventCreateAuditLogData(ulong id, ScheduledEventInfoAuditLogModel model)
{
Id = id;
ChannelId = model.ChannelId;
Name = model.Name;
Description = model.Description;
ScheduledStartTime = model.StartTime;
ScheduledEndTime = model.EndTime;
PrivacyLevel = model.PrivacyLevel!.Value;
Status = model.EventStatus!.Value;
EntityType = model.EventType!.Value;
EntityId = model.EntityId;
Location = model.Location;
Image = model.Image;
}
internal static SocketScheduledEventCreateAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (_, data) = AuditLogHelper.CreateAuditLogEntityInfo<ScheduledEventInfoAuditLogModel>(changes, discord);
return new SocketScheduledEventCreateAuditLogData(entry.TargetId!.Value, data);
}
// Doc Note: Corresponds to the *current* data
/// <summary>
/// Gets the snowflake id of the event.
/// </summary>
public ulong Id { get; }
/// <summary>
/// Gets the snowflake id of the channel the event is associated with.
/// </summary>
public ulong? ChannelId { get; }
/// <summary>
/// Gets name of the event.
/// </summary>
public string Name { get; }
/// <summary>
/// Gets the description of the event. null if none is set.
/// </summary>
public string Description { get; }
/// <summary>
/// Gets the time the event was scheduled for.
/// </summary>
public DateTimeOffset? ScheduledStartTime { get; }
/// <summary>
/// Gets the time the event was scheduled to end.
/// </summary>
public DateTimeOffset? ScheduledEndTime { get; }
/// <summary>
/// Gets the privacy level of the event.
/// </summary>
public GuildScheduledEventPrivacyLevel PrivacyLevel { get; }
/// <summary>
/// Gets the status of the event.
/// </summary>
public GuildScheduledEventStatus Status { get; }
/// <summary>
/// Gets the type of the entity associated with the event (stage / void / external).
/// </summary>
public GuildScheduledEventType EntityType { get; }
/// <summary>
/// Gets the snowflake id of the entity associated with the event (stage / void / external).
/// </summary>
public ulong? EntityId { get; }
/// <summary>
/// Gets the metadata for the entity associated with the event.
/// </summary>
public string Location { get; }
/// <summary>
/// Gets the image hash of the image that was attached to the event. Null if not set.
/// </summary>
public string Image { get; }
}

View File

@@ -0,0 +1,97 @@
using Discord.API.AuditLogs;
using Discord.Rest;
using System;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a scheduled event deletion.
/// </summary>
public class SocketScheduledEventDeleteAuditLogData : ISocketAuditLogData
{
private SocketScheduledEventDeleteAuditLogData(ulong id, ScheduledEventInfoAuditLogModel model)
{
Id = id;
ChannelId = model.ChannelId;
Name = model.Name;
Description = model.Description;
ScheduledStartTime = model.StartTime;
ScheduledEndTime = model.EndTime;
PrivacyLevel = model.PrivacyLevel;
Status = model.EventStatus;
EntityType = model.EventType;
EntityId = model.EntityId;
Location = model.Location;
Image = model.Image;
}
internal static SocketScheduledEventDeleteAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (data, _) = AuditLogHelper.CreateAuditLogEntityInfo<ScheduledEventInfoAuditLogModel>(changes, discord);
return new SocketScheduledEventDeleteAuditLogData(entry.TargetId!.Value, data);
}
/// <summary>
/// Gets the snowflake id of the event.
/// </summary>
public ulong Id { get; }
/// <summary>
/// Gets the snowflake id of the channel the event is associated with.
/// </summary>
public ulong? ChannelId { get; }
/// <summary>
/// Gets name of the event.
/// </summary>
public string Name { get; }
/// <summary>
/// Gets the description of the event. null if none is set.
/// </summary>
public string Description { get; }
/// <summary>
/// Gets the time the event was scheduled for.
/// </summary>
public DateTimeOffset? ScheduledStartTime { get; }
/// <summary>
/// Gets the time the event was scheduled to end.
/// </summary>
public DateTimeOffset? ScheduledEndTime { get; }
/// <summary>
/// Gets the privacy level of the event.
/// </summary>
public GuildScheduledEventPrivacyLevel? PrivacyLevel { get; }
/// <summary>
/// Gets the status of the event.
/// </summary>
public GuildScheduledEventStatus? Status { get; }
/// <summary>
/// Gets the type of the entity associated with the event (stage / void / external).
/// </summary>
public GuildScheduledEventType? EntityType { get; }
/// <summary>
/// Gets the snowflake id of the entity associated with the event (stage / void / external).
/// </summary>
public ulong? EntityId { get; }
/// <summary>
/// Gets the metadata for the entity associated with the event.
/// </summary>
public string Location { get; }
/// <summary>
/// Gets the image hash of the image that was attached to the event. Null if not set.
/// </summary>
public string Image { get; }
}

View File

@@ -0,0 +1,80 @@
using Discord.API.AuditLogs;
using System;
namespace Discord.WebSocket;
/// <summary>
/// Represents information for a scheduled event.
/// </summary>
public class SocketScheduledEventInfo
{
/// <summary>
/// Gets the snowflake id of the channel the event is associated with.
/// </summary>
public ulong? ChannelId { get; }
/// <summary>
/// Gets name of the event.
/// </summary>
public string Name { get; }
/// <summary>
/// Gets the description of the event. null if none is set.
/// </summary>
public string Description { get; }
/// <summary>
/// Gets the time the event was scheduled for.
/// </summary>
public DateTimeOffset? ScheduledStartTime { get; }
/// <summary>
/// Gets the time the event was scheduled to end.
/// </summary>
public DateTimeOffset? ScheduledEndTime { get; }
/// <summary>
/// Gets the privacy level of the event.
/// </summary>
public GuildScheduledEventPrivacyLevel? PrivacyLevel { get; }
/// <summary>
/// Gets the status of the event.
/// </summary>
public GuildScheduledEventStatus? Status { get; }
/// <summary>
/// Gets the type of the entity associated with the event (stage / void / external).
/// </summary>
public GuildScheduledEventType? EntityType { get; }
/// <summary>
/// Gets the snowflake id of the entity associated with the event (stage / void / external).
/// </summary>
public ulong? EntityId { get; }
/// <summary>
/// Gets the metadata for the entity associated with the event.
/// </summary>
public string Location { get; }
/// <summary>
/// Gets the image hash of the image that was attached to the event. Null if not set.
/// </summary>
public string Image { get; }
internal SocketScheduledEventInfo(ScheduledEventInfoAuditLogModel model)
{
ChannelId = model.ChannelId;
Name = model.Name;
Description = model.Description;
ScheduledStartTime = model.StartTime;
ScheduledEndTime = model.EndTime;
PrivacyLevel = model.PrivacyLevel;
Status = model.EventStatus;
EntityType = model.EventType;
EntityId = model.EntityId;
Location = model.Location;
Image = model.Image;
}
}

View File

@@ -0,0 +1,44 @@
using Discord.API.AuditLogs;
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a scheduled event updates.
/// </summary>
public class SocketScheduledEventUpdateAuditLogData : ISocketAuditLogData
{
private SocketScheduledEventUpdateAuditLogData(ulong id, SocketScheduledEventInfo before, SocketScheduledEventInfo after)
{
Id = id;
Before = before;
After = after;
}
internal static SocketScheduledEventUpdateAuditLogData Create(BaseDiscordClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (before, after) = AuditLogHelper.CreateAuditLogEntityInfo<ScheduledEventInfoAuditLogModel>(changes, discord);
return new SocketScheduledEventUpdateAuditLogData(entry.TargetId!.Value, new(before), new(after));
}
// Doc Note: Corresponds to the *current* data
/// <summary>
/// Gets the snowflake id of the event.
/// </summary>
public ulong Id { get; }
/// <summary>
/// Gets the state before the change.
/// </summary>
public SocketScheduledEventInfo Before { get; }
/// <summary>
/// Gets the state after the change.
/// </summary>
public SocketScheduledEventInfo After { get; }
}

View File

@@ -0,0 +1,23 @@
namespace Discord.WebSocket;
/// <summary>
/// Represents information for a stage.
/// </summary>
public class SocketStageInfo
{
/// <summary>
/// Gets the topic of the stage channel.
/// </summary>
public string Topic { get; }
/// <summary>
/// Gets the privacy level of the stage channel.
/// </summary>
public StagePrivacyLevel? PrivacyLevel { get; }
internal SocketStageInfo(StagePrivacyLevel? level, string topic)
{
Topic = topic;
PrivacyLevel = level;
}
}

View File

@@ -0,0 +1,41 @@
using System.Linq;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a stage going live.
/// </summary>
public class SocketStageInstanceCreateAuditLogData : ISocketAuditLogData
{
/// <summary>
/// Gets the topic of the stage channel.
/// </summary>
public string Topic { get; }
/// <summary>
/// Gets the privacy level of the stage channel.
/// </summary>
public StagePrivacyLevel PrivacyLevel { get; }
/// <summary>
/// Gets the Id of the stage channel.
/// </summary>
public ulong StageChannelId { get; }
internal SocketStageInstanceCreateAuditLogData(string topic, StagePrivacyLevel privacyLevel, ulong channelId)
{
Topic = topic;
PrivacyLevel = privacyLevel;
StageChannelId = channelId;
}
internal static SocketStageInstanceCreateAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var topic = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "topic").NewValue.ToObject<string>(discord.ApiClient.Serializer);
var privacyLevel = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "privacy_level").NewValue.ToObject<StagePrivacyLevel>(discord.ApiClient.Serializer);
var channelId = entry.Options.ChannelId;
return new SocketStageInstanceCreateAuditLogData(topic, privacyLevel, channelId ?? 0);
}
}

View File

@@ -0,0 +1,41 @@
using System.Linq;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a stage instance deleted.
/// </summary>
public class SocketStageInstanceDeleteAuditLogData : ISocketAuditLogData
{
/// <summary>
/// Gets the topic of the stage channel.
/// </summary>
public string Topic { get; }
/// <summary>
/// Gets the privacy level of the stage channel.
/// </summary>
public StagePrivacyLevel PrivacyLevel { get; }
/// <summary>
/// Gets the Id of the stage channel.
/// </summary>
public ulong StageChannelId { get; }
internal SocketStageInstanceDeleteAuditLogData(string topic, StagePrivacyLevel privacyLevel, ulong channelId)
{
Topic = topic;
PrivacyLevel = privacyLevel;
StageChannelId = channelId;
}
internal static SocketStageInstanceDeleteAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var topic = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "topic").OldValue.ToObject<string>(discord.ApiClient.Serializer);
var privacyLevel = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "privacy_level").OldValue.ToObject<StagePrivacyLevel>(discord.ApiClient.Serializer);
var channelId = entry.Options.ChannelId;
return new SocketStageInstanceDeleteAuditLogData(topic, privacyLevel, channelId ?? 0);
}
}

View File

@@ -0,0 +1,47 @@
using System.Linq;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a stage instance update.
/// </summary>
public class SocketStageInstanceUpdatedAuditLogData : ISocketAuditLogData
{
/// <summary>
/// Gets the Id of the stage channel.
/// </summary>
public ulong StageChannelId { get; }
/// <summary>
/// Gets the stage information before the changes.
/// </summary>
public SocketStageInfo Before { get; }
/// <summary>
/// Gets the stage information after the changes.
/// </summary>
public SocketStageInfo After { get; }
internal SocketStageInstanceUpdatedAuditLogData(ulong channelId, SocketStageInfo before, SocketStageInfo after)
{
StageChannelId = channelId;
Before = before;
After = after;
}
internal static SocketStageInstanceUpdatedAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var channelId = entry.Options.ChannelId.Value;
var topic = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "topic");
var privacy = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "privacy");
var oldTopic = topic?.OldValue.ToObject<string>();
var newTopic = topic?.NewValue.ToObject<string>();
var oldPrivacy = privacy?.OldValue.ToObject<StagePrivacyLevel>();
var newPrivacy = privacy?.NewValue.ToObject<StagePrivacyLevel>();
return new SocketStageInstanceUpdatedAuditLogData(channelId, new SocketStageInfo(oldPrivacy, oldTopic), new SocketStageInfo(newPrivacy, newTopic));
}
}

View File

@@ -0,0 +1,29 @@
using Discord.API.AuditLogs;
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a sticker creation.
/// </summary>
public class SocketStickerCreatedAuditLogData : ISocketAuditLogData
{
internal SocketStickerCreatedAuditLogData(SocketStickerInfo data)
{
Data = data;
}
internal static SocketStickerCreatedAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (_, data) = AuditLogHelper.CreateAuditLogEntityInfo<StickerInfoAuditLogModel>(changes, discord);
return new SocketStickerCreatedAuditLogData(new(data));
}
/// <summary>
/// Gets the sticker information after the changes.
/// </summary>
public SocketStickerInfo Data { get; }
}

View File

@@ -0,0 +1,29 @@
using Discord.API.AuditLogs;
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a sticker removal.
/// </summary>
public class SocketStickerDeletedAuditLogData : ISocketAuditLogData
{
internal SocketStickerDeletedAuditLogData(SocketStickerInfo data)
{
Data = data;
}
internal static SocketStickerDeletedAuditLogData Create(BaseDiscordClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (data, _) = AuditLogHelper.CreateAuditLogEntityInfo<StickerInfoAuditLogModel>(changes, discord);
return new SocketStickerDeletedAuditLogData(new(data));
}
/// <summary>
/// Gets the sticker information before the changes.
/// </summary>
public SocketStickerInfo Data { get; }
}

View File

@@ -0,0 +1,31 @@
using Discord.API.AuditLogs;
namespace Discord.WebSocket;
/// <summary>
/// Represents information for a guild.
/// </summary>
public class SocketStickerInfo
{
internal SocketStickerInfo(StickerInfoAuditLogModel model)
{
Name = model.Name;
Tags = model.Tags;
Description = model.Description;
}
/// <summary>
/// Gets the name of the sticker. <see langword="null" /> if the value was not updated in this entry.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Gets tags of the sticker. <see langword="null" /> if the value was not updated in this entry.
/// </summary>
public string Tags { get; set; }
/// <summary>
/// Gets the description of the sticker. <see langword="null" /> if the value was not updated in this entry.
/// </summary>
public string Description { get; set; }
}

View File

@@ -0,0 +1,35 @@
using Discord.API.AuditLogs;
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a sticker update.
/// </summary>
public class SocketStickerUpdatedAuditLogData : ISocketAuditLogData
{
internal SocketStickerUpdatedAuditLogData(SocketStickerInfo before, SocketStickerInfo after)
{
Before = before;
After = after;
}
internal static SocketStickerUpdatedAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (before, after) = AuditLogHelper.CreateAuditLogEntityInfo<StickerInfoAuditLogModel>(changes, discord);
return new SocketStickerUpdatedAuditLogData(new(before), new (after));
}
/// <summary>
/// Gets the sticker information before the changes.
/// </summary>
public SocketStickerInfo Before { get; }
/// <summary>
/// Gets the sticker information after the changes.
/// </summary>
public SocketStickerInfo After { get; }
}

View File

@@ -0,0 +1,109 @@
using Discord.API.AuditLogs;
using Discord.Rest;
using System.Collections.Generic;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a thread creation.
/// </summary>
public class SocketThreadCreateAuditLogData : ISocketAuditLogData
{
private SocketThreadCreateAuditLogData(ulong id, ThreadInfoAuditLogModel model)
{
ThreadId = id;
ThreadName = model.Name;
IsArchived = model.IsArchived!.Value;
AutoArchiveDuration = model.ArchiveDuration!.Value;
IsLocked = model.IsLocked!.Value;
SlowModeInterval = model.SlowModeInterval;
AppliedTags = model.AppliedTags;
Flags = model.ChannelFlags;
ThreadType = model.Type;
}
internal static SocketThreadCreateAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (_, data) = AuditLogHelper.CreateAuditLogEntityInfo<ThreadInfoAuditLogModel>(changes, discord);
return new SocketThreadCreateAuditLogData(entry.TargetId!.Value, data);
}
/// <summary>
/// Gets the snowflake ID of the thread.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier for the thread.
/// </returns>
public ulong ThreadId { get; }
/// <summary>
/// Gets the name of the thread.
/// </summary>
/// <returns>
/// A string containing the name of the thread.
/// </returns>
public string ThreadName { get; }
/// <summary>
/// Gets the type of the thread.
/// </summary>
/// <returns>
/// The type of thread.
/// </returns>
public ThreadType ThreadType { get; }
/// <summary>
/// Gets the value that indicates whether the thread is archived.
/// </summary>
/// <returns>
/// <c>true</c> if this thread has the Archived flag enabled; otherwise <c>false</c>.
/// </returns>
public bool IsArchived { get; }
/// <summary>
/// Gets the auto archive duration of the thread.
/// </summary>
/// <returns>
/// The thread auto archive duration of the thread.
/// </returns>
public ThreadArchiveDuration AutoArchiveDuration { get; }
/// <summary>
/// Gets the value that indicates whether the thread is locked.
/// </summary>
/// <returns>
/// <c>true</c> if this thread has the Locked flag enabled; otherwise <c>false</c>.
/// </returns>
public bool IsLocked { get; }
/// <summary>
/// Gets the slow-mode delay of the thread.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the time in seconds required before the user can send another
/// message; <c>0</c> if disabled.
/// <c>null</c> if this is not mentioned in this entry.
/// </returns>
public int? SlowModeInterval { get; }
/// <summary>
/// Gets the applied tags of this thread.
/// </summary>
/// <remarks>
/// <see langword="null"/> if the property was not updated.
/// </remarks>
public IReadOnlyCollection<ulong> AppliedTags { get; }
/// <summary>
/// Gets the flags of the thread channel.
/// </summary>
/// <remarks>
/// <see langword="null"/> if the property was not updated.
/// </remarks>
public ChannelFlags? Flags { get; }
}

View File

@@ -0,0 +1,111 @@
using Discord.API.AuditLogs;
using Discord.Rest;
using System.Collections.Generic;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a thread deletion.
/// </summary>
public class SocketThreadDeleteAuditLogData : ISocketAuditLogData
{
private SocketThreadDeleteAuditLogData(ulong id, ThreadInfoAuditLogModel model)
{
ThreadId = id;
ThreadName = model.Name;
IsArchived = model.IsArchived!.Value;
AutoArchiveDuration = model.ArchiveDuration!.Value;
IsLocked = model.IsLocked!.Value;
SlowModeInterval = model.SlowModeInterval;
AppliedTags = model.AppliedTags;
Flags = model.ChannelFlags;
ThreadType = model.Type;
}
internal static SocketThreadDeleteAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (data, _) = AuditLogHelper.CreateAuditLogEntityInfo<ThreadInfoAuditLogModel>(changes, discord);
return new SocketThreadDeleteAuditLogData(entry.TargetId!.Value, data);
}
/// <summary>
/// Gets the snowflake ID of the deleted thread.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier for the deleted thread.
///
/// </returns>
public ulong ThreadId { get; }
/// <summary>
/// Gets the name of the deleted thread.
/// </summary>
/// <returns>
/// A string containing the name of the deleted thread.
///
/// </returns>
public string ThreadName { get; }
/// <summary>
/// Gets the type of the deleted thread.
/// </summary>
/// <returns>
/// The type of thread that was deleted.
/// </returns>
public ThreadType ThreadType { get; }
/// <summary>
/// Gets the value that indicates whether the deleted thread was archived.
/// </summary>
/// <returns>
/// <c>true</c> if this thread had the Archived flag enabled; otherwise <c>false</c>.
/// </returns>
public bool IsArchived { get; }
/// <summary>
/// Gets the thread auto archive duration of the deleted thread.
/// </summary>
/// <returns>
/// The thread auto archive duration of the thread that was deleted.
/// </returns>
public ThreadArchiveDuration AutoArchiveDuration { get; }
/// <summary>
/// Gets the value that indicates whether the deleted thread was locked.
/// </summary>
/// <returns>
/// <c>true</c> if this thread had the Locked flag enabled; otherwise <c>false</c>.
/// </returns>
public bool IsLocked { get; }
/// <summary>
/// Gets the slow-mode delay of the deleted thread.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the time in seconds required before the user can send another
/// message; <c>0</c> if disabled.
/// <c>null</c> if this is not mentioned in this entry.
/// </returns>
public int? SlowModeInterval { get; }
/// <summary>
/// Gets the applied tags of this thread.
/// </summary>
/// <remarks>
/// <see langword="null"/> if this is not mentioned in this entry.
/// </remarks>
public IReadOnlyCollection<ulong> AppliedTags { get; }
/// <summary>
/// Gets the flags of the thread channel.
/// </summary>
/// <remarks>
/// <see langword="null"/> if this is not mentioned in this entry.
/// </remarks>
public ChannelFlags? Flags { get; }
}

View File

@@ -0,0 +1,83 @@
using Discord.API.AuditLogs;
using System.Collections.Generic;
namespace Discord.WebSocket;
/// <summary>
/// Represents information for a thread.
/// </summary>
public class SocketThreadInfo
{
/// <summary>
/// Gets the name of the thread.
/// </summary>
public string Name { get; }
/// <summary>
/// Gets the value that indicates whether the thread is archived.
/// </summary>
/// <remarks>
/// <see langword="null"/> if the property was not updated.
/// </remarks>
public bool? IsArchived { get; }
/// <summary>
/// Gets the auto archive duration of thread.
/// </summary>
/// <remarks>
/// <see langword="null"/> if the property was not updated.
/// </remarks>
public ThreadArchiveDuration? AutoArchiveDuration { get; }
/// <summary>
/// Gets the value that indicates whether the thread is locked.
/// </summary>
/// <remarks>
/// <see langword="null"/> if the property was not updated.
/// </remarks>
public bool? IsLocked { get; }
/// <summary>
/// Gets the slow-mode delay of the thread.
/// </summary>
/// <remarks>
/// <see langword="null"/> if the property was not updated.
/// </remarks>
public int? SlowModeInterval { get; }
/// <summary>
/// Gets the applied tags of this thread.
/// </summary>
/// <remarks>
/// <see langword="null"/> if the property was not updated.
/// </remarks>
public IReadOnlyCollection<ulong> AppliedTags { get; }
/// <summary>
/// Gets the flags of the thread channel.
/// </summary>
/// <remarks>
/// <see langword="null"/> if the property was not updated.
/// </remarks>
public ChannelFlags? Flags { get; }
/// <summary>
/// Gets the type of the thread.
/// </summary>
/// <remarks>
/// <see langword="null"/> if the property was not updated.
/// </remarks>
public ThreadType Type { get; }
internal SocketThreadInfo(ThreadInfoAuditLogModel model)
{
Name = model.Name;
IsArchived = model.IsArchived;
AutoArchiveDuration = model.ArchiveDuration;
IsLocked = model.IsLocked;
SlowModeInterval = model.SlowModeInterval;
AppliedTags = model.AppliedTags;
Flags = model.ChannelFlags;
Type = model.Type;
}
}

View File

@@ -0,0 +1,51 @@
using Discord.API.AuditLogs;
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a thread update.
/// </summary>
public class SocketThreadUpdateAuditLogData : ISocketAuditLogData
{
private SocketThreadUpdateAuditLogData(ThreadType type, ThreadInfo before, ThreadInfo after)
{
ThreadType = type;
Before = before;
After = after;
}
internal static SocketThreadUpdateAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (before, after) = AuditLogHelper.CreateAuditLogEntityInfo<ThreadInfoAuditLogModel>(changes, discord);
return new SocketThreadUpdateAuditLogData(before.Type, new(before), new (after));
}
/// <summary>
/// Gets the type of the thread.
/// </summary>
/// <returns>
/// The type of thread.
/// </returns>
public ThreadType ThreadType { get; }
/// <summary>
/// Gets the thread information before the changes.
/// </summary>
/// <returns>
/// A thread information object representing the thread before the changes were made.
/// </returns>
public ThreadInfo Before { get; }
/// <summary>
/// Gets the thread information after the changes.
/// </summary>
/// <returns>
/// A thread information object representing the thread after the changes were made.
/// </returns>
public ThreadInfo After { get; }
}

View File

@@ -0,0 +1,39 @@
using Discord.Rest;
using System.Linq;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to an unban.
/// </summary>
public class SocketUnbanAuditLogData : ISocketAuditLogData
{
private SocketUnbanAuditLogData(Cacheable<SocketUser, RestUser, IUser, ulong> user)
{
Target = user;
}
internal static SocketUnbanAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var cachedUser = discord.GetUser(entry.TargetId!.Value);
var cacheableUser = new Cacheable<SocketUser, RestUser, IUser, ulong>(
cachedUser,
entry.TargetId.Value,
cachedUser is not null,
async () =>
{
var user = await discord.ApiClient.GetUserAsync(entry.TargetId.Value);
return user is not null ? RestUser.Create(discord, user) : null;
});
return new SocketUnbanAuditLogData(cacheableUser);
}
/// <summary>
/// Gets the user that was unbanned.
/// </summary>
/// <returns>
/// A cacheable user object representing the user that was unbanned.
/// </returns>
public Cacheable<SocketUser, RestUser, IUser, ulong> Target { get; }
}

View File

@@ -0,0 +1,71 @@
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
using Model = Discord.API.AuditLogs.WebhookInfoAuditLogModel;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a webhook creation.
/// </summary>
public class SocketWebhookCreateAuditLogData : ISocketAuditLogData
{
private SocketWebhookCreateAuditLogData(ulong webhookId, Model model)
{
WebhookId = webhookId;
Name = model.Name;
Type = model.Type!.Value;
ChannelId = model.ChannelId!.Value;
Avatar = model.AvatarHash;
}
internal static SocketWebhookCreateAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (_, data) = AuditLogHelper.CreateAuditLogEntityInfo<Model>(changes, discord);
return new SocketWebhookCreateAuditLogData(entry.TargetId!.Value, data);
}
/// <summary>
/// Gets the webhook id.
/// </summary>
/// <returns>
/// The webhook identifier.
/// </returns>
public ulong WebhookId { get; }
/// <summary>
/// Gets the type of webhook that was created.
/// </summary>
/// <returns>
/// The type of webhook that was created.
/// </returns>
public WebhookType Type { get; }
/// <summary>
/// Gets the name of the webhook.
/// </summary>
/// <returns>
/// A string containing the name of the webhook.
/// </returns>
public string Name { get; }
/// <summary>
/// Gets the ID of the channel that the webhook could send to.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier of the channel that the webhook could send
/// to.
/// </returns>
public ulong ChannelId { get; }
/// <summary>
/// Gets the hash value of the webhook's avatar.
/// </summary>
/// <returns>
/// A string containing the hash of the webhook's avatar.
/// </returns>
public string Avatar { get; }
}

View File

@@ -0,0 +1,71 @@
using Discord.Rest;
using Model = Discord.API.AuditLogs.WebhookInfoAuditLogModel;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a webhook deletion.
/// </summary>
public class SocketWebhookDeleteAuditLogData : ISocketAuditLogData
{
private SocketWebhookDeleteAuditLogData(ulong id, Model model)
{
WebhookId = id;
ChannelId = model.ChannelId!.Value;
Name = model.Name;
Type = model.Type!.Value;
Avatar = model.AvatarHash;
}
internal static SocketWebhookDeleteAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (data, _) = AuditLogHelper.CreateAuditLogEntityInfo<Model>(changes, discord);
return new SocketWebhookDeleteAuditLogData(entry.TargetId!.Value,data);
}
/// <summary>
/// Gets the ID of the webhook that was deleted.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier of the webhook that was deleted.
/// </returns>
public ulong WebhookId { get; }
/// <summary>
/// Gets the ID of the channel that the webhook could send to.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier of the channel that the webhook could send
/// to.
/// </returns>
public ulong ChannelId { get; }
/// <summary>
/// Gets the type of the webhook that was deleted.
/// </summary>
/// <returns>
/// The type of webhook that was deleted.
/// </returns>
public WebhookType Type { get; }
/// <summary>
/// Gets the name of the webhook that was deleted.
/// </summary>
/// <returns>
/// A string containing the name of the webhook that was deleted.
/// </returns>
public string Name { get; }
/// <summary>
/// Gets the hash value of the webhook's avatar.
/// </summary>
/// <returns>
/// A string containing the hash of the webhook's avatar.
/// </returns>
public string Avatar { get; }
}

View File

@@ -0,0 +1,39 @@
using Model = Discord.API.AuditLogs.WebhookInfoAuditLogModel;
namespace Discord.WebSocket;
/// <summary>
/// Represents information for a webhook.
/// </summary>
public struct SocketWebhookInfo
{
internal SocketWebhookInfo(Model model)
{
Name = model.Name;
ChannelId = model.ChannelId;
Avatar = model.AvatarHash;
}
/// <summary>
/// Gets the name of this webhook.
/// </summary>
/// <returns>
/// A string containing the name of this webhook.
/// </returns>
public string Name { get; }
/// <summary>
/// Gets the ID of the channel that this webhook sends to.
/// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identifier of the channel that this webhook can send
/// to.
/// </returns>
public ulong? ChannelId { get; }
/// <summary>
/// Gets the hash value of this webhook's avatar.
/// </summary>
/// <returns>
/// A string containing the hash of this webhook's avatar.
/// </returns>
public string Avatar { get; }
}

View File

@@ -0,0 +1,44 @@
using Discord.Rest;
using EntryModel = Discord.API.AuditLogEntry;
using Model = Discord.API.AuditLogs.WebhookInfoAuditLogModel;
namespace Discord.WebSocket;
/// <summary>
/// Contains a piece of audit log data related to a webhook update.
/// </summary>
public class SocketWebhookUpdateAuditLogData : ISocketAuditLogData
{
private SocketWebhookUpdateAuditLogData(SocketWebhookInfo before, SocketWebhookInfo after)
{
Before = before;
After = after;
}
internal static SocketWebhookUpdateAuditLogData Create(DiscordSocketClient discord, EntryModel entry)
{
var changes = entry.Changes;
var (before, after) = AuditLogHelper.CreateAuditLogEntityInfo<Model>(changes, discord);
return new SocketWebhookUpdateAuditLogData(new(before), new(after));
}
/// <summary>
/// Gets the webhook information before the changes.
/// </summary>
/// <returns>
/// A webhook information object representing the webhook before the changes were made.
/// </returns>
public SocketWebhookInfo Before { get; }
/// <summary>
/// Gets the webhook information after the changes.
/// </summary>
/// <returns>
/// A webhook information object representing the webhook after the changes were made.
/// </returns>
public SocketWebhookInfo After { get; }
}

View File

@@ -0,0 +1,9 @@
namespace Discord.WebSocket;
/// <summary>
/// Represents data applied to a <see cref="SocketAuditLogEntry"/>.
/// </summary>
public interface ISocketAuditLogData : IAuditLogData
{
}

View File

@@ -0,0 +1,55 @@
using Discord.Rest;
using System;
using System.Linq;
using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.Gateway.AuditLogCreatedEvent;
namespace Discord.WebSocket;
/// <summary>
/// Represents a Socket-based audit log entry.
/// </summary>
public class SocketAuditLogEntry : SocketEntity<ulong>, IAuditLogEntry
{
private SocketAuditLogEntry(DiscordSocketClient discord, EntryModel model)
: base(discord, model.Id)
{
Action = model.Action;
Data = SocketAuditLogHelper.CreateData(discord, model);
Reason = model.Reason;
var guild = discord.State.GetGuild(model.GuildId);
User = guild?.GetUser(model.UserId ?? 0);
}
internal static SocketAuditLogEntry Create(DiscordSocketClient discord, EntryModel model)
{
return new SocketAuditLogEntry(discord, model);
}
/// <inheritdoc/>
public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id);
/// <inheritdoc/>
public ActionType Action { get; }
/// <inheritdoc cref="IAuditLogEntry.Data"/>
public ISocketAuditLogData Data { get; }
/// <inheritdoc cref="IAuditLogEntry.User" />
public SocketUser User { get; private set; }
/// <inheritdoc/>
public string Reason { get; }
#region IAuditLogEntry
/// <inheritdoc/>
IUser IAuditLogEntry.User => User;
/// <inheritdoc/>
IAuditLogData IAuditLogEntry.Data => Data;
#endregion
}

View File

@@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.WebSocket;
internal static class SocketAuditLogHelper
{
private static readonly Dictionary<ActionType, Func<DiscordSocketClient, EntryModel, ISocketAuditLogData>> CreateMapping
= new ()
{
[ActionType.GuildUpdated] = SocketGuildUpdateAuditLogData.Create,
[ActionType.ChannelCreated] = SocketChannelCreateAuditLogData.Create,
[ActionType.ChannelUpdated] = SocketChannelUpdateAuditLogData.Create,
[ActionType.ChannelDeleted] = SocketChannelDeleteAuditLogData.Create,
[ActionType.OverwriteCreated] = SocketOverwriteCreateAuditLogData.Create,
[ActionType.OverwriteUpdated] = SocketOverwriteUpdateAuditLogData.Create,
[ActionType.OverwriteDeleted] = SocketOverwriteDeleteAuditLogData.Create,
[ActionType.Kick] = SocketKickAuditLogData.Create,
[ActionType.Prune] = SocketPruneAuditLogData.Create,
[ActionType.Ban] = SocketBanAuditLogData.Create,
[ActionType.Unban] = SocketUnbanAuditLogData.Create,
[ActionType.MemberUpdated] = SocketMemberUpdateAuditLogData.Create,
[ActionType.MemberRoleUpdated] = SocketMemberRoleAuditLogData.Create,
[ActionType.MemberMoved] = SocketMemberMoveAuditLogData.Create,
[ActionType.MemberDisconnected] = SocketMemberDisconnectAuditLogData.Create,
[ActionType.BotAdded] = SocketBotAddAuditLogData.Create,
[ActionType.RoleCreated] = SocketRoleCreateAuditLogData.Create,
[ActionType.RoleUpdated] = SocketRoleUpdateAuditLogData.Create,
[ActionType.RoleDeleted] = SocketRoleDeleteAuditLogData.Create,
[ActionType.InviteCreated] = SocketInviteCreateAuditLogData.Create,
[ActionType.InviteUpdated] = SocketInviteUpdateAuditLogData.Create,
[ActionType.InviteDeleted] = SocketInviteDeleteAuditLogData.Create,
[ActionType.WebhookCreated] = SocketWebhookCreateAuditLogData.Create,
[ActionType.WebhookUpdated] = SocketWebhookUpdateAuditLogData.Create,
[ActionType.WebhookDeleted] = SocketWebhookDeleteAuditLogData.Create,
[ActionType.EmojiCreated] = SocketEmoteCreateAuditLogData.Create,
[ActionType.EmojiUpdated] = SocketEmoteUpdateAuditLogData.Create,
[ActionType.EmojiDeleted] = SocketEmoteDeleteAuditLogData.Create,
[ActionType.MessageDeleted] = SocketMessageDeleteAuditLogData.Create,
[ActionType.MessageBulkDeleted] = SocketMessageBulkDeleteAuditLogData.Create,
[ActionType.MessagePinned] = SocketMessagePinAuditLogData.Create,
[ActionType.MessageUnpinned] = SocketMessageUnpinAuditLogData.Create,
[ActionType.EventCreate] = SocketScheduledEventCreateAuditLogData.Create,
[ActionType.EventUpdate] = SocketScheduledEventUpdateAuditLogData.Create,
[ActionType.EventDelete] = SocketScheduledEventDeleteAuditLogData.Create,
[ActionType.ThreadCreate] = SocketThreadCreateAuditLogData.Create,
[ActionType.ThreadUpdate] = SocketThreadUpdateAuditLogData.Create,
[ActionType.ThreadDelete] = SocketThreadDeleteAuditLogData.Create,
[ActionType.ApplicationCommandPermissionUpdate] = SocketCommandPermissionUpdateAuditLogData.Create,
[ActionType.IntegrationCreated] = SocketIntegrationCreatedAuditLogData.Create,
[ActionType.IntegrationUpdated] = SocketIntegrationUpdatedAuditLogData.Create,
[ActionType.IntegrationDeleted] = SocketIntegrationDeletedAuditLogData.Create,
[ActionType.StageInstanceCreated] = SocketStageInstanceCreateAuditLogData.Create,
[ActionType.StageInstanceUpdated] = SocketStageInstanceUpdatedAuditLogData.Create,
[ActionType.StageInstanceDeleted] = SocketStageInstanceDeleteAuditLogData.Create,
[ActionType.StickerCreated] = SocketStickerCreatedAuditLogData.Create,
[ActionType.StickerUpdated] = SocketStickerUpdatedAuditLogData.Create,
[ActionType.StickerDeleted] = SocketStickerDeletedAuditLogData.Create,
[ActionType.AutoModerationRuleCreate] = SocketAutoModRuleCreatedAuditLogData.Create,
[ActionType.AutoModerationRuleUpdate] = AutoModRuleUpdatedAuditLogData.Create,
[ActionType.AutoModerationRuleDelete] = SocketAutoModRuleDeletedAuditLogData.Create,
[ActionType.AutoModerationBlockMessage] = SocketAutoModBlockedMessageAuditLogData.Create,
[ActionType.AutoModerationFlagToChannel] = SocketAutoModFlaggedMessageAuditLogData.Create,
[ActionType.AutoModerationUserCommunicationDisabled] = SocketAutoModTimeoutUserAuditLogData.Create,
[ActionType.OnboardingQuestionCreated] = SocketOnboardingPromptCreatedAuditLogData.Create,
[ActionType.OnboardingQuestionUpdated] = SocketOnboardingPromptUpdatedAuditLogData.Create,
[ActionType.OnboardingUpdated] = SocketOnboardingUpdatedAuditLogData.Create,
};
public static ISocketAuditLogData CreateData(DiscordSocketClient discord, EntryModel entry)
{
if (CreateMapping.TryGetValue(entry.Action, out var func))
return func(discord, entry);
return null;
}
}

View File

@@ -81,7 +81,7 @@ namespace Discord.WebSocket
DefaultSortOrder = model.DefaultSortOrder.GetValueOrDefault();
Tags = model.ForumTags.GetValueOrDefault(Array.Empty<API.ForumTags>()).Select(
Tags = model.ForumTags.GetValueOrDefault(Array.Empty<API.ForumTag>()).Select(
x => new ForumTag(x.Id, x.Name, x.EmojiId.GetValueOrDefault(null), x.EmojiName.GetValueOrDefault(), x.Moderated)
).ToImmutableArray();

View File

@@ -49,6 +49,8 @@ namespace Discord.WebSocket
private ConcurrentDictionary<ulong, SocketAutoModRule> _automodRules;
private ImmutableArray<GuildEmote> _emotes;
private readonly AuditLogCache _auditLogs;
private AudioClient _audioClient;
private VoiceStateUpdateParams _voiceStateUpdateParams;
#pragma warning restore IDISP002, IDISP006
@@ -415,6 +417,7 @@ namespace Discord.WebSocket
_audioLock = new SemaphoreSlim(1, 1);
_emotes = ImmutableArray.Create<GuildEmote>();
_automodRules = new ConcurrentDictionary<ulong, SocketAutoModRule>();
_auditLogs = new AuditLogCache(client);
}
internal static SocketGuild Create(DiscordSocketClient discord, ClientState state, ExtendedModel model)
{
@@ -1403,9 +1406,36 @@ namespace Discord.WebSocket
/// <returns>
/// A task that represents the asynchronous get operation. The task result contains a read-only collection
/// of the requested audit log entries.
/// </returns>
/// </returns>
public IAsyncEnumerable<IReadOnlyCollection<RestAuditLogEntry>> 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);
/// <summary>
/// Gets all cached audit log entries from this guild.
/// </summary>
public IReadOnlyCollection<SocketAuditLogEntry> CachedAuditLogs => _auditLogs?.AuditLogs ?? ImmutableArray.Create<SocketAuditLogEntry>();
/// <summary>
/// Gets cached audit log entry with the provided id.
/// </summary>
/// <remarks>
/// Returns <see langword="null"/> if no entry with provided id was found in cache.
/// </remarks>
public SocketAuditLogEntry GetCachedAuditLog(ulong id)
=> _auditLogs.Get(id);
/// <summary>
/// Gets audit log entries with the specified type from cache.
/// </summary>
public IReadOnlyCollection<SocketAuditLogEntry> GetCachedAuditLogs(int limit = DiscordConfig.MaxAuditLogEntriesPerBatch, ActionType? action = null,
ulong? fromEntryId = null, Direction direction = Direction.Before)
{
return _auditLogs.GetMany(fromEntryId, direction, limit, action);
}
internal void AddAuditLog(SocketAuditLogEntry entry)
=> _auditLogs.Add(entry);
#endregion
#region Webhooks