From 84a80fbabd818c20b8844e8d3f6307109d42626d Mon Sep 17 00:00:00 2001 From: ThisIsMyName134 Date: Mon, 18 May 2026 15:23:33 +0100 Subject: [PATCH 1/2] Added a Database manager class (scraper/Database) to centralise control of the database collections. --- .../legitimoose/bot/chat/GameChatHandler.java | 8 +-- .../bot/chat/command/BlockCommands.java | 8 +-- .../bot/chat/command/StreakCommand.java | 16 ++--- .../bot/discord/command/MsgCommand.java | 6 +- .../bot/discord/command/StreakCommand.java | 6 +- .../command/StreakLeaderboardHandler.java | 4 +- .../bot/http/endpoint/PlayerEndpoint.java | 6 +- .../java/net/legitimoose/bot/scraper/Ban.java | 5 +- .../net/legitimoose/bot/scraper/Database.java | 58 +++++++++++++++++++ .../net/legitimoose/bot/scraper/Player.java | 5 +- .../net/legitimoose/bot/scraper/Scraper.java | 13 +---- 11 files changed, 86 insertions(+), 49 deletions(-) create mode 100644 src/client/java/net/legitimoose/bot/scraper/Database.java diff --git a/src/client/java/net/legitimoose/bot/chat/GameChatHandler.java b/src/client/java/net/legitimoose/bot/chat/GameChatHandler.java index 3969104..28b3f8a 100644 --- a/src/client/java/net/legitimoose/bot/chat/GameChatHandler.java +++ b/src/client/java/net/legitimoose/bot/chat/GameChatHandler.java @@ -10,10 +10,7 @@ import net.legitimoose.bot.discord.DiscordBot; import net.legitimoose.bot.discord.command.MsgCommand; import net.legitimoose.bot.discord.command.ReplyCommand; -import net.legitimoose.bot.scraper.Ban; -import net.legitimoose.bot.scraper.Player; -import net.legitimoose.bot.scraper.Rank; -import net.legitimoose.bot.scraper.Scraper; +import net.legitimoose.bot.scraper.*; import net.legitimoose.bot.util.DiscordUtil; import net.legitimoose.bot.util.DiscordWebhook; import net.legitimoose.bot.util.DiscordWebhook.Embed; @@ -210,9 +207,8 @@ public void handleSwitchMessage(SwitchMatcher transfer, DiscordWebhook webhook, public void handleJoinMessage(JoinMatcher join, DiscordWebhook webhook) { Instant time = Instant.now(); - MongoCollection players = Scraper.getInstance().db.getCollection("players", Player.class); String username = join.getUsername(); - Player dbPlayer = players.find(eq("name", username)).first(); + Player dbPlayer = Database.getPlayers().find(eq("name", username)).first(); String uuid = McUtil.getUuidOrThrow(username); int days; diff --git a/src/client/java/net/legitimoose/bot/chat/command/BlockCommands.java b/src/client/java/net/legitimoose/bot/chat/command/BlockCommands.java index 90ff12c..d2bd863 100644 --- a/src/client/java/net/legitimoose/bot/chat/command/BlockCommands.java +++ b/src/client/java/net/legitimoose/bot/chat/command/BlockCommands.java @@ -7,6 +7,7 @@ import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mongodb.client.MongoCollection; import com.mongodb.client.model.Updates; +import net.legitimoose.bot.scraper.Database; import net.legitimoose.bot.scraper.Player; import net.legitimoose.bot.scraper.Scraper; @@ -15,7 +16,6 @@ import static com.mongodb.client.model.Filters.eq; public class BlockCommands { - private static final MongoCollection coll = Scraper.getInstance().db.getCollection("players", Player.class); public static void register(CommandDispatcher dispatcher) { // Block @@ -24,14 +24,14 @@ public static void register(CommandDispatcher dispatcher) { .executes(context -> { CommandSource source = context.getSource(); String blocked = context.getArgument("username", String.class); - coll.updateOne(eq("name", source.username()), Updates.set("blocked", List.of(blocked))); + Database.getPlayers().updateOne(eq("name", source.username()), Updates.set("blocked", List.of(blocked))); source.sendMessage("Blocked @" + blocked + " from sending you messages"); return Command.SINGLE_SUCCESS; })) .then(LiteralArgumentBuilder.literal("list") .executes(context -> { CommandSource source = context.getSource(); - List blockedPlayers = coll.find(eq("name", source.username())).first().blocked(); + List blockedPlayers = Database.getPlayers().find(eq("name", source.username())).first().blocked(); source.sendMessage("Blocked players:
" + String.join("
", blockedPlayers)); return Command.SINGLE_SUCCESS; }))); @@ -41,7 +41,7 @@ public static void register(CommandDispatcher dispatcher) { .executes(context -> { CommandSource source = context.getSource(); String blocked = context.getArgument("username", String.class); - coll.updateOne(eq("name", source.username()), Updates.pull("blocked", blocked)); + Database.getPlayers().updateOne(eq("name", source.username()), Updates.pull("blocked", blocked)); source.sendMessage("Unblocked @" + blocked + " from sending you messages"); return Command.SINGLE_SUCCESS; }))); diff --git a/src/client/java/net/legitimoose/bot/chat/command/StreakCommand.java b/src/client/java/net/legitimoose/bot/chat/command/StreakCommand.java index b8e96b4..bea8670 100644 --- a/src/client/java/net/legitimoose/bot/chat/command/StreakCommand.java +++ b/src/client/java/net/legitimoose/bot/chat/command/StreakCommand.java @@ -8,6 +8,7 @@ import com.mongodb.client.MongoCollection; import com.mongodb.client.model.Filters; import com.mongodb.client.model.Updates; +import net.legitimoose.bot.scraper.Database; import net.legitimoose.bot.scraper.Player; import net.legitimoose.bot.scraper.Scraper; @@ -15,18 +16,17 @@ import static com.mongodb.client.model.Sorts.descending; public class StreakCommand { - public static final MongoCollection players = Scraper.getInstance().db.getCollection("players", Player.class); public static void register(CommandDispatcher dispatcher) { dispatcher.register(LiteralArgumentBuilder.literal("streak") .then(LiteralArgumentBuilder.literal("on") .executes(context -> { - Player player = players.find(eq("name", context.getSource().username())).first(); + Player player = Database.getPlayers().find(eq("name", context.getSource().username())).first(); assert player != null; if (player.streak().notifications() == true) { context.getSource().sendMessage("Your streak notifications are already enabled!"); } else { - players.updateOne(eq("name", context.getSource().username()), Updates.set("streak.notify", true)); + Database.getPlayers().updateOne(eq("name", context.getSource().username()), Updates.set("streak.notify", true)); context.getSource().sendMessage("Enabled streak notifications!"); } return Command.SINGLE_SUCCESS; @@ -34,12 +34,12 @@ public static void register(CommandDispatcher dispatcher) { .then(LiteralArgumentBuilder.literal("off") .executes(context -> { - Player player = players.find(eq("name", context.getSource().username())).first(); + Player player = Database.getPlayers().find(eq("name", context.getSource().username())).first(); assert player != null; if (player.streak().notifications() == false) { context.getSource().sendMessage("Your streak notifications are already disabled!"); } else { - players.updateOne(eq("name", context.getSource().username()), Updates.set("streak.notify", false)); + Database.getPlayers().updateOne(eq("name", context.getSource().username()), Updates.set("streak.notify", false)); context.getSource().sendMessage("Disabled streak notifications!"); } return Command.SINGLE_SUCCESS; @@ -48,7 +48,7 @@ public static void register(CommandDispatcher dispatcher) { .then(RequiredArgumentBuilder.argument("username", StringArgumentType.string()) .executes(context -> { String username = context.getArgument("username", String.class); - Player player = players.find(eq("name", username)).first(); + Player player = Database.getPlayers().find(eq("name", username)).first(); if (player == null) { context.getSource().sendMessage("Player not found!"); return Command.SINGLE_SUCCESS; @@ -70,7 +70,7 @@ public static void register(CommandDispatcher dispatcher) { })) .executes(context -> { - Player player = players.find(eq("name", context.getSource().username())).first(); + Player player = Database.getPlayers().find(eq("name", context.getSource().username())).first(); assert player != null; context.getSource().sendMessage("Your current login streak is " + player.streak().days() + " day(s)"); return Command.SINGLE_SUCCESS; @@ -80,7 +80,7 @@ public static void register(CommandDispatcher dispatcher) { private static String getLeaderboardString(int page) { StringBuilder lbString = new StringBuilder("
"); int i = 1; - for (Player player : players.find(Filters.exists("streak.days")).sort(descending("streak.days", "last_joined")).skip((page - 1) * 5).limit(5)) { + for (Player player : Database.getPlayers().find(Filters.exists("streak.days")).sort(descending("streak.days", "last_joined")).skip((page - 1) * 5).limit(5)) { lbString.append((page - 1) * 5 + i).append(". ").append(player.name()).append(" - ").append(player.streak().days()).append(" day(s)"); if (i < 5) { lbString.append("
"); diff --git a/src/client/java/net/legitimoose/bot/discord/command/MsgCommand.java b/src/client/java/net/legitimoose/bot/discord/command/MsgCommand.java index b693e43..84fe6f1 100644 --- a/src/client/java/net/legitimoose/bot/discord/command/MsgCommand.java +++ b/src/client/java/net/legitimoose/bot/discord/command/MsgCommand.java @@ -1,10 +1,9 @@ package net.legitimoose.bot.discord.command; -import com.mongodb.client.MongoCollection; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.legitimoose.bot.scraper.Database; import net.legitimoose.bot.scraper.Player; -import net.legitimoose.bot.scraper.Scraper; import net.legitimoose.bot.util.DiscordUtil; import net.legitimoose.bot.util.McUtil; import net.minecraft.client.Minecraft; @@ -15,7 +14,6 @@ import static com.mongodb.client.model.Filters.regex; public class MsgCommand extends ListenerAdapter { - private static final MongoCollection coll = Scraper.getInstance().db.getCollection("players", Player.class); public static final Map lastSent = new HashMap<>(); @@ -24,7 +22,7 @@ public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { if (!event.getName().equals("msg")) return; String message = event.getOption("message").getAsString(); String player = event.getOption("player").getAsString(); - Player playerObj = coll.find(regex("name", player, "i")).first(); + Player playerObj = Database.getPlayers().find(regex("name", player, "i")).first(); if (playerObj == null || playerObj.blocked().contains(event.getUser().getName())) { event.reply("Failed to send, player has blocked you or does not exist.").setEphemeral(true).queue(); return; diff --git a/src/client/java/net/legitimoose/bot/discord/command/StreakCommand.java b/src/client/java/net/legitimoose/bot/discord/command/StreakCommand.java index 62c6b0c..37dd4da 100644 --- a/src/client/java/net/legitimoose/bot/discord/command/StreakCommand.java +++ b/src/client/java/net/legitimoose/bot/discord/command/StreakCommand.java @@ -1,14 +1,12 @@ package net.legitimoose.bot.discord.command; -import com.mongodb.client.MongoCollection; import com.mongodb.client.model.Filters; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.legitimoose.bot.scraper.Database; import net.legitimoose.bot.scraper.Player; -import net.legitimoose.bot.scraper.Scraper; public class StreakCommand extends ListenerAdapter { - private final MongoCollection players = Scraper.getInstance().db.getCollection("players", Player.class); @Override public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { @@ -18,7 +16,7 @@ public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { String player = event.getOption("player").getAsString(); event.deferReply().queue(); - Player dbPlayer = players.find(Filters.eq("name", player)).first(); + Player dbPlayer = Database.getPlayers().find(Filters.eq("name", player)).first(); if (dbPlayer == null) { event.getHook().sendMessage("Could not find a player named " + player).queue(); return; diff --git a/src/client/java/net/legitimoose/bot/discord/command/StreakLeaderboardHandler.java b/src/client/java/net/legitimoose/bot/discord/command/StreakLeaderboardHandler.java index e9fbb8a..67a5e0e 100644 --- a/src/client/java/net/legitimoose/bot/discord/command/StreakLeaderboardHandler.java +++ b/src/client/java/net/legitimoose/bot/discord/command/StreakLeaderboardHandler.java @@ -7,10 +7,10 @@ import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.legitimoose.bot.scraper.Database; import net.legitimoose.bot.scraper.Player; import static com.mongodb.client.model.Sorts.descending; -import static net.legitimoose.bot.chat.command.StreakCommand.players; public class StreakLeaderboardHandler extends ListenerAdapter { static int maxId = 0; @@ -46,7 +46,7 @@ public void onButtonInteraction(ButtonInteractionEvent event) { private String getLeaderboardString(int page) { StringBuilder lbString = new StringBuilder(); int i = 1; - for (Player player : players.find(Filters.exists("streak.days")).sort(descending("streak.days", "last_joined")).skip((page - 1) * 5).limit(5)) { + for (Player player : Database.getPlayers().find(Filters.exists("streak.days")).sort(descending("streak.days", "last_joined")).skip((page - 1) * 5).limit(5)) { lbString.append((page - 1) * 5 + i).append(". ").append(player.name()).append(" - ").append(player.streak().days()).append(" day(s)").append('\n'); i++; } diff --git a/src/client/java/net/legitimoose/bot/http/endpoint/PlayerEndpoint.java b/src/client/java/net/legitimoose/bot/http/endpoint/PlayerEndpoint.java index 8248533..cec9c25 100644 --- a/src/client/java/net/legitimoose/bot/http/endpoint/PlayerEndpoint.java +++ b/src/client/java/net/legitimoose/bot/http/endpoint/PlayerEndpoint.java @@ -3,6 +3,7 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.mongodb.client.MongoCollection; +import net.legitimoose.bot.scraper.Database; import net.legitimoose.bot.scraper.Player; import net.legitimoose.bot.scraper.Rank; import net.legitimoose.bot.scraper.Scraper; @@ -18,7 +19,6 @@ public class PlayerEndpoint { private final Pattern glistPattern = Pattern.compile("\\[(.*)] \\(\\d*\\): (.*)"); - private final MongoCollection players = Scraper.getInstance().db.getCollection("players", Player.class); public JsonArray handleRequest() { JsonArray response = new JsonArray(); @@ -36,7 +36,7 @@ public JsonArray handleRequest() { JsonObject player = new JsonObject(); try { String uuid = McUtil.getUuid(user); - Player dbPlayer = players.find(eq("uuid", uuid)).first(); + Player dbPlayer = Database.getPlayers().find(eq("uuid", uuid)).first(); Rank rank; if (dbPlayer == null) { rank = Rank.Unknown; @@ -75,7 +75,7 @@ public JsonObject handleRequest(String uuid) { if (!McUtil.getUuid(user).equals(uuid)) { continue; } - Player dbPlayer = players.find(eq("uuid", uuid)).first(); + Player dbPlayer = Database.getPlayers().find(eq("uuid", uuid)).first(); Rank rank; if (dbPlayer == null) { rank = Rank.Unknown; diff --git a/src/client/java/net/legitimoose/bot/scraper/Ban.java b/src/client/java/net/legitimoose/bot/scraper/Ban.java index 593dfa0..6032dc0 100644 --- a/src/client/java/net/legitimoose/bot/scraper/Ban.java +++ b/src/client/java/net/legitimoose/bot/scraper/Ban.java @@ -1,6 +1,5 @@ package net.legitimoose.bot.scraper; -import com.mongodb.client.MongoCollection; import net.legitimoose.bot.util.McUtil; public record Ban( @@ -45,8 +44,6 @@ public static void writePermBan(long banTime, String bannedPlayer, String modera } private void write() { - MongoCollection bans = Scraper.getInstance().db.getCollection("bans", Ban.class); - - bans.insertOne(this); + Database.getBans().insertOne(this); } } diff --git a/src/client/java/net/legitimoose/bot/scraper/Database.java b/src/client/java/net/legitimoose/bot/scraper/Database.java new file mode 100644 index 0000000..5acfe03 --- /dev/null +++ b/src/client/java/net/legitimoose/bot/scraper/Database.java @@ -0,0 +1,58 @@ +package net.legitimoose.bot.scraper; + +import static net.legitimoose.bot.LegitimooseBot.CONFIG; +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoDatabase; +import org.bson.Document; + +public class Database { + + private static Database instance; + + private static final String DATABASE_NAME = "legitimooseapi"; + + private static final MongoClient mongoClient = + MongoClients.create(CONFIG.getString("mongoUri")); + + private final MongoDatabase database = + mongoClient.getDatabase(DATABASE_NAME); + + private MongoCollection worlds; + private MongoCollection players; + private MongoCollection stats; + private MongoCollection bans; + + private Database() { + initialize(); + } + + private static Database getInstance() { + return instance == null ? (instance = new Database()) : instance; + } + + private void initialize() { + worlds = database.getCollection("worlds", World.class); + players = database.getCollection("players", Player.class); + stats = database.getCollection("stats"); + bans = database.getCollection("bans", Ban.class); + } + + public static MongoCollection getPlayers() { + return getInstance().players; + } + + public static MongoCollection getWorlds() { + return getInstance().worlds; + } + + public static MongoCollection getStats() { + return getInstance().stats; + } + + public static MongoCollection getBans() { + return getInstance().bans; + } + +} \ No newline at end of file diff --git a/src/client/java/net/legitimoose/bot/scraper/Player.java b/src/client/java/net/legitimoose/bot/scraper/Player.java index 5912962..c335234 100644 --- a/src/client/java/net/legitimoose/bot/scraper/Player.java +++ b/src/client/java/net/legitimoose/bot/scraper/Player.java @@ -1,6 +1,5 @@ package net.legitimoose.bot.scraper; -import com.mongodb.client.MongoCollection; import com.mongodb.client.model.UpdateOptions; import com.mongodb.client.model.Updates; import org.bson.BsonDateTime; @@ -25,8 +24,6 @@ public record Streak(Integer days, @BsonProperty("notify") Boolean notifications } public void write() { - MongoCollection players = Scraper.getInstance().db.getCollection("players", Player.class); - Bson updates = Updates.combine( Updates.set("uuid", this.uuid), @@ -35,6 +32,6 @@ public void write() { Updates.set("blocked", this.blocked), Updates.set("streak", this.streak), Updates.set("last_joined", new BsonDateTime(this.last_joined.toEpochMilli()))); - players.updateOne(eq("uuid", this.uuid), updates, new UpdateOptions().upsert(true)); + Database.getPlayers().updateOne(eq("uuid", this.uuid), updates, new UpdateOptions().upsert(true)); } } diff --git a/src/client/java/net/legitimoose/bot/scraper/Scraper.java b/src/client/java/net/legitimoose/bot/scraper/Scraper.java index d042b69..d27c09f 100644 --- a/src/client/java/net/legitimoose/bot/scraper/Scraper.java +++ b/src/client/java/net/legitimoose/bot/scraper/Scraper.java @@ -5,10 +5,7 @@ import com.mojang.brigadier.context.CommandContextBuilder; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.serialization.JsonOps; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoClients; import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoDatabase; import com.mongodb.client.model.*; import net.legitimoose.bot.LegitimooseBotClient; import net.legitimoose.bot.util.DiscordUtil; @@ -46,15 +43,11 @@ public class Scraper { private volatile boolean scrapeOverride = false; - private final MongoClient mongoClient = MongoClients.create(CONFIG.getString("mongoUri")); private final DiscordWebhook errorWebhook = new DiscordWebhook(CONFIG.getString("errorWebhook")); private final Pattern jamScorePattern = Pattern.compile("^CategoryScore\\(rank=(.*), score=(.*)\\)"); private final Pattern ownerNamePattern = Pattern.compile("^by (?:[^|]+\\|\\s*)?(.+)"); - public final MongoDatabase db = mongoClient.getDatabase("legitimooseapi"); - private final MongoCollection coll = db.getCollection("worlds", World.class); - private void waitSeconds(int time) { try { TimeUnit.SECONDS.sleep(time); @@ -98,12 +91,12 @@ private void error(String message, Exception exception) throws IOException, URIS public void scrape() { if (!CONFIG.getBoolean("scrape", true)) return; Minecraft client = Minecraft.getInstance(); - MongoCollection stats = db.getCollection("stats"); + MongoCollection stats = Database.getStats(); stats.createIndex(Indexes.descending("timestamp")); List indexes = new ArrayList<>(); indexes.add(new IndexModel(Indexes.ascending("world_uuid"))); indexes.add(new IndexModel(Indexes.ascending("last_scraped_ms"), new IndexOptions().expireAfter(24L, TimeUnit.HOURS))); - coll.createIndexes(indexes); + Database.getWorlds().createIndexes(indexes); // Please ignore the nulls. Only the 'input' is actually used CommandContext context = new CommandContextBuilder(null, null, null, 1).build("/find "); @@ -317,7 +310,7 @@ private void bulkUpsert(List worlds) { } if (!operations.isEmpty()) { - coll.bulkWrite(operations); + Database.getWorlds().bulkWrite(operations); } LOGGER.info("Bulk wrote {} worlds", operations.size()); } From ff341fb532098a37fedf1cb9e887666ac335a71b Mon Sep 17 00:00:00 2001 From: ThisIsMyName134 Date: Mon, 18 May 2026 16:38:36 +0100 Subject: [PATCH 2/2] Inlined the initialise function content into the constructor (scraper/Database) --- .../java/net/legitimoose/bot/scraper/Database.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/client/java/net/legitimoose/bot/scraper/Database.java b/src/client/java/net/legitimoose/bot/scraper/Database.java index 5acfe03..18473f7 100644 --- a/src/client/java/net/legitimoose/bot/scraper/Database.java +++ b/src/client/java/net/legitimoose/bot/scraper/Database.java @@ -25,20 +25,16 @@ public class Database { private MongoCollection bans; private Database() { - initialize(); - } - - private static Database getInstance() { - return instance == null ? (instance = new Database()) : instance; - } - - private void initialize() { worlds = database.getCollection("worlds", World.class); players = database.getCollection("players", Player.class); stats = database.getCollection("stats"); bans = database.getCollection("bans", Ban.class); } + private static Database getInstance() { + return instance == null ? (instance = new Database()) : instance; + } + public static MongoCollection getPlayers() { return getInstance().players; }