diff --git a/PluralKit.Bot/Commands/Lists/ContextListExt.cs b/PluralKit.Bot/Commands/Lists/ContextListExt.cs index 1b8a562c..891dfa6e 100644 --- a/PluralKit.Bot/Commands/Lists/ContextListExt.cs +++ b/PluralKit.Bot/Commands/Lists/ContextListExt.cs @@ -63,8 +63,7 @@ public static class ContextListExt if (ctx.MatchFlag("with-last-switch", "with-last-fronted", "with-last-front", "wls", "wlf")) p.IncludeLastSwitch = true; if (ctx.MatchFlag("with-last-message", "with-last-proxy", "wlm", "wlp")) - throw new PKError("Sorting by last message is temporarily disabled due to database issues, sorry."); - // p.IncludeLastMessage = true; + p.IncludeLastMessage = true; if (ctx.MatchFlag("with-message-count", "wmc")) p.IncludeMessageCount = true; if (ctx.MatchFlag("with-created", "wc")) @@ -135,8 +134,8 @@ public static class ContextListExt ret += $"({count} messages)"; else if (opts.IncludeLastSwitch && m.MetadataPrivacy.TryGet(lookupCtx, m.LastSwitchTime, out var lastSw)) ret += $"(last switched in: )"; - // else if (opts.IncludeLastMessage && m.MetadataPrivacy.TryGet(lookupCtx, m.LastMessage, out var lastMsg)) - // ret += $"(last message: )"; + else if (opts.IncludeLastMessage && m.MetadataPrivacy.TryGet(lookupCtx, m.LastMessageTimestamp, out var lastMsg)) + ret += $"(last message: )"; else if (opts.IncludeCreated && m.MetadataPrivacy.TryGet(lookupCtx, m.Created, out var created)) ret += $"(created at )"; else if (opts.IncludeAvatar && m.AvatarFor(lookupCtx) is { } avatarUrl) @@ -181,8 +180,8 @@ public static class ContextListExt m.MessageCountFor(lookupCtx) is { } count && count > 0) profile.Append($"\n**Message count:** {count}"); - // if ((opts.IncludeLastMessage || opts.SortProperty == SortProperty.LastMessage) && m.MetadataPrivacy.TryGet(lookupCtx, m.LastMessage, out var lastMsg)) - // profile.Append($"\n**Last message:** {DiscordUtils.SnowflakeToInstant(lastMsg.Value).FormatZoned(zone)}"); + if ((opts.IncludeLastMessage || opts.SortProperty == SortProperty.LastMessage) && m.MetadataPrivacy.TryGet(lookupCtx, m.LastMessageTimestamp, out var lastMsg)) + profile.Append($"\n**Last message:** {m.LastMessageTimestamp.Value.FormatZoned(ctx.Zone)}"); if ((opts.IncludeLastSwitch || opts.SortProperty == SortProperty.LastSwitch) && m.MetadataPrivacy.TryGet(lookupCtx, m.LastSwitchTime, out var lastSw)) diff --git a/PluralKit.Bot/Commands/Lists/ListOptions.cs b/PluralKit.Bot/Commands/Lists/ListOptions.cs index 1835f17f..1e9393b6 100644 --- a/PluralKit.Bot/Commands/Lists/ListOptions.cs +++ b/PluralKit.Bot/Commands/Lists/ListOptions.cs @@ -130,11 +130,9 @@ public static class ListOptionsExt SortProperty.Birthdate => input .OrderByDescending(m => m.AnnualBirthday.HasValue && m.BirthdayPrivacy.CanAccess(ctx)) .ThenBy(m => m.BirthdayPrivacy.Get(ctx, m.AnnualBirthday), ReverseMaybe(Comparer.Default)), - SortProperty.LastMessage => throw new PKError( - "Sorting by last message is temporarily disabled due to database issues, sorry."), - // SortProperty.LastMessage => input - // .OrderByDescending(m => m.LastMessage.HasValue) - // .ThenByDescending(m => m.LastMessage, ReverseMaybe(Comparer.Default)), + SortProperty.LastMessage => input + .OrderByDescending(m => m.LastMessageTimestamp.HasValue) + .ThenByDescending(m => m.LastMessageTimestamp!.Value, ReverseMaybe(Comparer.Default)), SortProperty.LastSwitch => input .OrderByDescending(m => m.LastSwitchTime.HasValue && m.MetadataPrivacy.CanAccess(ctx)) .ThenByDescending(m => m.MetadataPrivacy.Get(ctx, m.LastSwitchTime), ReverseMaybe(Comparer.Default)), diff --git a/PluralKit.Bot/Proxy/ProxyService.cs b/PluralKit.Bot/Proxy/ProxyService.cs index acd665b6..dfb125d2 100644 --- a/PluralKit.Bot/Proxy/ProxyService.cs +++ b/PluralKit.Bot/Proxy/ProxyService.cs @@ -435,6 +435,9 @@ public class ProxyService await _redis.SetOriginalMid(triggerMessage.Id, proxyMessage.Id); } + Task UpdateMemberForSentMessage() + => _repo.UpdateMemberForSentMessage(sentMessage.Member!.Value); + Task LogMessageToChannel() => _logChannel.LogMessage(sentMessage, triggerMessage, proxyMessage).AsTask(); @@ -474,6 +477,7 @@ public class ProxyService DeleteProxyTriggerMessage(), SaveMessageInDatabase(), SaveMessageInRedis(), + UpdateMemberForSentMessage(), LogMessageToChannel(), SaveLatchAutoproxy(), DispatchWebhook() diff --git a/PluralKit.Core/Database/Migrations/32.sql b/PluralKit.Core/Database/Migrations/32.sql new file mode 100644 index 00000000..737d0420 --- /dev/null +++ b/PluralKit.Core/Database/Migrations/32.sql @@ -0,0 +1,6 @@ +-- database version 32 +-- re-add last message timestamp to members + +alter table members add column last_message_timestamp timestamp; + +update info set schema_version = 32; \ No newline at end of file diff --git a/PluralKit.Core/Database/Repository/ModelRepository.Member.cs b/PluralKit.Core/Database/Repository/ModelRepository.Member.cs index d676b11a..1b489f07 100644 --- a/PluralKit.Core/Database/Repository/ModelRepository.Member.cs +++ b/PluralKit.Core/Database/Repository/ModelRepository.Member.cs @@ -97,4 +97,16 @@ public partial class ModelRepository new { member = id, account = userId } ); } + + public async Task UpdateMemberForSentMessage(MemberId id) + { + var query = new Query("members") + .AsUpdate(new { + last_message_timestamp = new UnsafeLiteral("now()"), + message_count = new UnsafeLiteral("message_count + 1") + }) + .Where("id", id); + + await _db.ExecuteQuery(query); + } } \ No newline at end of file diff --git a/PluralKit.Core/Database/Utils/DatabaseMigrator.cs b/PluralKit.Core/Database/Utils/DatabaseMigrator.cs index d610e094..10183fdd 100644 --- a/PluralKit.Core/Database/Utils/DatabaseMigrator.cs +++ b/PluralKit.Core/Database/Utils/DatabaseMigrator.cs @@ -9,7 +9,7 @@ namespace PluralKit.Core; internal class DatabaseMigrator { private const string RootPath = "PluralKit.Core.Database"; // "resource path" root for SQL files - private const int TargetSchemaVersion = 31; + private const int TargetSchemaVersion = 32; private readonly ILogger _logger; public DatabaseMigrator(ILogger logger) diff --git a/PluralKit.Core/Database/Views/ListedMember.cs b/PluralKit.Core/Database/Views/ListedMember.cs index 7ca8f9f4..c3d8f49b 100644 --- a/PluralKit.Core/Database/Views/ListedMember.cs +++ b/PluralKit.Core/Database/Views/ListedMember.cs @@ -6,7 +6,6 @@ namespace PluralKit.Core; // TODO: is inheritance here correct? public class ListedMember: PKMember { - // public ulong? LastMessage { get; } public Instant? LastSwitchTime { get; } public AnnualDate? AnnualBirthday => diff --git a/PluralKit.Core/Database/Views/views.sql b/PluralKit.Core/Database/Views/views.sql index d2373b64..e23ebb6f 100644 --- a/PluralKit.Core/Database/Views/views.sql +++ b/PluralKit.Core/Database/Views/views.sql @@ -9,10 +9,6 @@ from systems create view member_list as select members.*, - -- Find last message ID - -- max(mid) does full table scan, order by/limit uses index (dunno why, but it works!) - -- (select mid from messages where messages.member = members.id order by mid desc nulls last limit 1) as last_message, - -- Find last switch timestamp ( select max(switches.timestamp) diff --git a/PluralKit.Core/Models/PKMember.cs b/PluralKit.Core/Models/PKMember.cs index f6030375..7afb2b52 100644 --- a/PluralKit.Core/Models/PKMember.cs +++ b/PluralKit.Core/Models/PKMember.cs @@ -50,6 +50,7 @@ public class PKMember public bool KeepProxy { get; private set; } public Instant Created { get; private set; } public int MessageCount { get; private set; } + public Instant? LastMessageTimestamp { get; } public bool AllowAutoproxy { get; private set; } public PrivacyLevel MemberVisibility { get; private set; }