From 67a4a7795c979e2f59fa33aadb0ad0b2671aaefb Mon Sep 17 00:00:00 2001 From: FloatingMilkshake Date: Fri, 25 Oct 2024 15:32:55 -0400 Subject: [PATCH 1/4] Add 'channels' option to /tracking add Allows filtering tracking to specific channels --- .../TrackingInteractions.cs | 76 ++++++++++++++++++- Events/MessageEvent.cs | 45 ++++++++--- 2 files changed, 111 insertions(+), 10 deletions(-) diff --git a/Commands/InteractionCommands/TrackingInteractions.cs b/Commands/InteractionCommands/TrackingInteractions.cs index a2e55d2..e3d52e0 100644 --- a/Commands/InteractionCommands/TrackingInteractions.cs +++ b/Commands/InteractionCommands/TrackingInteractions.cs @@ -7,12 +7,85 @@ internal class TrackingInteractions : ApplicationCommandModule public class TrackingSlashCommands { [SlashCommand("add", "Track a users messages.")] - public async Task TrackingAddSlashCmd(InteractionContext ctx, [Option("member", "The member to track.")] DiscordUser discordUser) + public async Task TrackingAddSlashCmd(InteractionContext ctx, [Option("member", "The member to track.")] DiscordUser discordUser, [Option("channels", "Optional channels to filter to. Use IDs or mentions, and separate with commas or spaces.")] string channels = "") { await ctx.DeferAsync(ephemeral: false); + + var channelsUpdated = false; + + // Resolve list of filter channels + List filterChannels = new(); + if (!string.IsNullOrEmpty(channels)) + { + channels = channels.Replace(", ", ","); // "#general-chat, #lounge" ~> "#general-chat,#lounge" + var channelIds = channels.Split(' ', ','); + foreach (var channel in channelIds) + { + // If this is a channel mention, get the ID first + var channelId = channel.Replace("<#", "").Replace(">", ""); + + if (ulong.TryParse(channelId, out var id)) + { + if (!filterChannels.Contains(id)) + filterChannels.Add(id); + } + else + { + // Invalid ID; couldn't parse as ulong + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent($"{Program.cfgjson.Emoji.Error} I couldn't parse \"{channel}\" as a channel ID or mention! Please double-check it and try again.")); + return; + } + } + } + + // If we were passed nothing, filterChannels remains an empty List. Otherwise, it is populated with the parsed channel IDs + + // Compare to db; if there is a mismatch, replace whatever is already in the db with what was passed to this command + if (Program.db.HashExists("trackingChannels", discordUser.Id)) + { + var dbChannels = Program.db.HashGet("trackingChannels", discordUser.Id).ToString(); + string cmdChannels; + if (filterChannels.Count < 1) + { + // No channels were passed. If there are any in the db, remove them + if (await Program.db.HashExistsAsync("trackingChannels", discordUser.Id)) + { + await Program.db.HashDeleteAsync("trackingChannels", discordUser.Id); + channelsUpdated = true; + } + } + else + { + cmdChannels = JsonConvert.SerializeObject(filterChannels); + if (dbChannels != cmdChannels) + { + // Passed channels do not match db channels, update db + var newChannels = JsonConvert.SerializeObject(filterChannels); + await Program.db.HashSetAsync("trackingChannels", discordUser.Id, newChannels); + channelsUpdated = true; + } + } + } + else + { + // No channels in db; just add whatever was passed + // If nothing was passed, don't add anything + if (filterChannels.Count > 0) + { + var newChannels = JsonConvert.SerializeObject(filterChannels); + await Program.db.HashSetAsync("trackingChannels", discordUser.Id, newChannels); + channelsUpdated = true; + } + } if (Program.db.SetContains("trackedUsers", discordUser.Id)) { + if (channelsUpdated) + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent($"{Program.cfgjson.Emoji.Success} Successfully updated tracking for {discordUser.Mention}!")); + return; + } + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent($"{Program.cfgjson.Emoji.Error} This user is already tracked!")); return; } @@ -51,6 +124,7 @@ public async Task TrackingRemoveSlashCmd(InteractionContext ctx, [Option("member } await Program.db.SetRemoveAsync("trackedUsers", discordUser.Id); + await Program.db.HashDeleteAsync("trackingChannels", discordUser.Id); var channelId = Program.db.HashGet("trackingThreads", discordUser.Id); DiscordThreadChannel thread = (DiscordThreadChannel)await ctx.Client.GetChannelAsync((ulong)channelId); diff --git a/Events/MessageEvent.cs b/Events/MessageEvent.cs index 0fb3215..6741c9b 100644 --- a/Events/MessageEvent.cs +++ b/Events/MessageEvent.cs @@ -142,19 +142,23 @@ public static async Task MessageHandlerAsync(DiscordClient client, MockDiscordMe { if (Program.db.SetContains("trackedUsers", message.Author.Id)) { - DiscordThreadChannel relayThread; - - if (trackingThreadCache.ContainsKey(message.Author.Id)) - { - relayThread = trackingThreadCache[message.Author.Id]; + // Check current channel against tracking channels + var trackingChannels = await Program.db.HashGetAsync("trackingChannels", message.Author.Id); + var trackingChannelsList = JsonConvert.DeserializeObject>(trackingChannels); + if (trackingChannelsList.Count > 0) + { + // This user's tracking is filtered to channels; check the channel before relaying the msg to the tracking thread + var channels = JsonConvert.DeserializeObject>(trackingChannels); + if (channels.Contains(channel.Id)) + { + await RelayTrackedMessageAsync(client, message); + } } else { - relayThread = (DiscordThreadChannel)await client.GetChannelAsync((ulong)await Program.db.HashGetAsync("trackingThreads", message.Author.Id)); - trackingThreadCache.Add(message.Author.Id, relayThread); + // This user's tracking is not filtered to channels, so just relay the msg to the tracking thread + await RelayTrackedMessageAsync(client, message); } - var _ = await relayThread.SendMessageAsync(await DiscordHelpers.GenerateMessageRelay(message.BaseMessage, true, true)); - } if (!isAnEdit && channel.IsPrivate && Program.cfgjson.LogChannels.ContainsKey("dms")) @@ -935,6 +939,29 @@ public static async Task InviteCheck(DiscordInvite? invite, MockDiscordMes return false; } } + + private static async Task RelayTrackedMessageAsync(DiscordClient client, DiscordMessage message) + { + await RelayTrackedMessageAsync(client, new MockDiscordMessage(message)); + } + private static async Task RelayTrackedMessageAsync(DiscordClient client, MockDiscordMessage message) + { + DiscordThreadChannel relayThread; + + if (trackingThreadCache.ContainsKey(message.Author.Id)) + { + relayThread = trackingThreadCache[message.Author.Id]; + } + else + { + relayThread = (DiscordThreadChannel)await client.GetChannelAsync( + (ulong)await Program.db.HashGetAsync("trackingThreads", message.Author.Id)); + trackingThreadCache.Add(message.Author.Id, relayThread); + } + + var _ = await relayThread.SendMessageAsync( + await DiscordHelpers.GenerateMessageRelay(message.BaseMessage, true, true)); + } } } From 528a6d9b6141786306c4bd75811429236ebf74e3 Mon Sep 17 00:00:00 2001 From: FloatingMilkshake Date: Fri, 25 Oct 2024 15:43:49 -0400 Subject: [PATCH 2/4] Account for threads --- Events/MessageEvent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Events/MessageEvent.cs b/Events/MessageEvent.cs index 6741c9b..02d8151 100644 --- a/Events/MessageEvent.cs +++ b/Events/MessageEvent.cs @@ -149,7 +149,7 @@ public static async Task MessageHandlerAsync(DiscordClient client, MockDiscordMe { // This user's tracking is filtered to channels; check the channel before relaying the msg to the tracking thread var channels = JsonConvert.DeserializeObject>(trackingChannels); - if (channels.Contains(channel.Id)) + if (channels.Contains(channel.Id) || channels.Contains(channel.Parent.Id)) { await RelayTrackedMessageAsync(client, message); } From df44f1f11003d2e1d327c379ab2fda7f3852fac3 Mon Sep 17 00:00:00 2001 From: FloatingMilkshake Date: Fri, 25 Oct 2024 16:11:38 -0400 Subject: [PATCH 3/4] Use regex to match any number of spaces when parsing channel list & trim --- Commands/InteractionCommands/TrackingInteractions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Commands/InteractionCommands/TrackingInteractions.cs b/Commands/InteractionCommands/TrackingInteractions.cs index e3d52e0..b00f9a7 100644 --- a/Commands/InteractionCommands/TrackingInteractions.cs +++ b/Commands/InteractionCommands/TrackingInteractions.cs @@ -17,7 +17,7 @@ public async Task TrackingAddSlashCmd(InteractionContext ctx, [Option("member", List filterChannels = new(); if (!string.IsNullOrEmpty(channels)) { - channels = channels.Replace(", ", ","); // "#general-chat, #lounge" ~> "#general-chat,#lounge" + channels = Regex.Replace(channels, ", +", ",").Trim(); // "#general-chat, #lounge" ~> "#general-chat,#lounge" & trim var channelIds = channels.Split(' ', ','); foreach (var channel in channelIds) { From d64550572049108f070d535cc4c71a22431e25df Mon Sep 17 00:00:00 2001 From: FloatingMilkshake Date: Sat, 16 Nov 2024 14:06:55 -0500 Subject: [PATCH 4/4] Account for missing "trackingChannels" key in db --- Events/MessageEvent.cs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/Events/MessageEvent.cs b/Events/MessageEvent.cs index 209e473..e39c843 100644 --- a/Events/MessageEvent.cs +++ b/Events/MessageEvent.cs @@ -144,21 +144,24 @@ public static async Task MessageHandlerAsync(DiscordClient client, MockDiscordMe { // Check current channel against tracking channels var trackingChannels = await Program.db.HashGetAsync("trackingChannels", message.Author.Id); - var trackingChannelsList = JsonConvert.DeserializeObject>(trackingChannels); - if (trackingChannelsList.Count > 0) + if (trackingChannels.HasValue) { - // This user's tracking is filtered to channels; check the channel before relaying the msg to the tracking thread - var channels = JsonConvert.DeserializeObject>(trackingChannels); - if (channels.Contains(channel.Id) || channels.Contains(channel.Parent.Id)) + var trackingChannelsList = JsonConvert.DeserializeObject>(trackingChannels); + if (trackingChannelsList.Count > 0) { + // This user's tracking is filtered to channels; check the channel before relaying the msg to the tracking thread + var channels = JsonConvert.DeserializeObject>(trackingChannels); + if (channels.Contains(channel.Id) || channels.Contains(channel.Parent.Id)) + { + await RelayTrackedMessageAsync(client, message); + } + } + else + { + // This user's tracking is not filtered to channels, so just relay the msg to the tracking thread await RelayTrackedMessageAsync(client, message); } } - else - { - // This user's tracking is not filtered to channels, so just relay the msg to the tracking thread - await RelayTrackedMessageAsync(client, message); - } } if (!isAnEdit && channel.IsPrivate && Program.cfgjson.LogChannels.ContainsKey("dms"))