Add support for miniMessage serialization in chat logs

This commit is contained in:
akastijn 2026-07-18 19:11:53 +02:00
parent c48d4f5c86
commit dc513ac2f0
4 changed files with 102 additions and 54 deletions

View File

@ -15,15 +15,22 @@ import java.util.concurrent.CompletionException;
public class ChatLogQueries { public class ChatLogQueries {
protected static void createChatLogTable() { protected static void createChatLogTable() {
String nicknamesTableQuery = "CREATE TABLE IF NOT EXISTS chat_log(" String nicknamesTableQuery = """
+ "uuid CHAR(48) NOT NULL," CREATE TABLE chat_log (
+ "time_stamp TIMESTAMP(6) NOT NULL, " id BIGINT AUTO_INCREMENT PRIMARY KEY,
+ "server VARCHAR(50) NOT NULL, " uuid CHAR(36) NOT NULL,
+ "chat_message VARCHAR(300) NOT NULL, " time_stamp TIMESTAMP(6) NOT NULL,
+ "blocked BIT(1) NOT NULL DEFAULT 0" server VARCHAR(50) NOT NULL,
+ ")"; chat_message VARCHAR(300) NOT NULL,
mini_message JSON,
blocked BOOLEAN NOT NULL DEFAULT FALSE,
try (PreparedStatement preparedStatement = DatabaseConnection.getConnection().prepareStatement(nicknamesTableQuery)) { INDEX idx_time_stamp (time_stamp)
)
""";
try (PreparedStatement preparedStatement = DatabaseConnection.getConnection().prepareStatement(
nicknamesTableQuery)) {
preparedStatement.executeUpdate(); preparedStatement.executeUpdate();
} catch (Throwable throwable) { } catch (Throwable throwable) {
ALogger.error("Failed to create chat log table", throwable); ALogger.error("Failed to create chat log table", throwable);
@ -31,7 +38,7 @@ public class ChatLogQueries {
} }
public static @NotNull CompletableFuture<Boolean> storeMessages(HashMap<UUID, List<ChatLog>> chatMessages) { public static @NotNull CompletableFuture<Boolean> storeMessages(HashMap<UUID, List<ChatLog>> 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(() -> { return CompletableFuture.supplyAsync(() -> {
try (Connection connection = DatabaseConnection.createTransactionConnection()) { try (Connection connection = DatabaseConnection.createTransactionConnection()) {
PreparedStatement preparedStatement = connection.prepareStatement(insertQuery); PreparedStatement preparedStatement = connection.prepareStatement(insertQuery);
@ -76,7 +83,9 @@ public class ChatLogQueries {
return chatLogs; return chatLogs;
} catch (SQLException sqlException) { } catch (SQLException sqlException) {
ALogger.error(String.format("Failed to retrieve messages for user %s", uuid), 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))); preparedStatement.setTimestamp(1, Timestamp.from(Instant.now().minus(duration)));
return preparedStatement.execute(); return preparedStatement.execute();
} catch (SQLException sqlException) { } catch (SQLException sqlException) {
ALogger.error(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()),
throw new CompletionException(String.format("Failed to delete messages older than %s days", duration.toDays()), sqlException); sqlException
);
throw new CompletionException(String.format("Failed to delete messages older than %s days",
duration.toDays()
), sqlException
);
} }
}); });
} }

View File

@ -1,6 +1,8 @@
package com.alttd.chat.objects.chat_log; package com.alttd.chat.objects.chat_log;
import com.alttd.chat.objects.BatchInsertable; 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 org.jetbrains.annotations.NotNull;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
@ -15,13 +17,15 @@ public class ChatLog implements BatchInsertable {
private final Instant timestamp; private final Instant timestamp;
private final String server; private final String server;
private final String message; private final String message;
private final Component miniMessage;
private final boolean blocked; 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.uuid = uuid;
this.timestamp = timestamp; this.timestamp = timestamp;
this.server = server; this.server = server;
this.message = message; this.message = message;
this.miniMessage = miniMessage;
this.blocked = blocked; this.blocked = blocked;
} }
@ -31,7 +35,10 @@ public class ChatLog implements BatchInsertable {
preparedStatement.setTimestamp(2, Timestamp.from(timestamp)); preparedStatement.setTimestamp(2, Timestamp.from(timestamp));
preparedStatement.setString(3, server); preparedStatement.setString(3, server);
preparedStatement.setString(4, message); 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() { public UUID getUuid() {

View File

@ -3,6 +3,8 @@ package com.alttd.chat.objects.chat_log;
import com.alttd.chat.config.Config; import com.alttd.chat.config.Config;
import com.alttd.chat.database.ChatLogQueries; import com.alttd.chat.database.ChatLogQueries;
import com.alttd.chat.util.ALogger; 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 org.jetbrains.annotations.NotNull;
import java.sql.ResultSet; import java.sql.ResultSet;
@ -18,8 +20,9 @@ public class ChatLogHandler {
private ScheduledExecutorService executorService = null; private ScheduledExecutorService executorService = null;
public static ChatLogHandler getInstance(boolean enableLogging) { public static ChatLogHandler getInstance(boolean enableLogging) {
if (instance == null) if (instance == null) {
instance = new ChatLogHandler(enableLogging); instance = new ChatLogHandler(enableLogging);
}
return instance; return instance;
} }
@ -35,23 +38,30 @@ public class ChatLogHandler {
Duration deleteThreshold = Duration.ofDays(Config.CHAT_LOG_DELETE_OLDER_THAN_DAYS); Duration deleteThreshold = Duration.ofDays(Config.CHAT_LOG_DELETE_OLDER_THAN_DAYS);
ChatLogQueries.deleteOldMessages(deleteThreshold).thenAccept(success -> { ChatLogQueries.deleteOldMessages(deleteThreshold).thenAccept(success -> {
if (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 { } 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 = Executors.newSingleThreadScheduledExecutor();
executorService.scheduleAtFixedRate(() -> { executorService.scheduleAtFixedRate(() -> {
saveToDatabase(false); 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!"); ALogger.info("Logging has started!");
} }
/** /**
* Shuts down the executor service and saves the chat logs to the database. * Shuts down the executor service and saves the chat logs to the database. Will throw an error if called on a
* Will throw an error if called on a ChatLogHandler that was started without logging * ChatLogHandler that was started without logging
*/ */
public void shutDown() { public void shutDown() {
executorService.shutdown(); executorService.shutdown();
@ -66,7 +76,7 @@ public class ChatLogHandler {
return isSaving; return isSaving;
} }
public synchronized void addLog(ChatLog chatLog) { private synchronized void addLog(ChatLog chatLog) {
if (isBlocked()) { if (isBlocked()) {
chatLogQueue.add(chatLog); chatLogQueue.add(chatLog);
} else { } else {
@ -105,12 +115,15 @@ public class ChatLogHandler {
Instant chatTimestamp = resultSet.getTimestamp("time_stamp").toInstant(); Instant chatTimestamp = resultSet.getTimestamp("time_stamp").toInstant();
String server = resultSet.getString("server"); String server = resultSet.getString("server");
String chatMessage = resultSet.getString("chat_message"); 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; 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) { public void addChatLog(UUID uuid, String server, String message, Component miniMessage, boolean blocked) {
addLog(new ChatLog(uuid, Instant.now(), server, message, blocked)); addLog(new ChatLog(uuid, Instant.now(), server, message, miniMessage, blocked));
} }
public CompletableFuture<List<ChatLog>> retrieveChatLogs(UUID uuid, Duration duration, String server) { public CompletableFuture<List<ChatLog>> retrieveChatLogs(UUID uuid, Duration duration, String server) {

View File

@ -55,7 +55,9 @@ public class ChatListener implements Listener {
} }
Component formatComponent = Component.text("%message%"); 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())); 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%"); 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())); event.result(formatComponent.replaceText(TextReplacementConfig.builder().match("%message%").replacement(message).build()));
} }
@ -92,8 +96,9 @@ public class ChatListener implements Listener {
UUID uuid = player.getUniqueId(); UUID uuid = player.getUniqueId();
ComponentLike input = event.message().colorIfAbsent(NamedTextColor.WHITE); 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 // todo a better way for this
if (!RegexManager.filterText(player.getName(), uuid, modifiableString, true, "chat", filterType -> { if (!RegexManager.filterText(player.getName(), uuid, modifiableString, true, "chat", filterType -> {
@ -107,12 +112,15 @@ public class ChatListener implements Listener {
out.writeUTF(uuid.toString()); out.writeUTF(uuid.toString());
out.writeUTF(modifiableString.string()); out.writeUTF(modifiableString.string());
player.sendPluginMessage(ChatPlugin.getInstance(), Config.MESSAGECHANNEL, out.toByteArray()); player.sendPluginMessage(ChatPlugin.getInstance(), Config.MESSAGECHANNEL, out.toByteArray());
})) { }
)) {
event.setCancelled(true); event.setCancelled(true);
GalaxyUtility.sendBlockedNotification("Language", player, GalaxyUtility.sendBlockedNotification("Language", player,
modifiableString.component(), modifiableString.component(),
""); ""
chatLogHandler.addChatLog(uuid, ServerName.getServerName(), PlainTextComponentSerializer.plainText().serialize(input.asComponent()), true); );
String originalMessage = PlainTextComponentSerializer.plainText().serialize(inputComponent);
chatLogHandler.addChatLog(uuid, ServerName.getServerName(), originalMessage, inputComponent, true);
return; // the message was blocked return; // the message was blocked
} }
@ -120,7 +128,8 @@ public class ChatListener implements Listener {
.map(audience -> (Player) audience); .map(audience -> (Player) audience);
if (!player.hasPermission("chat.ignorebypass")) { 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")); || receiver.hasPermission("chat.ignorebypass"));
} }
Set<Player> receivers = stream.collect(Collectors.toSet()); Set<Player> receivers = stream.collect(Collectors.toSet());
@ -135,8 +144,13 @@ public class ChatListener implements Listener {
for (Player pingPlayer : playersToPing) { for (Player pingPlayer : playersToPing) {
pingPlayer.playSound(pingPlayer.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1, 1); pingPlayer.playSound(pingPlayer.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1, 1);
} }
chatLogHandler.addChatLog(uuid, ServerName.getServerName(), modifiableString.string(), false); chatLogHandler.addChatLog(uuid,
ALogger.info(PlainTextComponentSerializer.plainText().serialize(input.asComponent())); ServerName.getServerName(),
modifiableString.string(),
modifiableString.component(),
false
);
ALogger.info(PlainTextComponentSerializer.plainText().serialize(inputComponent));
} }
private void pingPlayers(Set<Player> playersToPing, ModifiableString modifiableString, Player player) { private void pingPlayers(Set<Player> playersToPing, ModifiableString modifiableString, Player player) {