From dc513ac2f0857084f4618688bd6dd5ba396fe869 Mon Sep 17 00:00:00 2001 From: akastijn Date: Sat, 18 Jul 2026 19:11:53 +0200 Subject: [PATCH] Add support for `miniMessage` serialization in chat logs --- .../alttd/chat/database/ChatLogQueries.java | 38 ++++++---- .../alttd/chat/objects/chat_log/ChatLog.java | 11 ++- .../chat/objects/chat_log/ChatLogHandler.java | 35 ++++++--- .../alttd/chat/listeners/ChatListener.java | 72 +++++++++++-------- 4 files changed, 102 insertions(+), 54 deletions(-) diff --git a/api/src/main/java/com/alttd/chat/database/ChatLogQueries.java b/api/src/main/java/com/alttd/chat/database/ChatLogQueries.java index 1554450..f1ab580 100644 --- a/api/src/main/java/com/alttd/chat/database/ChatLogQueries.java +++ b/api/src/main/java/com/alttd/chat/database/ChatLogQueries.java @@ -15,15 +15,22 @@ import java.util.concurrent.CompletionException; public class ChatLogQueries { protected static void createChatLogTable() { - String nicknamesTableQuery = "CREATE TABLE IF NOT EXISTS chat_log(" - + "uuid CHAR(48) NOT NULL," - + "time_stamp TIMESTAMP(6) NOT NULL, " - + "server VARCHAR(50) NOT NULL, " - + "chat_message VARCHAR(300) NOT NULL, " - + "blocked BIT(1) NOT NULL DEFAULT 0" - + ")"; + String nicknamesTableQuery = """ + CREATE TABLE chat_log ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + uuid CHAR(36) NOT NULL, + time_stamp TIMESTAMP(6) NOT NULL, + server VARCHAR(50) NOT NULL, + chat_message VARCHAR(300) NOT NULL, + mini_message JSON, + blocked BOOLEAN NOT NULL DEFAULT FALSE, + + INDEX idx_time_stamp (time_stamp) + ) + """; - try (PreparedStatement preparedStatement = DatabaseConnection.getConnection().prepareStatement(nicknamesTableQuery)) { + try (PreparedStatement preparedStatement = DatabaseConnection.getConnection().prepareStatement( + nicknamesTableQuery)) { preparedStatement.executeUpdate(); } catch (Throwable throwable) { ALogger.error("Failed to create chat log table", throwable); @@ -31,7 +38,7 @@ public class ChatLogQueries { } public static @NotNull CompletableFuture storeMessages(HashMap> chatMessages) { - String insertQuery = "INSERT INTO chat_log (uuid, time_stamp, server, chat_message, blocked) VALUES (?, ?, ?, ?, ?)"; + String insertQuery = "INSERT INTO chat_log (uuid, time_stamp, server, chat_message, mini_message, blocked) VALUES (?, ?, ?, ?, ?, ?)"; return CompletableFuture.supplyAsync(() -> { try (Connection connection = DatabaseConnection.createTransactionConnection()) { PreparedStatement preparedStatement = connection.prepareStatement(insertQuery); @@ -76,7 +83,9 @@ public class ChatLogQueries { return chatLogs; } catch (SQLException sqlException) { ALogger.error(String.format("Failed to retrieve messages for user %s", uuid), sqlException); - throw new CompletionException(String.format("Failed to retrieve messages for user %s", uuid), sqlException); + throw new CompletionException(String.format("Failed to retrieve messages for user %s", uuid), + sqlException + ); } }); } @@ -90,8 +99,13 @@ public class ChatLogQueries { preparedStatement.setTimestamp(1, Timestamp.from(Instant.now().minus(duration))); return preparedStatement.execute(); } catch (SQLException sqlException) { - ALogger.error(String.format("Failed to delete messages older than %s days", duration.toDays()), sqlException); - throw new CompletionException(String.format("Failed to delete messages older than %s days", duration.toDays()), sqlException); + ALogger.error(String.format("Failed to delete messages older than %s days", duration.toDays()), + sqlException + ); + throw new CompletionException(String.format("Failed to delete messages older than %s days", + duration.toDays() + ), sqlException + ); } }); } diff --git a/api/src/main/java/com/alttd/chat/objects/chat_log/ChatLog.java b/api/src/main/java/com/alttd/chat/objects/chat_log/ChatLog.java index 8015b91..6fd44d0 100644 --- a/api/src/main/java/com/alttd/chat/objects/chat_log/ChatLog.java +++ b/api/src/main/java/com/alttd/chat/objects/chat_log/ChatLog.java @@ -1,6 +1,8 @@ package com.alttd.chat.objects.chat_log; import com.alttd.chat.objects.BatchInsertable; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import org.jetbrains.annotations.NotNull; import java.sql.PreparedStatement; @@ -15,13 +17,15 @@ public class ChatLog implements BatchInsertable { private final Instant timestamp; private final String server; private final String message; + private final Component miniMessage; private final boolean blocked; - protected ChatLog(UUID uuid, Instant timestamp, String server, String message, boolean blocked) { + protected ChatLog(UUID uuid, Instant timestamp, String server, String message, Component miniMessage, boolean blocked) { this.uuid = uuid; this.timestamp = timestamp; this.server = server; this.message = message; + this.miniMessage = miniMessage; this.blocked = blocked; } @@ -31,7 +35,10 @@ public class ChatLog implements BatchInsertable { preparedStatement.setTimestamp(2, Timestamp.from(timestamp)); preparedStatement.setString(3, server); preparedStatement.setString(4, message); - preparedStatement.setInt(5, blocked ? 1 : 0); + preparedStatement.setString(5, + miniMessage == null ? null : GsonComponentSerializer.gson().serialize(miniMessage) + ); + preparedStatement.setInt(6, blocked ? 1 : 0); } public UUID getUuid() { diff --git a/api/src/main/java/com/alttd/chat/objects/chat_log/ChatLogHandler.java b/api/src/main/java/com/alttd/chat/objects/chat_log/ChatLogHandler.java index a3fee53..55281bf 100644 --- a/api/src/main/java/com/alttd/chat/objects/chat_log/ChatLogHandler.java +++ b/api/src/main/java/com/alttd/chat/objects/chat_log/ChatLogHandler.java @@ -3,6 +3,8 @@ package com.alttd.chat.objects.chat_log; import com.alttd.chat.config.Config; import com.alttd.chat.database.ChatLogQueries; import com.alttd.chat.util.ALogger; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import org.jetbrains.annotations.NotNull; import java.sql.ResultSet; @@ -18,8 +20,9 @@ public class ChatLogHandler { private ScheduledExecutorService executorService = null; public static ChatLogHandler getInstance(boolean enableLogging) { - if (instance == null) + if (instance == null) { instance = new ChatLogHandler(enableLogging); + } return instance; } @@ -35,23 +38,30 @@ public class ChatLogHandler { Duration deleteThreshold = Duration.ofDays(Config.CHAT_LOG_DELETE_OLDER_THAN_DAYS); ChatLogQueries.deleteOldMessages(deleteThreshold).thenAccept(success -> { if (success) { - ALogger.info(String.format("Deleted all messages older than %s days from chat log database.", deleteThreshold.toDays())); + ALogger.info(String.format("Deleted all messages older than %s days from chat log database.", + deleteThreshold.toDays() + )); } else { - ALogger.warn(String.format("Failed to delete all messages older than %s days from chat log database.", deleteThreshold.toDays())); + ALogger.warn(String.format("Failed to delete all messages older than %s days from chat log database.", + deleteThreshold.toDays() + )); } }); executorService = Executors.newSingleThreadScheduledExecutor(); executorService.scheduleAtFixedRate(() -> { saveToDatabase(false); - ALogger.info(String.format("Running scheduler to save messages with a %d delay", Config.CHAT_LOG_SAVE_DELAY_MINUTES)); + ALogger.info(String.format("Running scheduler to save messages with a %d delay", + Config.CHAT_LOG_SAVE_DELAY_MINUTES + )); }, - Config.CHAT_LOG_SAVE_DELAY_MINUTES, Config.CHAT_LOG_SAVE_DELAY_MINUTES, TimeUnit.MINUTES); + Config.CHAT_LOG_SAVE_DELAY_MINUTES, Config.CHAT_LOG_SAVE_DELAY_MINUTES, TimeUnit.MINUTES + ); ALogger.info("Logging has started!"); } /** - * Shuts down the executor service and saves the chat logs to the database. - * Will throw an error if called on a ChatLogHandler that was started without logging + * Shuts down the executor service and saves the chat logs to the database. Will throw an error if called on a + * ChatLogHandler that was started without logging */ public void shutDown() { executorService.shutdown(); @@ -66,7 +76,7 @@ public class ChatLogHandler { return isSaving; } - public synchronized void addLog(ChatLog chatLog) { + private synchronized void addLog(ChatLog chatLog) { if (isBlocked()) { chatLogQueue.add(chatLog); } else { @@ -105,12 +115,15 @@ public class ChatLogHandler { Instant chatTimestamp = resultSet.getTimestamp("time_stamp").toInstant(); String server = resultSet.getString("server"); String chatMessage = resultSet.getString("chat_message"); + String stringMiniMessage = resultSet.getString("mini_message"); + Component miniMessage = stringMiniMessage == null ? null : GsonComponentSerializer.gson().deserialize( + stringMiniMessage); boolean chatMessageBlocked = resultSet.getInt("blocked") == 1; - return new ChatLog(chatLogUUID, chatTimestamp, server, chatMessage, chatMessageBlocked); + return new ChatLog(chatLogUUID, chatTimestamp, server, chatMessage, miniMessage, chatMessageBlocked); } - public void addChatLog(UUID uuid, String server, String message, boolean blocked) { - addLog(new ChatLog(uuid, Instant.now(), server, message, blocked)); + public void addChatLog(UUID uuid, String server, String message, Component miniMessage, boolean blocked) { + addLog(new ChatLog(uuid, Instant.now(), server, message, miniMessage, blocked)); } public CompletableFuture> retrieveChatLogs(UUID uuid, Duration duration, String server) { diff --git a/galaxy/src/main/java/com/alttd/chat/listeners/ChatListener.java b/galaxy/src/main/java/com/alttd/chat/listeners/ChatListener.java index 34b2abe..db42128 100755 --- a/galaxy/src/main/java/com/alttd/chat/listeners/ChatListener.java +++ b/galaxy/src/main/java/com/alttd/chat/listeners/ChatListener.java @@ -55,7 +55,9 @@ public class ChatListener implements Listener { } Component formatComponent = Component.text("%message%"); - ComponentLike message = parseMessageContent(event.player(), plainTextComponentSerializer.serialize(event.originalMessage())); + ComponentLike message = parseMessageContent(event.player(), + plainTextComponentSerializer.serialize(event.originalMessage()) + ); event.result(formatComponent.replaceText(TextReplacementConfig.builder().match("%message%").replacement(message).build())); } @@ -67,7 +69,9 @@ public class ChatListener implements Listener { } Component formatComponent = Component.text("%message%"); - ComponentLike message = parseMessageContent(event.player(), plainTextComponentSerializer.serialize(event.originalMessage())); + ComponentLike message = parseMessageContent(event.player(), + plainTextComponentSerializer.serialize(event.originalMessage()) + ); event.result(formatComponent.replaceText(TextReplacementConfig.builder().match("%message%").replacement(message).build())); } @@ -92,27 +96,31 @@ public class ChatListener implements Listener { UUID uuid = player.getUniqueId(); ComponentLike input = event.message().colorIfAbsent(NamedTextColor.WHITE); + Component inputComponent = input.asComponent(); - ModifiableString modifiableString = new ModifiableString(input.asComponent()); + ModifiableString modifiableString = new ModifiableString(inputComponent); // todo a better way for this if (!RegexManager.filterText(player.getName(), uuid, modifiableString, true, "chat", filterType -> { - if (!filterType.equals(FilterType.PUNISH)) { - ALogger.warn("Received another FilterType than punish when filtering chat and executing a filter action"); - return; - } - ByteArrayDataOutput out = ByteStreams.newDataOutput(); - out.writeUTF("punish"); - out.writeUTF(player.getName()); - out.writeUTF(uuid.toString()); - out.writeUTF(modifiableString.string()); - player.sendPluginMessage(ChatPlugin.getInstance(), Config.MESSAGECHANNEL, out.toByteArray()); - })) { + if (!filterType.equals(FilterType.PUNISH)) { + ALogger.warn("Received another FilterType than punish when filtering chat and executing a filter action"); + return; + } + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeUTF("punish"); + out.writeUTF(player.getName()); + out.writeUTF(uuid.toString()); + out.writeUTF(modifiableString.string()); + player.sendPluginMessage(ChatPlugin.getInstance(), Config.MESSAGECHANNEL, out.toByteArray()); + } + )) { event.setCancelled(true); GalaxyUtility.sendBlockedNotification("Language", player, - modifiableString.component(), - ""); - chatLogHandler.addChatLog(uuid, ServerName.getServerName(), PlainTextComponentSerializer.plainText().serialize(input.asComponent()), true); + modifiableString.component(), + "" + ); + String originalMessage = PlainTextComponentSerializer.plainText().serialize(inputComponent); + chatLogHandler.addChatLog(uuid, ServerName.getServerName(), originalMessage, inputComponent, true); return; // the message was blocked } @@ -120,7 +128,8 @@ public class ChatListener implements Listener { .map(audience -> (Player) audience); if (!player.hasPermission("chat.ignorebypass")) { - stream = stream.filter(receiver -> !ChatUserManager.getChatUser(receiver.getUniqueId()).getIgnoredPlayers().contains(uuid) + stream = stream.filter(receiver -> !ChatUserManager.getChatUser(receiver.getUniqueId()).getIgnoredPlayers().contains( + uuid) || receiver.hasPermission("chat.ignorebypass")); } Set receivers = stream.collect(Collectors.toSet()); @@ -135,8 +144,13 @@ public class ChatListener implements Listener { for (Player pingPlayer : playersToPing) { pingPlayer.playSound(pingPlayer.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1, 1); } - chatLogHandler.addChatLog(uuid, ServerName.getServerName(), modifiableString.string(), false); - ALogger.info(PlainTextComponentSerializer.plainText().serialize(input.asComponent())); + chatLogHandler.addChatLog(uuid, + ServerName.getServerName(), + modifiableString.string(), + modifiableString.component(), + false + ); + ALogger.info(PlainTextComponentSerializer.plainText().serialize(inputComponent)); } private void pingPlayers(Set playersToPing, ModifiableString modifiableString, Player player) { @@ -152,10 +166,10 @@ public class ChatListener implements Listener { ChatUser onlinePlayerUser = ChatUserManager.getChatUser(onlinePlayer.getUniqueId()); if (namePattern.matcher(modifiableString.string()).find()) { modifiableString.replace(TextReplacementConfig.builder() - .once() - .match(namePattern) - .replacement(mention.append(onlinePlayerUser.getDisplayName())) - .build()); + .once() + .match(namePattern) + .replacement(mention.append(onlinePlayerUser.getDisplayName())) + .build()); //TODO replace all instances of \name with just name but using the match result so the capitalization doesn't change // modifiableString.replace(TextReplacementConfig.builder() // .once() @@ -171,10 +185,10 @@ public class ChatListener implements Listener { } } else if (nickPattern.matcher(modifiableString.string()).find()) { modifiableString.replace(TextReplacementConfig.builder() - .once() - .match(nickPattern) - .replacement(mention.append(onlinePlayerUser.getDisplayName())) - .build()); + .once() + .match(nickPattern) + .replacement(mention.append(onlinePlayerUser.getDisplayName())) + .build()); if (!ChatUserManager.getChatUser(onlinePlayer.getUniqueId()).getIgnoredPlayers().contains(player.getUniqueId()) || player.hasPermission("chat.ignorebypass")) { playersToPing.add(onlinePlayer); @@ -192,7 +206,7 @@ public class ChatListener implements Listener { Placeholder.component("prefixall", user.getPrefixAll()), Placeholder.component("staffprefix", user.getStaffPrefix()), Placeholder.component("message", message) - ); + ); return Utility.parseMiniMessage(Config.CHATFORMAT, placeholders); }