From 05cc30279a15797df72f19050c54126e496fa457 Mon Sep 17 00:00:00 2001 From: dev-kittens Date: Sun, 18 Oct 2020 00:18:52 -0500 Subject: [PATCH 1/4] - Add storing bot response messages in postgres - Add scheduled task to clean up said store --- PluralKit.Bot/Bot.cs | 12 ++++++++++-- PluralKit.Bot/CommandSystem/Context.cs | 9 +++++++-- PluralKit.Bot/Commands/CommandTree.cs | 4 ++-- PluralKit.Core/Database/Database.cs | 2 +- PluralKit.Core/Database/Migrations/11.sql | 17 +++++++++++++++++ .../ModelRepository.CommandMessage.cs | 15 +++++++++++++++ 6 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 PluralKit.Core/Database/Migrations/11.sql create mode 100644 PluralKit.Core/Database/Repository/ModelRepository.CommandMessage.cs diff --git a/PluralKit.Bot/Bot.cs b/PluralKit.Bot/Bot.cs index b973f54a..5e87f3da 100644 --- a/PluralKit.Bot/Bot.cs +++ b/PluralKit.Bot/Bot.cs @@ -9,6 +9,8 @@ using App.Metrics; using Autofac; +using Dapper; + using DSharpPlus; using DSharpPlus.Entities; using DSharpPlus.EventArgs; @@ -34,18 +36,21 @@ namespace PluralKit.Bot private readonly PeriodicStatCollector _collector; private readonly IMetrics _metrics; private readonly ErrorMessageService _errorMessageService; + private readonly IDatabase _db; private bool _hasReceivedReady = false; private Timer _periodicTask; // Never read, just kept here for GC reasons - public Bot(DiscordShardedClient client, ILifetimeScope services, ILogger logger, PeriodicStatCollector collector, IMetrics metrics, ErrorMessageService errorMessageService) + public Bot(DiscordShardedClient client, ILifetimeScope services, ILogger logger, PeriodicStatCollector collector, IMetrics metrics, + ErrorMessageService errorMessageService, IDatabase db) { _client = client; + _logger = logger.ForContext(); _services = services; _collector = collector; _metrics = metrics; _errorMessageService = errorMessageService; - _logger = logger.ForContext(); + _db = db; } public void Init() @@ -177,6 +182,9 @@ namespace PluralKit.Bot await UpdateBotStatus(); + // Clean up message cache in postgres + await _db.Execute(conn => conn.QueryAsync("select from cleanup_command_message()")); + // Collect some stats, submit them to the metrics backend await _collector.CollectStats(); await Task.WhenAll(((IMetricsRoot) _metrics).ReportRunner.RunAllAsync()); diff --git a/PluralKit.Bot/CommandSystem/Context.cs b/PluralKit.Bot/CommandSystem/Context.cs index 1cff801b..1097cc18 100644 --- a/PluralKit.Bot/CommandSystem/Context.cs +++ b/PluralKit.Bot/CommandSystem/Context.cs @@ -64,7 +64,7 @@ namespace PluralKit.Bot internal IDatabase Database => _db; internal ModelRepository Repository => _repo; - public Task Reply(string text = null, DiscordEmbed embed = null, IEnumerable mentions = null) + public async Task Reply(string text = null, DiscordEmbed embed = null, IEnumerable mentions = null) { if (!this.BotHasAllPermissions(Permissions.SendMessages)) // Will be "swallowed" during the error handler anyway, this message is never shown. @@ -72,7 +72,12 @@ namespace PluralKit.Bot if (embed != null && !this.BotHasAllPermissions(Permissions.EmbedLinks)) throw new PKError("PluralKit does not have permission to send embeds in this channel. Please ensure I have the **Embed Links** permission enabled."); - return Channel.SendMessageFixedAsync(text, embed: embed, mentions: mentions); + var msg = await Channel.SendMessageFixedAsync(text, embed: embed, mentions: mentions); + if (embed != null) + // Sensitive information that might want to be deleted by :x: reaction is typically in an embed format (member cards, for example) + // This may need to be changed at some point but works well enough for now + await _db.Execute(conn => _repo.SaveCommandMessage(conn, msg.Id, Author.Id)); + return msg; } public async Task Execute(Command commandDef, Func handler) diff --git a/PluralKit.Bot/Commands/CommandTree.cs b/PluralKit.Bot/Commands/CommandTree.cs index 0b0491a2..b15e7777 100644 --- a/PluralKit.Bot/Commands/CommandTree.cs +++ b/PluralKit.Bot/Commands/CommandTree.cs @@ -189,9 +189,9 @@ namespace PluralKit.Bot if (ctx.Match("random", "r")) return ctx.Execute(MemberRandom, m => m.MemberRandom(ctx)); - ctx.Reply( + // remove compiler warning + return ctx.Reply( $"{Emojis.Error} Unknown command {ctx.PeekArgument().AsCode()}. For a list of possible commands, see ."); - return Task.CompletedTask; } private async Task HandleSystemCommand(Context ctx) diff --git a/PluralKit.Core/Database/Database.cs b/PluralKit.Core/Database/Database.cs index aea4f6fe..a0436a23 100644 --- a/PluralKit.Core/Database/Database.cs +++ b/PluralKit.Core/Database/Database.cs @@ -19,7 +19,7 @@ namespace PluralKit.Core internal class Database: IDatabase { private const string RootPath = "PluralKit.Core.Database"; // "resource path" root for SQL files - private const int TargetSchemaVersion = 10; + private const int TargetSchemaVersion = 11; private readonly CoreConfig _config; private readonly ILogger _logger; diff --git a/PluralKit.Core/Database/Migrations/11.sql b/PluralKit.Core/Database/Migrations/11.sql new file mode 100644 index 00000000..77764819 --- /dev/null +++ b/PluralKit.Core/Database/Migrations/11.sql @@ -0,0 +1,17 @@ +-- SCHEMA VERSION 11: (insert date) -- +-- Create command message table -- + +create table command_message +( + message_id bigint primary key, + invoker_id bigint not null, + timestamp timestamp not null default now() +); + +create function cleanup_command_message() returns void as $$ +begin + delete from command_message where timestamp < now() - interval '1 minute'; +end; +$$ language plpgsql; + +update info set schema_version = 11; \ No newline at end of file diff --git a/PluralKit.Core/Database/Repository/ModelRepository.CommandMessage.cs b/PluralKit.Core/Database/Repository/ModelRepository.CommandMessage.cs new file mode 100644 index 00000000..f10df683 --- /dev/null +++ b/PluralKit.Core/Database/Repository/ModelRepository.CommandMessage.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Data; +using System.Threading.Tasks; + +using Dapper; + +namespace PluralKit.Core +{ + public partial class ModelRepository + { + public Task SaveCommandMessage(IPKConnection conn, ulong message_id, ulong author_id) => + conn.QueryAsync("insert into command_message (message_id, invoker_id) values (@Message, @Author)", + new {Message = message_id, Author = author_id }); + } +} \ No newline at end of file From 56bb5e975f8dbc8bd79ba4ae27b3c399eb6dabd7 Mon Sep 17 00:00:00 2001 From: dev-kittens Date: Sun, 18 Oct 2020 00:59:36 -0500 Subject: [PATCH 2/4] Add handling command :x: reactions and, uhh, rename invoker_id to author_id --- PluralKit.Bot/Handlers/ReactionAdded.cs | 22 +++++++++++++++++++ PluralKit.Core/Database/Migrations/11.sql | 2 +- .../ModelRepository.CommandMessage.cs | 11 +++++++++- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/PluralKit.Bot/Handlers/ReactionAdded.cs b/PluralKit.Bot/Handlers/ReactionAdded.cs index 5abc5840..57b08dcb 100644 --- a/PluralKit.Bot/Handlers/ReactionAdded.cs +++ b/PluralKit.Bot/Handlers/ReactionAdded.cs @@ -48,12 +48,15 @@ namespace PluralKit.Bot _db.Execute(c => _repo.GetMessage(c, evt.Message.Id)); FullMessage msg; + CommandMessage cmdmsg; switch (evt.Emoji.Name) { // Message deletion case "\u274C": // Red X if ((msg = await GetMessage()) != null) await HandleDeleteReaction(evt, msg); + else if ((cmdmsg = await _db.Execute(conn => _repo.GetCommandMessage(conn, evt.Message.Id))) != null) + await HandleCommandDeleteReaction(evt, cmdmsg); break; case "\u2753": // Red question mark @@ -92,6 +95,25 @@ namespace PluralKit.Bot await _db.Execute(c => _repo.DeleteMessage(c, evt.Message.Id)); } + private async ValueTask HandleCommandDeleteReaction(MessageReactionAddEventArgs evt, CommandMessage msg) + { + if (!evt.Channel.BotHasAllPermissions(Permissions.ManageMessages)) return; + + // Can only delete your own message + if (msg.author_id != evt.User.Id) return; + + try + { + await evt.Message.DeleteAsync(); + } + catch (NotFoundException) + { + // Message was deleted by something/someone else before we got to it + } + + // No need to delete database row here, it'll get deleted by the once-per-minute scheduled task. + } + private async ValueTask HandleQueryReaction(MessageReactionAddEventArgs evt, FullMessage msg) { // Try to DM the user info about the message diff --git a/PluralKit.Core/Database/Migrations/11.sql b/PluralKit.Core/Database/Migrations/11.sql index 77764819..dda320af 100644 --- a/PluralKit.Core/Database/Migrations/11.sql +++ b/PluralKit.Core/Database/Migrations/11.sql @@ -4,7 +4,7 @@ create table command_message ( message_id bigint primary key, - invoker_id bigint not null, + author_id bigint not null, timestamp timestamp not null default now() ); diff --git a/PluralKit.Core/Database/Repository/ModelRepository.CommandMessage.cs b/PluralKit.Core/Database/Repository/ModelRepository.CommandMessage.cs index f10df683..1e38c447 100644 --- a/PluralKit.Core/Database/Repository/ModelRepository.CommandMessage.cs +++ b/PluralKit.Core/Database/Repository/ModelRepository.CommandMessage.cs @@ -9,7 +9,16 @@ namespace PluralKit.Core public partial class ModelRepository { public Task SaveCommandMessage(IPKConnection conn, ulong message_id, ulong author_id) => - conn.QueryAsync("insert into command_message (message_id, invoker_id) values (@Message, @Author)", + conn.QueryAsync("insert into command_message (message_id, author_id) values (@Message, @Author)", new {Message = message_id, Author = author_id }); + + public Task GetCommandMessage(IPKConnection conn, ulong message_id) => + conn.QuerySingleOrDefaultAsync("select message_id, author_id from command_message where message_id = @Message", + new {Message = message_id}); + } + + public class CommandMessage + { + public ulong author_id { get; set; } } } \ No newline at end of file From ece7a523bab66e7333753dc001fb39a7040823e2 Mon Sep 17 00:00:00 2001 From: spiral Date: Sun, 18 Oct 2020 01:06:20 -0500 Subject: [PATCH 3/4] whoops --- PluralKit.Core/Database/Migrations/11.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PluralKit.Core/Database/Migrations/11.sql b/PluralKit.Core/Database/Migrations/11.sql index dda320af..360057d3 100644 --- a/PluralKit.Core/Database/Migrations/11.sql +++ b/PluralKit.Core/Database/Migrations/11.sql @@ -10,8 +10,8 @@ create table command_message create function cleanup_command_message() returns void as $$ begin - delete from command_message where timestamp < now() - interval '1 minute'; + delete from command_message where timestamp < now() - interval '1 hour'; end; $$ language plpgsql; -update info set schema_version = 11; \ No newline at end of file +update info set schema_version = 11; From 9da023e97a835cbd057d4f6691770a4d327a216a Mon Sep 17 00:00:00 2001 From: spiral Date: Mon, 19 Oct 2020 03:57:43 -0500 Subject: [PATCH 4/4] change command message timeout to 2 hours --- PluralKit.Core/Database/Migrations/11.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PluralKit.Core/Database/Migrations/11.sql b/PluralKit.Core/Database/Migrations/11.sql index 360057d3..bfed37e1 100644 --- a/PluralKit.Core/Database/Migrations/11.sql +++ b/PluralKit.Core/Database/Migrations/11.sql @@ -10,7 +10,7 @@ create table command_message create function cleanup_command_message() returns void as $$ begin - delete from command_message where timestamp < now() - interval '1 hour'; + delete from command_message where timestamp < now() - interval '2 hours'; end; $$ language plpgsql;