Browse Source

Merge aa67a5b209 into 9979a027d5

pull/719/merge
Finite Reality GitHub 7 years ago
parent
commit
87bdc6d906
42 changed files with 1547 additions and 0 deletions
  1. +1
    -0
      src/Discord.Net.Core/DiscordConfig.cs
  2. +50
    -0
      src/Discord.Net.Core/Entities/AuditLogs/ActionType.cs
  3. +14
    -0
      src/Discord.Net.Core/Entities/AuditLogs/IAuditLogData.cs
  4. +34
    -0
      src/Discord.Net.Core/Entities/AuditLogs/IAuditLogEntry.cs
  5. +3
    -0
      src/Discord.Net.Core/Entities/Guilds/IGuild.cs
  6. +17
    -0
      src/Discord.Net.Rest/API/Common/AuditLog.cs
  7. +17
    -0
      src/Discord.Net.Rest/API/Common/AuditLogChange.cs
  8. +26
    -0
      src/Discord.Net.Rest/API/Common/AuditLogEntry.cs
  9. +28
    -0
      src/Discord.Net.Rest/API/Common/AuditLogOptions.cs
  10. +16
    -0
      src/Discord.Net.Rest/API/Common/AuditLogWebhook.cs
  11. +8
    -0
      src/Discord.Net.Rest/API/Rest/GetAuditLogsParams.cs
  12. +20
    -0
      src/Discord.Net.Rest/DiscordRestApiClient.cs
  13. +79
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/AuditLogHelper.cs
  14. +23
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/BanAuditLogData.cs
  15. +56
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelCreateAuditLogData.cs
  16. +45
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelDeleteAuditLogData.cs
  17. +67
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs
  18. +31
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteCreateAuditLogData.cs
  19. +27
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteDeleteAuditLogData.cs
  20. +31
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteUpdateAuditLogData.cs
  21. +128
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildUpdateAuditLogData.cs
  22. +55
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteCreateAuditLogData.cs
  23. +55
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteDeleteAuditLogData.cs
  24. +23
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/KickAuditLogData.cs
  25. +50
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleAuditLogData.cs
  26. +35
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberUpdateAuditLogData.cs
  27. +22
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MessageDeleteAuditLogData.cs
  28. +37
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteCreateAuditLogData.cs
  29. +42
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteDeleteAuditLogData.cs
  30. +46
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteUpdateAuditLogData.cs
  31. +22
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/PruneAuditLogData.cs
  32. +52
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleCreateAuditLogData.cs
  33. +52
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleDeleteAuditLogData.cs
  34. +65
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleUpdateAuditLogData.cs
  35. +23
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/UnbanAuditLogData.cs
  36. +44
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookCreateAuditLogData.cs
  37. +44
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookDeleteAuditLogData.cs
  38. +68
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookUpdateAuditLogData.cs
  39. +38
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/RestAuditLogEntry.cs
  40. +29
    -0
      src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs
  41. +12
    -0
      src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
  42. +12
    -0
      src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs

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

@@ -20,6 +20,7 @@ namespace Discord
public const int MaxMessagesPerBatch = 100; public const int MaxMessagesPerBatch = 100;
public const int MaxUsersPerBatch = 1000; public const int MaxUsersPerBatch = 1000;
public const int MaxGuildsPerBatch = 100; public const int MaxGuildsPerBatch = 100;
public const int MaxAuditLogEntriesPerBatch = 100;


/// <summary> Gets or sets how a request should act in the case of an error, by default. </summary> /// <summary> Gets or sets how a request should act in the case of an error, by default. </summary>
public RetryMode DefaultRetryMode { get; set; } = RetryMode.AlwaysRetry; public RetryMode DefaultRetryMode { get; set; } = RetryMode.AlwaysRetry;


+ 50
- 0
src/Discord.Net.Core/Entities/AuditLogs/ActionType.cs View File

@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Discord
{
/// <summary>
/// The action type within a <see cref="IAuditLogEntry"/>
/// </summary>
public enum ActionType
{
GuildUpdated = 1,

ChannelCreated = 10,
ChannelUpdated = 11,
ChannelDeleted = 12,

OverwriteCreated = 13,
OverwriteUpdated = 14,
OverwriteDeleted = 15,

Kick = 20,
Prune = 21,
Ban = 22,
Unban = 23,

MemberUpdated = 24,
MemberRoleUpdated = 25,

RoleCreated = 30,
RoleUpdated = 31,
RoleDeleted = 32,

InviteCreated = 40,
InviteUpdated = 41,
InviteDeleted = 42,

WebhookCreated = 50,
WebhookUpdated = 51,
WebhookDeleted = 52,

EmojiCreated = 60,
EmojiUpdated = 61,
EmojiDeleted = 62,

MessageDeleted = 72
}
}

+ 14
- 0
src/Discord.Net.Core/Entities/AuditLogs/IAuditLogData.cs View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Discord
{
/// <summary>
/// Represents data applied to an <see cref="IAuditLogEntry"/>
/// </summary>
public interface IAuditLogData
{ }
}

+ 34
- 0
src/Discord.Net.Core/Entities/AuditLogs/IAuditLogEntry.cs View File

@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Discord
{
/// <summary>
/// Represents an entry in an audit log
/// </summary>
public interface IAuditLogEntry : IEntity<ulong>
{
/// <summary>
/// The action which occured to create this entry
/// </summary>
ActionType Action { get; }

/// <summary>
/// The data for this entry. May be <see cref="null"/> if no data was available.
/// </summary>
IAuditLogData Data { get; }

/// <summary>
/// The user responsible for causing the changes
/// </summary>
IUser User { get; }

/// <summary>
/// The reason behind the change. May be <see cref="null"/> if no reason was provided.
/// </summary>
string Reason { get; }
}
}

+ 3
- 0
src/Discord.Net.Core/Entities/Guilds/IGuild.cs View File

@@ -117,5 +117,8 @@ namespace Discord
Task DownloadUsersAsync(); Task DownloadUsersAsync();
/// <summary> Removes all users from this guild if they have not logged on in a provided number of days or, if simulate is true, returns the number of users that would be removed. </summary> /// <summary> Removes all users from this guild if they have not logged on in a provided number of days or, if simulate is true, returns the number of users that would be removed. </summary>
Task<int> PruneUsersAsync(int days = 30, bool simulate = false, RequestOptions options = null); Task<int> PruneUsersAsync(int days = 30, bool simulate = false, RequestOptions options = null);

Task<IReadOnlyCollection<IAuditLogEntry>> GetAuditLogAsync(int limit = DiscordConfig.MaxAuditLogEntriesPerBatch,
CacheMode cacheMode = CacheMode.AllowDownload, RequestOptions options = null);
} }
} }

+ 17
- 0
src/Discord.Net.Rest/API/Common/AuditLog.cs View File

@@ -0,0 +1,17 @@
using Newtonsoft.Json;

namespace Discord.API
{
internal class AuditLog
{
//TODO: figure out how this works
[JsonProperty("webhooks")]
public AuditLogWebhook[] Webhooks { get; set; }

[JsonProperty("users")]
public User[] Users { get; set; }

[JsonProperty("audit_log_entries")]
public AuditLogEntry[] Entries { get; set; }
}
}

+ 17
- 0
src/Discord.Net.Rest/API/Common/AuditLogChange.cs View File

@@ -0,0 +1,17 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Discord.API
{
internal class AuditLogChange
{
[JsonProperty("key")]
public string ChangedProperty { get; set; }

[JsonProperty("new_value")]
public JToken NewValue { get; set; }

[JsonProperty("old_value")]
public JToken OldValue { get; set; }
}
}

+ 26
- 0
src/Discord.Net.Rest/API/Common/AuditLogEntry.cs View File

@@ -0,0 +1,26 @@
using Newtonsoft.Json;

namespace Discord.API
{
internal class AuditLogEntry
{
[JsonProperty("target_id")]
public ulong? TargetId { get; set; }
[JsonProperty("user_id")]
public ulong UserId { get; set; }

[JsonProperty("changes")]
public AuditLogChange[] Changes { get; set; }
[JsonProperty("options")]
public AuditLogOptions Options { get; set; }

[JsonProperty("id")]
public ulong Id { get; set; }

[JsonProperty("action_type")]
public ActionType Action { get; set; }

[JsonProperty("reason")]
public string Reason { get; set; }
}
}

+ 28
- 0
src/Discord.Net.Rest/API/Common/AuditLogOptions.cs View File

@@ -0,0 +1,28 @@
using Newtonsoft.Json;

namespace Discord.API
{
//TODO: Complete this with all possible values for options
internal class AuditLogOptions
{
//Message delete
[JsonProperty("count")]
public int? MessageDeleteCount { get; set; } //TODO: what type of int? (returned as string)
[JsonProperty("channel_id")]
public ulong? MessageDeleteChannelId { get; set; }

//Prune
[JsonProperty("delete_member_days")]
public int? PruneDeleteMemberDays { get; set; } //TODO: what type of int? (returned as string)
[JsonProperty("members_removed")]
public int? PruneMembersRemoved { get; set; } //TODO: what type of int? (returned as string)

//Overwrite Update
[JsonProperty("role_name")]
public string OverwriteRoleName { get; set; }
[JsonProperty("type")]
public string OverwriteType { get; set; }
[JsonProperty("id")]
public ulong? OverwriteTargetId { get; set; }
}
}

+ 16
- 0
src/Discord.Net.Rest/API/Common/AuditLogWebhook.cs View File

@@ -0,0 +1,16 @@
using Newtonsoft.Json;

namespace Discord.API
{
internal class AuditLogWebhook : User
{
[JsonProperty("channel_id")]
public ulong ChannelId { get; set; }

[JsonProperty("guild_id")]
public ulong GuildId { get; set; }

[JsonProperty("token")]
public string Token { get; set; }
}
}

+ 8
- 0
src/Discord.Net.Rest/API/Rest/GetAuditLogsParams.cs View File

@@ -0,0 +1,8 @@
namespace Discord.API.Rest
{
class GetAuditLogsParams
{
public Optional<int> Limit { get; set; }
public Optional<ulong> BeforeEntryId { get; set; }
}
}

+ 20
- 0
src/Discord.Net.Rest/DiscordRestApiClient.cs View File

@@ -1066,6 +1066,26 @@ namespace Discord.API
return await SendJsonAsync<IReadOnlyCollection<Role>>("PATCH", () => $"guilds/{guildId}/roles", args, ids, options: options).ConfigureAwait(false); return await SendJsonAsync<IReadOnlyCollection<Role>>("PATCH", () => $"guilds/{guildId}/roles", args, ids, options: options).ConfigureAwait(false);
} }


//Audit logs
public async Task<AuditLog> GetAuditLogsAsync(ulong guildId, GetAuditLogsParams args, RequestOptions options = null)
{
Preconditions.NotEqual(guildId, 0, nameof(guildId));
Preconditions.NotNull(args, nameof(args));
options = RequestOptions.CreateOrClone(options);

int limit = args.Limit.GetValueOrDefault(int.MaxValue);

var ids = new BucketIds(guildId: guildId);
Expression<Func<string>> endpoint;

if (args.BeforeEntryId.IsSpecified)
endpoint = () => $"guilds/{guildId}/audit-logs?limit={limit}&before={args.BeforeEntryId.Value}";
else
endpoint = () => $"guilds/{guildId}/audit-logs?limit={limit}";

return await SendAsync<AuditLog>("GET", endpoint, ids, options: options).ConfigureAwait(false);
}

//Users //Users
public async Task<User> GetUserAsync(ulong userId, RequestOptions options = null) public async Task<User> GetUserAsync(ulong userId, RequestOptions options = null)
{ {


+ 79
- 0
src/Discord.Net.Rest/Entities/AuditLogs/AuditLogHelper.cs View File

@@ -0,0 +1,79 @@
using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
internal static class AuditLogHelper
{
public static IAuditLogData CreateData(BaseDiscordClient discord, Model log, EntryModel entry)
{
switch (entry.Action)
{
case ActionType.GuildUpdated: //1
return GuildUpdateAuditLogData.Create(discord, log, entry);

case ActionType.ChannelCreated: //10
return ChannelCreateAuditLogData.Create(discord, log, entry);
case ActionType.ChannelUpdated:
return ChannelUpdateAuditLogData.Create(discord, log, entry);
case ActionType.ChannelDeleted:
return ChannelDeleteAuditLogData.Create(discord, log, entry);
case ActionType.OverwriteCreated:
return OverwriteCreateAuditLogData.Create(discord, log, entry);
case ActionType.OverwriteUpdated:
return OverwriteUpdateAuditLogData.Create(discord, log, entry);
case ActionType.OverwriteDeleted:
return OverwriteDeleteAuditLogData.Create(discord, log, entry);

case ActionType.Kick: //20
return KickAuditLogData.Create(discord, log, entry);
case ActionType.Prune:
return PruneAuditLogData.Create(discord, log, entry);
case ActionType.Ban:
return BanAuditLogData.Create(discord, log, entry);
case ActionType.Unban:
return UnbanAuditLogData.Create(discord, log, entry);
case ActionType.MemberUpdated:
return MemberUpdateAuditLogData.Create(discord, log, entry);
case ActionType.MemberRoleUpdated:
return MemberRoleAuditLogData.Create(discord, log, entry);

case ActionType.RoleCreated: //30
return RoleCreateAuditLogData.Create(discord, log, entry);
case ActionType.RoleUpdated:
return RoleUpdateAuditLogData.Create(discord, log, entry);
case ActionType.RoleDeleted:
return RoleDeleteAuditLogData.Create(discord, log, entry);

case ActionType.InviteCreated: //40
return InviteCreateAuditLogData.Create(discord, log, entry);
case ActionType.InviteUpdated:
break;
case ActionType.InviteDeleted:
return InviteDeleteAuditLogData.Create(discord, log, entry);

case ActionType.WebhookCreated: //50
return WebhookCreateAuditLogData.Create(discord, log, entry);
case ActionType.WebhookUpdated:
return WebhookUpdateAuditLogData.Create(discord, log, entry);
case ActionType.WebhookDeleted:
return WebhookDeleteAuditLogData.Create(discord, log, entry);

case ActionType.EmojiCreated: //60
return EmoteCreateAuditLogData.Create(discord, log, entry);
case ActionType.EmojiUpdated:
return EmoteUpdateAuditLogData.Create(discord, log, entry);
case ActionType.EmojiDeleted:
return EmoteDeleteAuditLogData.Create(discord, log, entry);

case ActionType.MessageDeleted: //72
return MessageDeleteAuditLogData.Create(discord, log, entry);

default: //Unknown
return null;
}
return null;
//throw new NotImplementedException($"{nameof(AuditLogHelper)} does not implement the {entry.Action} audit log event.");
}
}
}

+ 23
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/BanAuditLogData.cs View File

@@ -0,0 +1,23 @@
using System.Linq;

using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
public class BanAuditLogData : IAuditLogData
{
private BanAuditLogData(IUser user)
{
Target = user;
}

internal static BanAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{
var userInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId);
return new BanAuditLogData(RestUser.Create(discord, userInfo));
}

public IUser Target { get; }
}
}

+ 56
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelCreateAuditLogData.cs View File

@@ -0,0 +1,56 @@
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Linq;

using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
public class ChannelCreateAuditLogData : IAuditLogData
{
private ChannelCreateAuditLogData(ulong id, string name, ChannelType type, IReadOnlyCollection<Overwrite> overwrites)
{
ChannelId = id;
ChannelName = name;
ChannelType = type;
Overwrites = overwrites;
}

internal static ChannelCreateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{
var changes = entry.Changes;
var overwrites = new List<Overwrite>();

var overwritesModel = changes.FirstOrDefault(x => x.ChangedProperty == "permission_overwrites");
var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type");
var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name");

var type = typeModel.NewValue.ToObject<ChannelType>();
var name = nameModel.NewValue.ToObject<string>();

foreach (var overwrite in overwritesModel.NewValue)
{
var deny = overwrite.Value<ulong>("deny");
var _type = overwrite.Value<string>("type");
var id = overwrite.Value<ulong>("id");
var allow = overwrite.Value<ulong>("allow");

PermissionTarget permType;
if (_type == "member")
permType = PermissionTarget.User;
else
permType = PermissionTarget.Role;

overwrites.Add(new Overwrite(id, permType, new OverwritePermissions(allow, deny)));
}

return new ChannelCreateAuditLogData(entry.TargetId.Value, name, type, overwrites.ToReadOnlyCollection());
}

public ulong ChannelId { get; } //TODO: IChannel
public string ChannelName { get; }
public ChannelType ChannelType { get; }
public IReadOnlyCollection<Overwrite> Overwrites { get; }
}
}

+ 45
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelDeleteAuditLogData.cs View File

@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
public class ChannelDeleteAuditLogData : IAuditLogData
{
private ChannelDeleteAuditLogData(ulong id, string name, ChannelType type, IReadOnlyCollection<Overwrite> overwrites)
{
ChannelId = id;
ChannelName = name;
ChannelType = type;
Overwrites = overwrites;
}

internal static ChannelDeleteAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{
var changes = entry.Changes;

var overwritesModel = changes.FirstOrDefault(x => x.ChangedProperty == "permission_overwrites");
var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type");
var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name");

var overwrites = overwritesModel.OldValue.ToObject<API.Overwrite[]>()
.Select(x => new Overwrite(x.TargetId, x.TargetType, new OverwritePermissions(x.Allow, x.Deny)))
.ToList();
var type = typeModel.OldValue.ToObject<ChannelType>();
var name = nameModel.OldValue.ToObject<string>();
var id = entry.TargetId.Value; //NOTE: assuming this is the channel id here

return new ChannelDeleteAuditLogData(id, name, type, overwrites.ToReadOnlyCollection());
}

public ulong ChannelId { get; } //TODO: IChannel
public string ChannelName { get; }
public ChannelType ChannelType { get; }
public IReadOnlyCollection<Overwrite> Overwrites { get; }
}
}

+ 67
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs View File

@@ -0,0 +1,67 @@
using System.Linq;

using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
public class ChannelUpdateAuditLogData : IAuditLogData
{
private ChannelUpdateAuditLogData(GuildChannelProperties before, GuildChannelProperties after)
{
Before = before;
After = after;
}

internal static ChannelUpdateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{
var changes = entry.Changes;

var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name");
var topicModel = changes.FirstOrDefault(x => x.ChangedProperty == "topic");
var bitrateModel = changes.FirstOrDefault(x => x.ChangedProperty == "bitrate");
var userLimitModel = changes.FirstOrDefault(x => x.ChangedProperty == "user_limit");

if (topicModel != null) //If topic is supplied, we must be a text channel
{
var before = new TextChannelProperties
{
Name = nameModel?.OldValue?.ToObject<string>(),
Topic = topicModel.OldValue?.ToObject<string>()
};
var after = new TextChannelProperties
{
Name = nameModel?.NewValue?.ToObject<string>(),
Topic = topicModel.NewValue?.ToObject<string>()
};

return new ChannelUpdateAuditLogData(before, after);
}
else //By process of elimination, we must be a voice channel
{
var beforeBitrate = bitrateModel?.OldValue?.ToObject<int>();
var afterBitrate = bitrateModel?.NewValue?.ToObject<int>();
var beforeUserLimit = userLimitModel?.OldValue?.ToObject<int>();
var afterUserLimit = userLimitModel?.NewValue?.ToObject<int>();

var before = new VoiceChannelProperties
{
Name = nameModel?.OldValue?.ToObject<string>(),
Bitrate = beforeBitrate ?? Optional<int>.Unspecified,
UserLimit = beforeUserLimit
};
var after = new VoiceChannelProperties
{
Name = nameModel?.NewValue?.ToObject<string>(),
Bitrate = afterBitrate ?? Optional<int>.Unspecified,
UserLimit = afterUserLimit
};

return new ChannelUpdateAuditLogData(before, after);
}
}

public GuildChannelProperties Before { get; set; }
public GuildChannelProperties After { get; set; }
}
}

+ 31
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteCreateAuditLogData.cs View File

@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
public class EmoteCreateAuditLogData : IAuditLogData
{
private EmoteCreateAuditLogData(Emote emote)
{
Emote = emote;
}

internal static EmoteCreateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{
var change = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "name"); //TODO: only change?

var emoteName = change.NewValue?.ToObject<string>();
var emote = new Emote(entry.TargetId.Value, emoteName);

return new EmoteCreateAuditLogData(emote);
}

public Emote Emote { get; }
}
}

+ 27
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteDeleteAuditLogData.cs View File

@@ -0,0 +1,27 @@
using System.Linq;

using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
public class EmoteDeleteAuditLogData : IAuditLogData
{
private EmoteDeleteAuditLogData(Emote emote)
{
Emote = emote;
}

internal static EmoteDeleteAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{
var change = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "name"); //TODO: only change?

var emoteName = change.OldValue?.ToObject<string>();
var emote = new Emote(entry.TargetId.Value, emoteName);

return new EmoteDeleteAuditLogData(emote);
}

public Emote Emote { get; }
}
}

+ 31
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteUpdateAuditLogData.cs View File

@@ -0,0 +1,31 @@
using System.Linq;

using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
public class EmoteUpdateAuditLogData : IAuditLogData
{
private EmoteUpdateAuditLogData(Emote emote, string oldName)
{
Emote = emote;
PreviousName = oldName;
}

internal static EmoteUpdateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{
var change = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "name"); //TODO: only change?

var emoteName = change.NewValue?.ToObject<string>();
var emote = new Emote(entry.TargetId.Value, emoteName);

var oldName = change.OldValue?.ToObject<string>();

return new EmoteUpdateAuditLogData(emote, oldName);
}

public Emote Emote { get; }
public string PreviousName { get; }
}
}

+ 128
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildUpdateAuditLogData.cs View File

@@ -0,0 +1,128 @@
using System.Linq;

using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
public class GuildUpdateAuditLogData : IAuditLogData
{
private GuildUpdateAuditLogData(GuildInfo before, GuildInfo after)
{
Before = before;
After = after;
}

internal static GuildUpdateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{
var changes = entry.Changes;

int? oldAfkTimeout = null,
newAfkTimeout = null;
DefaultMessageNotifications? oldDefaultMessageNotifications = null,
newDefaultMessageNotifications = null;
ulong? oldAfkChannelId = null,
newAfkChannelId = null;
string oldName = null,
newName = null;
string oldRegionId = null,
newRegionId = null;
string oldIconHash = null,
newIconHash = null;
VerificationLevel? oldVerificationLevel = null,
newVerificationLevel = null;
ulong? oldOwnerId = null,
newOwnerId = null;

foreach (var change in changes)
{
switch (change.ChangedProperty)
{
case "afk_timeout":
oldAfkTimeout = change.OldValue?.ToObject<int>();
newAfkTimeout = change.NewValue?.ToObject<int>();
break;
case "default_message_notifications":
oldDefaultMessageNotifications = change.OldValue?.ToObject<DefaultMessageNotifications>();
newDefaultMessageNotifications = change.OldValue?.ToObject<DefaultMessageNotifications>();
break;
case "afk_channel_id":
oldAfkChannelId = change.OldValue?.ToObject<ulong>();
newAfkChannelId = change.NewValue?.ToObject<ulong>();
break;
case "name":
oldName = change.OldValue?.ToObject<string>();
newName = change.NewValue?.ToObject<string>();
break;
case "region":
oldRegionId = change.OldValue?.ToObject<string>();
newRegionId = change.NewValue?.ToObject<string>();
break;
case "icon_hash":
oldIconHash = change.OldValue?.ToObject<string>();
newIconHash = change.NewValue?.ToObject<string>();
break;
case "verification_level":
oldVerificationLevel = change.OldValue?.ToObject<VerificationLevel>();
newVerificationLevel = change.NewValue?.ToObject<VerificationLevel>();
break;
case "owner":
oldOwnerId = change.OldValue?.ToObject<ulong>();
newOwnerId = change.NewValue?.ToObject<ulong>();
break;
//TODO: 2fa auth, explicit content filter
}
}

IUser oldOwner = null;
if (oldOwnerId != null)
{
var oldOwnerInfo = log.Users.FirstOrDefault(x => x.Id == oldOwnerId.Value);
oldOwner = RestUser.Create(discord, oldOwnerInfo);
}

IUser newOwner = null;
if (newOwnerId != null)
{
var newOwnerInfo = log.Users.FirstOrDefault(x => x.Id == newOwnerId.Value);
newOwner = RestUser.Create(discord, newOwnerInfo);
}

var before = new GuildInfo(oldAfkTimeout, oldDefaultMessageNotifications,
oldAfkChannelId, oldName, oldRegionId, oldIconHash, oldVerificationLevel, oldOwner);
var after = new GuildInfo(newAfkTimeout, newDefaultMessageNotifications,
newAfkChannelId, newName, newRegionId, newIconHash, newVerificationLevel, newOwner);

return new GuildUpdateAuditLogData(before, after);
}

public GuildInfo Before { get; }
public GuildInfo After { get; }

public struct GuildInfo
{
internal GuildInfo(int? afkTimeout, DefaultMessageNotifications? defaultNotifs,
ulong? afkChannel, string name, string region, string icon,
VerificationLevel? verification, IUser owner)
{
AfkTimeout = afkTimeout;
DefaultMessageNotifications = defaultNotifs;
AfkChannelId = afkChannel;
Name = name;
RegionId = region;
IconHash = icon;
VerificationLevel = verification;
Owner = owner;
}

public int? AfkTimeout { get; }
public DefaultMessageNotifications? DefaultMessageNotifications { get; }
public ulong? AfkChannelId { get; }
public string Name { get; }
public string RegionId { get; }
public string IconHash { get; }
public VerificationLevel? VerificationLevel { get; }
public IUser Owner { get; }
}
}
}

+ 55
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteCreateAuditLogData.cs View File

@@ -0,0 +1,55 @@
using System.Linq;

using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
public class InviteCreateAuditLogData : IAuditLogData
{
private InviteCreateAuditLogData(int maxAge, string code, bool temporary, IUser inviter, ulong channelId, int uses, int maxUses)
{
MaxAge = maxAge;
Code = code;
Temporary = temporary;
Creator = inviter;
ChannelId = channelId;
Uses = uses;
MaxUses = maxUses;
}

internal static InviteCreateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{
var changes = entry.Changes;

var maxAgeModel = changes.FirstOrDefault(x => x.ChangedProperty == "max_age");
var codeModel = changes.FirstOrDefault(x => x.ChangedProperty == "code");
var temporaryModel = changes.FirstOrDefault(x => x.ChangedProperty == "temporary");
var inviterIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "inviter_id");
var channelIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "channel_id");
var usesModel = changes.FirstOrDefault(x => x.ChangedProperty == "uses");
var maxUsesModel = changes.FirstOrDefault(x => x.ChangedProperty == "max_uses");

var maxAge = maxAgeModel.NewValue.ToObject<int>();
var code = codeModel.NewValue.ToObject<string>();
var temporary = temporaryModel.NewValue.ToObject<bool>();
var inviterId = inviterIdModel.NewValue.ToObject<ulong>();
var channelId = channelIdModel.NewValue.ToObject<ulong>();
var uses = usesModel.NewValue.ToObject<int>();
var maxUses = maxUsesModel.NewValue.ToObject<int>();

var inviterInfo = log.Users.FirstOrDefault(x => x.Id == inviterId);
var inviter = RestUser.Create(discord, inviterInfo);

return new InviteCreateAuditLogData(maxAge, code, temporary, inviter, channelId, uses, maxUses);
}

public int MaxAge { get; }
public string Code { get; }
public bool Temporary { get; }
public IUser Creator { get; }
public ulong ChannelId { get; } //TODO: IChannel-ify
public int Uses { get; }
public int MaxUses { get; }
}
}

+ 55
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteDeleteAuditLogData.cs View File

@@ -0,0 +1,55 @@
using System.Linq;

using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
public class InviteDeleteAuditLogData : IAuditLogData
{
private InviteDeleteAuditLogData(int maxAge, string code, bool temporary, IUser inviter, ulong channelId, int uses, int maxUses)
{
MaxAge = maxAge;
Code = code;
Temporary = temporary;
Creator = inviter;
ChannelId = channelId;
Uses = uses;
MaxUses = maxUses;
}

internal static InviteDeleteAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{
var changes = entry.Changes;

var maxAgeModel = changes.FirstOrDefault(x => x.ChangedProperty == "max_age");
var codeModel = changes.FirstOrDefault(x => x.ChangedProperty == "code");
var temporaryModel = changes.FirstOrDefault(x => x.ChangedProperty == "temporary");
var inviterIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "inviter_id");
var channelIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "channel_id");
var usesModel = changes.FirstOrDefault(x => x.ChangedProperty == "uses");
var maxUsesModel = changes.FirstOrDefault(x => x.ChangedProperty == "max_uses");

var maxAge = maxAgeModel.OldValue.ToObject<int>();
var code = codeModel.OldValue.ToObject<string>();
var temporary = temporaryModel.OldValue.ToObject<bool>();
var inviterId = inviterIdModel.OldValue.ToObject<ulong>();
var channelId = channelIdModel.OldValue.ToObject<ulong>();
var uses = usesModel.OldValue.ToObject<int>();
var maxUses = maxUsesModel.OldValue.ToObject<int>();

var inviterInfo = log.Users.FirstOrDefault(x => x.Id == inviterId);
var inviter = RestUser.Create(discord, inviterInfo);

return new InviteDeleteAuditLogData(maxAge, code, temporary, inviter, channelId, uses, maxUses);
}

public int MaxAge { get; }
public string Code { get; }
public bool Temporary { get; }
public IUser Creator { get; }
public ulong ChannelId { get; } //TODO: IChannel-ify
public int Uses { get; }
public int MaxUses { get; }
}
}

+ 23
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/KickAuditLogData.cs View File

@@ -0,0 +1,23 @@
using System.Linq;

using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
public class KickAuditLogData : IAuditLogData
{
private KickAuditLogData(RestUser user)
{
Target = user;
}

internal static KickAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{
var userInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId);
return new KickAuditLogData(RestUser.Create(discord, userInfo));
}

public IUser Target { get; }
}
}

+ 50
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleAuditLogData.cs View File

@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;

using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
public class MemberRoleAuditLogData : IAuditLogData
{
private MemberRoleAuditLogData(IReadOnlyCollection<RoleInfo> roles, IUser target)
{
Roles = roles;
TargetUser = target;
}

internal static MemberRoleAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{
var changes = entry.Changes;

var roleInfos = changes.SelectMany(x => x.NewValue.ToObject<API.Role[]>(),
(model, role) => new { model.ChangedProperty, Role = role })
.Select(x => new RoleInfo(x.Role.Name, x.Role.Id, x.ChangedProperty == "$add"))
.ToList();

var userInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId);
var user = RestUser.Create(discord, userInfo);

return new MemberRoleAuditLogData(roleInfos.ToReadOnlyCollection(), user);
}

public IReadOnlyCollection<RoleInfo> Roles { get; }
public IUser TargetUser { get; }

public struct RoleInfo
{
internal RoleInfo(string name, ulong roleId, bool added)
{
Name = name;
RoleId = roleId;
Added = added;
}

string Name { get; }
ulong RoleId { get; }
bool Added { get; }
}
}
}

+ 35
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberUpdateAuditLogData.cs View File

@@ -0,0 +1,35 @@
using System.Linq;

using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;
using ChangeModel = Discord.API.AuditLogChange;

namespace Discord.Rest
{
public class MemberUpdateAuditLogData : IAuditLogData
{
private MemberUpdateAuditLogData(IUser user, string newNick, string oldNick)
{
User = user;
NewNick = newNick;
OldNick = oldNick;
}

internal static MemberUpdateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{
var changes = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "nick"); //TODO: only change?

var newNick = changes.NewValue?.ToObject<string>();
var oldNick = changes.OldValue?.ToObject<string>();

var targetInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId);
var user = RestUser.Create(discord, targetInfo);

return new MemberUpdateAuditLogData(user, newNick, oldNick);
}

public IUser User { get; }
public string NewNick { get; }
public string OldNick { get; }
}
}

+ 22
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MessageDeleteAuditLogData.cs View File

@@ -0,0 +1,22 @@
using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
public class MessageDeleteAuditLogData : IAuditLogData
{
private MessageDeleteAuditLogData(ulong channelId, int count)
{
ChannelId = channelId;
MessageCount = count;
}

internal static MessageDeleteAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{
return new MessageDeleteAuditLogData(entry.Options.MessageDeleteChannelId.Value, entry.Options.MessageDeleteCount.Value);
}

public int MessageCount { get; }
public ulong ChannelId { get; } //TODO: IChannel
}
}

+ 37
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteCreateAuditLogData.cs View File

@@ -0,0 +1,37 @@
using System.Linq;

using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
public class OverwriteCreateAuditLogData : IAuditLogData
{
private OverwriteCreateAuditLogData(Overwrite overwrite)
{
Overwrite = overwrite;
}

internal static OverwriteCreateAuditLogData Create(BaseDiscordClient discord, Model log, 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>();
var allow = allowModel.NewValue.ToObject<ulong>();

var permissions = new OverwritePermissions(allow, deny);

var id = entry.Options.OverwriteTargetId.Value;
var type = entry.Options.OverwriteType;

PermissionTarget target = type == "member" ? PermissionTarget.User : PermissionTarget.Role;

return new OverwriteCreateAuditLogData(new Overwrite(id, target, permissions));
}

public Overwrite Overwrite { get; }
}
}

+ 42
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteDeleteAuditLogData.cs View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;
using ChangeModel = Discord.API.AuditLogChange;
using OptionModel = Discord.API.AuditLogOptions;

namespace Discord.Rest
{
public class OverwriteDeleteAuditLogData : IAuditLogData
{
private OverwriteDeleteAuditLogData(Overwrite deletedOverwrite)
{
Overwrite = deletedOverwrite;
}

internal static OverwriteDeleteAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{
var changes = entry.Changes;

var denyModel = changes.FirstOrDefault(x => x.ChangedProperty == "deny");
var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type");
var idModel = changes.FirstOrDefault(x => x.ChangedProperty == "id");
var allowModel = changes.FirstOrDefault(x => x.ChangedProperty == "allow");

var deny = denyModel.OldValue.ToObject<ulong>();
var type = typeModel.OldValue.ToObject<string>(); //'role' or 'member', can't use PermissionsTarget :(
var id = idModel.OldValue.ToObject<ulong>();
var allow = allowModel.OldValue.ToObject<ulong>();

PermissionTarget target = type == "member" ? PermissionTarget.User : PermissionTarget.Role;

return new OverwriteDeleteAuditLogData(new Overwrite(id, target, new OverwritePermissions(allow, deny)));
}

public Overwrite Overwrite { get; }
}
}

+ 46
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteUpdateAuditLogData.cs View File

@@ -0,0 +1,46 @@
using System.Linq;

using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
public class OverwriteUpdateAuditLogData : IAuditLogData
{
private OverwriteUpdateAuditLogData(OverwritePermissions before, OverwritePermissions after, ulong targetId, PermissionTarget targetType)
{
Before = before;
After = after;
OverwriteTargetId = targetId;
OverwriteType = targetType;
}

internal static OverwriteUpdateAuditLogData Create(BaseDiscordClient discord, Model log, 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>();
var afterAllow = allowModel?.NewValue?.ToObject<ulong>();
var beforeDeny = denyModel?.OldValue?.ToObject<ulong>();
var afterDeny = denyModel?.OldValue?.ToObject<ulong>();

var beforePermissions = new OverwritePermissions(beforeAllow ?? 0, beforeDeny ?? 0);
var afterPermissions = new OverwritePermissions(afterAllow ?? 0, afterDeny ?? 0);

PermissionTarget target = entry.Options.OverwriteType == "member" ? PermissionTarget.User : PermissionTarget.Role;

return new OverwriteUpdateAuditLogData(beforePermissions, afterPermissions, entry.Options.OverwriteTargetId.Value, target);
}

//TODO: this is kind of janky. Should I leave it, create a custom type, or what?
public OverwritePermissions Before { get; }
public OverwritePermissions After { get; }

public ulong OverwriteTargetId { get; }
public PermissionTarget OverwriteType { get; }
//TODO: should we also include the role name if it is given?
}
}

+ 22
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/PruneAuditLogData.cs View File

@@ -0,0 +1,22 @@
using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
public class PruneAuditLogData : IAuditLogData
{
private PruneAuditLogData(int pruneDays, int membersRemoved)
{
PruneDays = pruneDays;
MembersRemoved = membersRemoved;
}

internal static PruneAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{
return new PruneAuditLogData(entry.Options.PruneDeleteMemberDays.Value, entry.Options.PruneMembersRemoved.Value);
}

public int PruneDays { get; }
public int MembersRemoved { get; }
}
}

+ 52
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleCreateAuditLogData.cs View File

@@ -0,0 +1,52 @@
using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
public class RoleCreateAuditLogData : IAuditLogData
{
private RoleCreateAuditLogData(RoleProperties newProps)
{
Properties = newProps;
}

internal static RoleCreateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{
var changes = entry.Changes;

var newProps = new RoleProperties();

foreach (var model in changes)
{
switch (model.ChangedProperty)
{
case "color":
if (model.NewValue != null)
newProps.Color = new Color(model.NewValue.ToObject<uint>());
break;
case "mentionable":
if (model.NewValue != null)
newProps.Mentionable = model.NewValue.ToObject<bool>();
break;
case "hoist":
if (model.NewValue != null)
newProps.Hoist = model.NewValue.ToObject<bool>();
break;
case "name":
if (model.NewValue != null)
newProps.Name = model.NewValue.ToObject<string>();
break;
case "permissions":
if (model.NewValue != null)
newProps.Permissions = new GuildPermissions(model.NewValue.ToObject<ulong>());
break;
}
}

return new RoleCreateAuditLogData(newProps);
}

//TODO: replace this with something read-only
public RoleProperties Properties { get; }
}
}

+ 52
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleDeleteAuditLogData.cs View File

@@ -0,0 +1,52 @@
using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
public class RoleDeleteAuditLogData : IAuditLogData
{
private RoleDeleteAuditLogData(RoleProperties properties)
{
Properties = properties;
}

internal static RoleDeleteAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{
var changes = entry.Changes;

var oldProps = new RoleProperties();

foreach (var model in changes)
{
switch (model.ChangedProperty)
{
case "color":
if (model.OldValue != null)
oldProps.Color = new Color(model.OldValue.ToObject<uint>());
break;
case "mentionable":
if (model.OldValue != null)
oldProps.Mentionable = model.OldValue.ToObject<bool>();
break;
case "hoist":
if (model.OldValue != null)
oldProps.Hoist = model.OldValue.ToObject<bool>();
break;
case "name":
if (model.OldValue != null)
oldProps.Name = model.OldValue.ToObject<string>();
break;
case "permissions":
if (model.OldValue != null)
oldProps.Permissions = new GuildPermissions(model.OldValue.ToObject<ulong>());
break;
}
}

return new RoleDeleteAuditLogData(oldProps);
}

//TODO: replace this with something read-only
public RoleProperties Properties { get; }
}
}

+ 65
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleUpdateAuditLogData.cs View File

@@ -0,0 +1,65 @@
using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
public class RoleUpdateAuditLogData : IAuditLogData
{
private RoleUpdateAuditLogData(RoleProperties oldProps, RoleProperties newProps)
{
Before = oldProps;
After = newProps;
}

internal static RoleUpdateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{
var changes = entry.Changes;

var newProps = new RoleProperties();
var oldProps = new RoleProperties();

foreach (var model in changes)
{
switch (model.ChangedProperty)
{
case "color":
if (model.NewValue != null)
newProps.Color = new Color(model.NewValue.ToObject<uint>());
if (model.OldValue != null)
oldProps.Color = new Color(model.OldValue.ToObject<uint>());
break;
case "mentionable":
if (model.NewValue != null)
newProps.Mentionable = model.NewValue.ToObject<bool>();
if (model.OldValue != null)
oldProps.Mentionable = model.OldValue.ToObject<bool>();
break;
case "hoist":
if (model.NewValue != null)
newProps.Hoist = model.NewValue.ToObject<bool>();
if (model.OldValue != null)
oldProps.Hoist = model.OldValue.ToObject<bool>();
break;
case "name":
if (model.NewValue != null)
newProps.Name = model.NewValue.ToObject<string>();
if (model.OldValue != null)
oldProps.Name = model.OldValue.ToObject<string>();
break;
case "permissions":
if (model.NewValue != null)
newProps.Permissions = new GuildPermissions(model.NewValue.ToObject<ulong>());
if (model.OldValue != null)
oldProps.Permissions = new GuildPermissions(model.OldValue.ToObject<ulong>());
break;
}
}

return new RoleUpdateAuditLogData(oldProps, newProps);
}

//TODO: replace these with something read-only
public RoleProperties Before { get; }
public RoleProperties After { get; }
}
}

+ 23
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/UnbanAuditLogData.cs View File

@@ -0,0 +1,23 @@
using System.Linq;

using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
public class UnbanAuditLogData : IAuditLogData
{
private UnbanAuditLogData(IUser user)
{
Target = user;
}

internal static UnbanAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{
var userInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId);
return new UnbanAuditLogData(RestUser.Create(discord, userInfo));
}

public IUser Target { get; }
}
}

+ 44
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookCreateAuditLogData.cs View File

@@ -0,0 +1,44 @@
using System.Linq;

using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
public class WebhookCreateAuditLogData : IAuditLogData
{
private WebhookCreateAuditLogData(IWebhookUser user, string token, string name, ulong channelId)
{
Webhook = user;
Token = token;
Name = name;
ChannelId = channelId;
}

internal static WebhookCreateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{
var changes = entry.Changes;

var channelIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "channel_id");
var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type");
var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name");

var channelId = channelIdModel.NewValue.ToObject<ulong>();
var type = typeModel.NewValue.ToObject<int>(); //TODO: what on *earth* is this for
var name = nameModel.NewValue.ToObject<string>();

var webhookInfo = log.Webhooks?.FirstOrDefault(x => x.Id == entry.TargetId);
var userInfo = RestWebhookUser.Create(discord, null, webhookInfo, entry.TargetId.Value);

return new WebhookCreateAuditLogData(userInfo, webhookInfo.Token, name, channelId);
}

//Corresponds to the *current* data
public IWebhookUser Webhook { get; }
public string Token { get; }

//Corresponds to the *audit log* data
public string Name { get; }
public ulong ChannelId { get; }
}
}

+ 44
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookDeleteAuditLogData.cs View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
public class WebhookDeleteAuditLogData : IAuditLogData
{
private WebhookDeleteAuditLogData(ulong id, ulong channel, string name, string avatar)
{
WebhookId = id;
ChannelId = channel;
Name = name;
Avatar = avatar;
}

internal static WebhookDeleteAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{
var changes = entry.Changes;

var channelIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "channel_id");
var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type");
var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name");
var avatarHashModel = changes.FirstOrDefault(x => x.ChangedProperty == "avatar_hash");

var channelId = channelIdModel.OldValue.ToObject<ulong>();
var type = typeModel.OldValue.ToObject<int>();
var name = nameModel.OldValue.ToObject<string>();
var avatarHash = avatarHashModel?.OldValue?.ToObject<string>();

return new WebhookDeleteAuditLogData(entry.TargetId.Value, channelId, name, avatarHash);
}

public ulong WebhookId { get; }
public ulong ChannelId { get; }
public string Name { get; }
public string Avatar { get; }
}
}

+ 68
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookUpdateAuditLogData.cs View File

@@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
public class WebhookUpdateAuditLogData : IAuditLogData
{
private WebhookUpdateAuditLogData(IWebhookUser user, string token, WebhookInfo before, WebhookInfo after)
{
Webhook = user;
Token = token;
Before = before;
After = after;
}

internal static WebhookUpdateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{
var changes = entry.Changes;

var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name");
var channelIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "channel_id");
var avatarHashModel = changes.FirstOrDefault(x => x.ChangedProperty == "avatar_hash");

var oldName = nameModel?.OldValue?.ToObject<string>();
var oldChannelId = channelIdModel?.OldValue?.ToObject<ulong>();
var oldAvatar = avatarHashModel?.OldValue?.ToObject<string>();
var before = new WebhookInfo(oldName, oldChannelId, oldAvatar);

var newName = nameModel?.NewValue?.ToObject<string>();
var newChannelId = channelIdModel?.NewValue?.ToObject<ulong>();
var newAvatar = avatarHashModel?.NewValue?.ToObject<string>();
var after = new WebhookInfo(newName, newChannelId, newAvatar);

var webhookInfo = log.Webhooks?.FirstOrDefault(x => x.Id == entry.TargetId);
var userInfo = RestWebhookUser.Create(discord, null, webhookInfo, entry.TargetId.Value);

return new WebhookUpdateAuditLogData(userInfo, webhookInfo.Token, before, after);
}

//Again, the *current* data
public IWebhookUser Webhook { get; }
public string Token { get; }

//And the *audit log* data
public WebhookInfo Before { get; }
public WebhookInfo After { get; }

public struct WebhookInfo
{
internal WebhookInfo(string name, ulong? channelId, string avatar)
{
Name = name;
ChannelId = channelId;
Avatar = avatar;
}

public string Name { get; }
public ulong? ChannelId { get; }
public string Avatar { get; }
}
}
}

+ 38
- 0
src/Discord.Net.Rest/Entities/AuditLogs/RestAuditLogEntry.cs View File

@@ -0,0 +1,38 @@
using System.Linq;

using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;

namespace Discord.Rest
{
public class RestAuditLogEntry : RestEntity<ulong>, IAuditLogEntry
{
private RestAuditLogEntry(BaseDiscordClient discord, Model fullLog, EntryModel model, IUser user)
: base(discord, model.Id)
{
Action = model.Action;
Data = AuditLogHelper.CreateData(discord, fullLog, model);
User = user;
Reason = model.Reason;
}

internal static RestAuditLogEntry Create(BaseDiscordClient discord, Model fullLog, EntryModel model)
{
var userInfo = fullLog.Users.FirstOrDefault(x => x.Id == model.UserId);
IUser user = null;
if (userInfo != null)
user = RestUser.Create(discord, userInfo);

return new RestAuditLogEntry(discord, fullLog, model, user);
}

/// <inheritdoc/>
public ActionType Action { get; }
/// <inheritdoc/>
public IAuditLogData Data { get; }
/// <inheritdoc/>
public IUser User { get; }
/// <inheritdoc/>
public string Reason { get; }
}
}

+ 29
- 0
src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs View File

@@ -253,5 +253,34 @@ namespace Discord.Rest
model = await client.ApiClient.BeginGuildPruneAsync(guild.Id, args, options).ConfigureAwait(false); model = await client.ApiClient.BeginGuildPruneAsync(guild.Id, args, options).ConfigureAwait(false);
return model.Pruned; return model.Pruned;
} }

public static IAsyncEnumerable<IReadOnlyCollection<RestAuditLogEntry>> GetAuditLogsAsync(IGuild guild, BaseDiscordClient client,
ulong? from, int? limit, RequestOptions options)
{
return new PagedAsyncEnumerable<RestAuditLogEntry>(
DiscordConfig.MaxAuditLogEntriesPerBatch,
async (info, ct) =>
{
var args = new GetAuditLogsParams
{
Limit = info.PageSize
};
if (info.Position != null)
args.BeforeEntryId = info.Position.Value;
var model = await client.ApiClient.GetAuditLogsAsync(guild.Id, args, options);
return model.Entries.Select((x) => RestAuditLogEntry.Create(client, model, x)).ToImmutableArray();
},
nextPage: (info, lastPage) =>
{
if (lastPage.Count != DiscordConfig.MaxAuditLogEntriesPerBatch)
return false;
info.Position = lastPage.Min(x => x.Id);
return true;
},
start: from,
count: limit
);

}
} }
} }

+ 12
- 0
src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs View File

@@ -257,6 +257,10 @@ namespace Discord.Rest
public Task<int> PruneUsersAsync(int days = 30, bool simulate = false, RequestOptions options = null) public Task<int> PruneUsersAsync(int days = 30, bool simulate = false, RequestOptions options = null)
=> GuildHelper.PruneUsersAsync(this, Discord, days, simulate, options); => GuildHelper.PruneUsersAsync(this, Discord, days, simulate, options);


//Audit logs
public IAsyncEnumerable<IReadOnlyCollection<RestAuditLogEntry>> GetAuditLogsAsync(int limit, RequestOptions options = null)
=> GuildHelper.GetAuditLogsAsync(this, Discord, null, limit, options);

public override string ToString() => Name; public override string ToString() => Name;
private string DebuggerDisplay => $"{Name} ({Id})"; private string DebuggerDisplay => $"{Name} ({Id})";


@@ -386,5 +390,13 @@ namespace Discord.Rest
return ImmutableArray.Create<IGuildUser>(); return ImmutableArray.Create<IGuildUser>();
} }
Task IGuild.DownloadUsersAsync() { throw new NotSupportedException(); } Task IGuild.DownloadUsersAsync() { throw new NotSupportedException(); }

async Task<IReadOnlyCollection<IAuditLogEntry>> IGuild.GetAuditLogAsync(int limit, CacheMode cacheMode, RequestOptions options)
{
if (cacheMode == CacheMode.AllowDownload)
return (await GetAuditLogsAsync(limit, options).Flatten().ConfigureAwait(false)).ToImmutableArray();
else
return ImmutableArray.Create<IAuditLogEntry>();
}
} }
} }

+ 12
- 0
src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs View File

@@ -433,6 +433,10 @@ namespace Discord.WebSocket
_downloaderPromise.TrySetResultAsync(true); _downloaderPromise.TrySetResultAsync(true);
} }


//Audit logs
public IAsyncEnumerable<IReadOnlyCollection<RestAuditLogEntry>> GetAuditLogsAsync(int limit, RequestOptions options = null)
=> GuildHelper.GetAuditLogsAsync(this, Discord, null, limit, options);

//Voice States //Voice States
internal async Task<SocketVoiceState> AddOrUpdateVoiceStateAsync(ClientState state, VoiceStateModel model) internal async Task<SocketVoiceState> AddOrUpdateVoiceStateAsync(ClientState state, VoiceStateModel model)
{ {
@@ -672,5 +676,13 @@ namespace Discord.WebSocket
Task<IGuildUser> IGuild.GetOwnerAsync(CacheMode mode, RequestOptions options) Task<IGuildUser> IGuild.GetOwnerAsync(CacheMode mode, RequestOptions options)
=> Task.FromResult<IGuildUser>(Owner); => Task.FromResult<IGuildUser>(Owner);
Task IGuild.DownloadUsersAsync() { throw new NotSupportedException(); } Task IGuild.DownloadUsersAsync() { throw new NotSupportedException(); }

async Task<IReadOnlyCollection<IAuditLogEntry>> IGuild.GetAuditLogAsync(int limit, CacheMode cacheMode, RequestOptions options)
{
if (cacheMode == CacheMode.AllowDownload)
return (await GetAuditLogsAsync(limit, options).Flatten().ConfigureAwait(false)).ToImmutableArray();
else
return ImmutableArray.Create<IAuditLogEntry>();
}
} }
} }

Loading…
Cancel
Save