diff --git a/Common/Utilities.cs b/Common/Utilities.cs index 3c17d64..ee2a4fc 100644 --- a/Common/Utilities.cs +++ b/Common/Utilities.cs @@ -70,14 +70,7 @@ public static class Utilities { /// Builds and returns an embed which displays this log entry. /// public static Embed BuildEmbed(this Data.ModLogEntry entry, RegexbotClient bot) { - string? issuedDisplay = null; - try { - var entityTry = new EntityName(entry.IssuedBy, EntityType.User); - var issueq = bot.EcQueryUser(entityTry.Id!.Value.ToString()); - if (issueq != null) issuedDisplay = $"<@{issueq.UserId}> - {issueq.Username}#{issueq.Discriminator} `{issueq.UserId}`"; - else issuedDisplay = $"Unknown user with ID `{entityTry.Id!.Value}`"; - } catch (Exception) { } - issuedDisplay ??= entry.IssuedBy; + var issuedDisplay = TryFromEntityNameString(entry.IssuedBy, bot); string targetDisplay; var targetq = bot.EcQueryUser(entry.UserId.ToString()); if (targetq != null) targetDisplay = $"<@{targetq.UserId}> - {targetq.Username}#{targetq.Discriminator} `{targetq.UserId}`"; @@ -107,4 +100,20 @@ public static class Utilities { /// Returns a representation of this entity that can be parsed by the constructor. /// public static string AsEntityNameString(this IUser entity) => $"@{entity.Id}::{entity.Username}"; + + /// + /// If given string is in an EntityName format, returns a displayable representation of it based on + /// a cache query. Otherwise, returns the input string as-is. + /// + [return: NotNullIfNotNull("input")] + public static string? TryFromEntityNameString(string? input, RegexbotClient bot) { + string? result = null; + try { + var entityTry = new EntityName(input!, EntityType.User); + var issueq = bot.EcQueryUser(entityTry.Id!.Value.ToString()); + if (issueq != null) result = $"<@{issueq.UserId}> - {issueq.Username}#{issueq.Discriminator} `{issueq.UserId}`"; + else result = $"Unknown user with ID `{entityTry.Id!.Value}`"; + } catch (Exception) { } + return result ?? input; + } } diff --git a/Modules/ModCommands/Commands/ShowModLogs.cs b/Modules/ModCommands/Commands/ShowModLogs.cs new file mode 100644 index 0000000..9837bb2 --- /dev/null +++ b/Modules/ModCommands/Commands/ShowModLogs.cs @@ -0,0 +1,82 @@ +using Discord; +using Microsoft.EntityFrameworkCore; +using RegexBot.Common; +using RegexBot.Data; + +namespace RegexBot.Modules.ModCommands.Commands; +class ShowModLogs : CommandConfig { + const int LogEntriesPerMessage = 10; + private readonly string _usage; + + protected override string DefaultUsageMsg => _usage; + + // No configuration. + // TODO bring in some options from BanKick. Particularly custom success msg. + // TODO when ModLogs fully implemented, add a reason? + public ShowModLogs(ModCommands module, JObject config) : base(module, config) { + _usage = $"{Command} `user or user ID` [page]\n" + + "Retrieves moderation log entries regarding the specified user."; + } + + // Usage: (command) (query) [page] + public override async Task Invoke(SocketGuild g, SocketMessage msg) { + var line = msg.Content.Split(new char[] { ' ' }, 3, StringSplitOptions.RemoveEmptyEntries); + if (line.Length < 2) { + await SendUsageMessageAsync(msg.Channel, null); + return; + } + int pagenum; + if (line.Length == 3) { + const string PageNumError = ":x: Requested page must be a non-negative number."; + if (!int.TryParse(line[2], out pagenum)) { + await SendUsageMessageAsync(msg.Channel, PageNumError); + } + if (pagenum <= 0) await SendUsageMessageAsync(msg.Channel, PageNumError); + } else pagenum = 1; + + var query = Module.Bot.EcQueryGuildUser(g.Id, line[1]); + if (query == null) { + await msg.Channel.SendMessageAsync(":x: Unable to find the given user."); + return; + } + + int totalPages; + List results; + using (var db = new BotDatabaseContext()) { + var totalEntries = db.ModLogs + .Where(l => l.GuildId == query.GuildId && l.UserId == query.UserId) + .Count(); + totalPages = (int)Math.Ceiling((double)totalEntries / LogEntriesPerMessage); + results = db.ModLogs + .Where(l => l.GuildId == query.GuildId && l.UserId == query.UserId) + .OrderByDescending(l => l.LogId) + .Skip((pagenum - 1) * LogEntriesPerMessage) + .Take(LogEntriesPerMessage) + .AsNoTracking() + .ToList(); + } + + var resultList = new EmbedBuilder() { + Author = new EmbedAuthorBuilder() { + Name = $"{query.User.Username}#{query.User.Discriminator}", + IconUrl = query.User.AvatarUrl + }, + Footer = new EmbedFooterBuilder() { + Text = $"Page {pagenum} of {totalPages}", + IconUrl = Module.Bot.DiscordClient.CurrentUser.GetAvatarUrl() + }, + Title = "Moderation logs" + }; + foreach (var item in results) { + var f = new EmbedFieldBuilder() { + Name = $"{Enum.GetName(item.LogType)} \\#{item.LogId}", + Value = $"**Timestamp**: \n" + + $"**Issued by**: {Utilities.TryFromEntityNameString(item.IssuedBy, Module.Bot)}\n" + + $"**Message**: {item.Message ?? "*none specified*"}" + }; + resultList.AddField(f); + } + + await msg.Channel.SendMessageAsync(embed: resultList.Build()); + } +} \ No newline at end of file diff --git a/Modules/ModCommands/ModuleConfig.cs b/Modules/ModCommands/ModuleConfig.cs index c30b0a8..9b7418e 100644 --- a/Modules/ModCommands/ModuleConfig.cs +++ b/Modules/ModCommands/ModuleConfig.cs @@ -41,7 +41,9 @@ class ModuleConfig { { "addrole", typeof(RoleAdd) }, { "roleadd", typeof(RoleAdd) }, { "delrole", typeof(RoleDel) }, - { "roledel", typeof(RoleDel) } + { "roledel", typeof(RoleDel) }, + { "modlogs", typeof(ShowModLogs) }, + { "showmodlogs", typeof(ShowModLogs) } } );