From 38126209e9d060147b05e96efef39e59d20c98bc Mon Sep 17 00:00:00 2001 From: Duncan Casteleyn <10881109+DuncanCasteleyn@users.noreply.github.com> Date: Sun, 16 Aug 2026 15:03:25 +0200 Subject: [PATCH] fix(logging): log stickers in deleted messages --- .../discordmodbot/logging/GuildLogger.kt | 6 +++++ .../discordmodbot/logging/MessageHistory.kt | 18 +++++++++++-- .../logging/persistence/DiscordMessage.kt | 3 ++- .../discordmodbot/logging/GuildLoggerTest.kt | 9 +++++-- .../logging/MessageHistoryTest.kt | 26 ++++++++++++++++++- 5 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/be/duncanc/discordmodbot/logging/GuildLogger.kt b/src/main/kotlin/be/duncanc/discordmodbot/logging/GuildLogger.kt index 5d171988..108fcd89 100644 --- a/src/main/kotlin/be/duncanc/discordmodbot/logging/GuildLogger.kt +++ b/src/main/kotlin/be/duncanc/discordmodbot/logging/GuildLogger.kt @@ -238,6 +238,9 @@ class GuildLogger oldMessage.emotes?.let { logEmbed.addField("Emote(s)", oldMessage.emotes, false) } + oldMessage.stickers?.let { + logEmbed.addField("Sticker(s)", it, false) + } log( logEmbed, user, @@ -354,6 +357,9 @@ class GuildLogger message.emotes?.let { entry.append("Emote(s):\n").append(it) } + message.stickers?.let { + entry.append("Sticker(s):\n").append(it) + } entries[index] = entry.toString() if (remainingLookups.decrementAndGet() == 0) { diff --git a/src/main/kotlin/be/duncanc/discordmodbot/logging/MessageHistory.kt b/src/main/kotlin/be/duncanc/discordmodbot/logging/MessageHistory.kt index 965eca14..07cae1df 100644 --- a/src/main/kotlin/be/duncanc/discordmodbot/logging/MessageHistory.kt +++ b/src/main/kotlin/be/duncanc/discordmodbot/logging/MessageHistory.kt @@ -3,6 +3,7 @@ package be.duncanc.discordmodbot.logging import be.duncanc.discordmodbot.logging.persistence.DiscordMessage import be.duncanc.discordmodbot.logging.persistence.DiscordMessageRepository import net.dv8tion.jda.api.entities.emoji.CustomEmoji +import net.dv8tion.jda.api.entities.sticker.StickerItem import net.dv8tion.jda.api.entities.MessageReference.MessageReferenceType import net.dv8tion.jda.api.events.message.MessageReceivedEvent import net.dv8tion.jda.api.events.message.MessageUpdateEvent @@ -53,7 +54,8 @@ constructor( }?.let { messageId -> "https://discord.com/channels/${reference.guildIdLong}/${reference.channelIdLong}/$messageId" } - } + }, + linkStickers(message.stickers) ) discordMessageRepository.save(discordMessage) if (message.attachments.size > 0) { @@ -81,7 +83,8 @@ constructor( message.author.idLong, messageContentEncryptor.encrypt(message.contentDisplay), existingMessage?.emotes, - existingMessage?.repliedToUrl + existingMessage?.repliedToUrl, + existingMessage?.stickers ) discordMessageRepository.save(discordMessage) } @@ -116,4 +119,15 @@ constructor( } return stringBuilder.toString() } + + private fun linkStickers(stickers: List): String? { + if (stickers.isEmpty()) { + return null + } + val stringBuilder = StringBuilder() + stickers.forEach { + stringBuilder.append("[" + it.name + "](" + it.iconUrl + ")\n") + } + return stringBuilder.toString() + } } diff --git a/src/main/kotlin/be/duncanc/discordmodbot/logging/persistence/DiscordMessage.kt b/src/main/kotlin/be/duncanc/discordmodbot/logging/persistence/DiscordMessage.kt index f0271242..ddf5f02d 100644 --- a/src/main/kotlin/be/duncanc/discordmodbot/logging/persistence/DiscordMessage.kt +++ b/src/main/kotlin/be/duncanc/discordmodbot/logging/persistence/DiscordMessage.kt @@ -14,7 +14,8 @@ data class DiscordMessage( val userId: Long, val content: String, val emotes: String? = null, - val repliedToUrl: String? = null + val repliedToUrl: String? = null, + val stickers: String? = null ) { @Transient val jumpUrl = "https://discord.com/channels/$guildId/$channelId/$messageId" diff --git a/src/test/kotlin/be/duncanc/discordmodbot/logging/GuildLoggerTest.kt b/src/test/kotlin/be/duncanc/discordmodbot/logging/GuildLoggerTest.kt index 73d9e5d3..34f57e05 100644 --- a/src/test/kotlin/be/duncanc/discordmodbot/logging/GuildLoggerTest.kt +++ b/src/test/kotlin/be/duncanc/discordmodbot/logging/GuildLoggerTest.kt @@ -178,7 +178,8 @@ class GuildLoggerTest { channelId = 10L, userId = 20L, content = "deleted content", - repliedToUrl = "https://discord.com/channels/1/10/50" + repliedToUrl = "https://discord.com/channels/1/10/50", + stickers = "[wave](https://cdn.discordapp.com/sticker.png)" ) whenever(event.guild).thenReturn(guild) whenever(event.channel).thenReturn(channel) @@ -209,6 +210,8 @@ class GuildLoggerTest { verify(logChannel).sendMessage(messageCaptor.capture()) val repliedToField = messageCaptor.firstValue.embeds.single().fields.first { it.name == "Replied to" } assertEquals("[Link](https://discord.com/channels/1/10/50)", repliedToField.value) + val stickerField = messageCaptor.firstValue.embeds.single().fields.first { it.name == "Sticker(s)" } + assertEquals("[wave](https://cdn.discordapp.com/sticker.png)", stickerField.value) } @Test @@ -220,7 +223,8 @@ class GuildLoggerTest { channelId = 10L, userId = 20L, content = "deleted content", - repliedToUrl = "https://discord.com/channels/1/10/50" + repliedToUrl = "https://discord.com/channels/1/10/50", + stickers = "[wave](https://cdn.discordapp.com/sticker.png)" ) whenever(bulkDeleteEvent.guild).thenReturn(guild) whenever(bulkDeleteEvent.channel).thenReturn(bulkChannel) @@ -247,6 +251,7 @@ class GuildLoggerTest { verify(logChannel, timeout(1000)).sendFiles(fileCaptor.capture()) val logContent = fileCaptor.firstValue.data.use { it.readBytes().toString(Charsets.UTF_8) } assertTrue(logContent.contains("Replied to:\nhttps://discord.com/channels/1/10/50")) + assertTrue(logContent.contains("Sticker(s):\n[wave](https://cdn.discordapp.com/sticker.png)")) } @Test diff --git a/src/test/kotlin/be/duncanc/discordmodbot/logging/MessageHistoryTest.kt b/src/test/kotlin/be/duncanc/discordmodbot/logging/MessageHistoryTest.kt index 871000c1..1d0f77f0 100644 --- a/src/test/kotlin/be/duncanc/discordmodbot/logging/MessageHistoryTest.kt +++ b/src/test/kotlin/be/duncanc/discordmodbot/logging/MessageHistoryTest.kt @@ -8,6 +8,7 @@ import net.dv8tion.jda.api.entities.MessageReference import net.dv8tion.jda.api.entities.Mentions import net.dv8tion.jda.api.entities.User import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion +import net.dv8tion.jda.api.entities.sticker.StickerItem import net.dv8tion.jda.api.events.message.MessageReceivedEvent import net.dv8tion.jda.api.events.message.MessageUpdateEvent import org.junit.jupiter.api.Assertions.assertEquals @@ -56,6 +57,9 @@ class MessageHistoryTest { @Mock private lateinit var messageReference: MessageReference + @Mock + private lateinit var sticker: StickerItem + private lateinit var messageContentEncryptor: MessageContentEncryptor private lateinit var messageHistory: MessageHistory @@ -101,6 +105,23 @@ class MessageHistoryTest { ) } + @Test + fun `store message preserves stickers`() { + stubReceivedMessage(content = "sticker") + whenever(message.stickers).thenReturn(listOf(sticker)) + whenever(sticker.name).thenReturn("wave") + whenever(sticker.iconUrl).thenReturn("https://cdn.discordapp.com/sticker.png") + + messageHistory.storeMessage(receivedEvent) + + val messageCaptor = argumentCaptor() + verify(discordMessageRepository).save(messageCaptor.capture()) + assertEquals( + "[wave](https://cdn.discordapp.com/sticker.png)\n", + messageCaptor.firstValue.stickers + ) + } + @Test fun `update message encrypts content before saving`() { stubUpdatedMessage(content = "updated content") @@ -114,7 +135,8 @@ class MessageHistoryTest { userId = 20L, content = messageContentEncryptor.encrypt("old content"), emotes = "[wave](https://cdn.discordapp.com/emote.png)", - repliedToUrl = "https://discord.com/channels/1/10/50" + repliedToUrl = "https://discord.com/channels/1/10/50", + stickers = "[sticker](https://cdn.discordapp.com/sticker.png)" ) ) ) @@ -127,6 +149,7 @@ class MessageHistoryTest { assertEquals("updated content", messageContentEncryptor.decrypt(messageCaptor.firstValue.content)) assertEquals("[wave](https://cdn.discordapp.com/emote.png)", messageCaptor.firstValue.emotes) assertEquals("https://discord.com/channels/1/10/50", messageCaptor.firstValue.repliedToUrl) + assertEquals("[sticker](https://cdn.discordapp.com/sticker.png)", messageCaptor.firstValue.stickers) } @Test @@ -201,6 +224,7 @@ class MessageHistoryTest { if (includeMentions) { whenever(message.mentions).thenReturn(mentions) whenever(mentions.customEmojis).thenReturn(emptyList()) + whenever(message.stickers).thenReturn(emptyList()) } if (includeAttachments) { whenever(message.attachments).thenReturn(emptyList())