From 6924cbb40320ac58b43bc0dab5d3ae6fed927b0d Mon Sep 17 00:00:00 2001 From: Omri H Date: Sun, 1 Mar 2026 14:28:46 +0200 Subject: [PATCH 01/18] chore: bump version to 3.0.0-alpha.1 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 8364ae8..2b405e3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ org.gradle.configuration-cache=false minecraft_version=1.21.11 loader_version=0.18.4 # Mod Properties -mod_version=2.13.0 +mod_version=3.0.0-alpha.1 maven_group=net.legitimoose archives_base_name=Legitimoose-Bot # Dependencies From 70b0306b9632caa440c5eb51224a9be967bc4c0d Mon Sep 17 00:00:00 2001 From: Omri H Date: Sun, 1 Mar 2026 14:30:11 +0200 Subject: [PATCH 02/18] feat: remove deprecated jam fields --- src/client/java/net/legitimoose/bot/scraper/Scraper.java | 5 ----- src/client/java/net/legitimoose/bot/scraper/World.java | 8 -------- 2 files changed, 13 deletions(-) diff --git a/src/client/java/net/legitimoose/bot/scraper/Scraper.java b/src/client/java/net/legitimoose/bot/scraper/Scraper.java index 27733f9..c8ac415 100644 --- a/src/client/java/net/legitimoose/bot/scraper/Scraper.java +++ b/src/client/java/net/legitimoose/bot/scraper/Scraper.java @@ -217,9 +217,6 @@ public void scrape() { featured_instant, - jam_world, - jam_id, - jam, itemStack.toString().substring(2), @@ -268,8 +265,6 @@ private void bulkUpsert(List worlds) { Updates.set("raw_name", Document.parse(world.raw_name())), Updates.set("raw_description", BsonArray.parse(world.raw_description())), Updates.set("featured_instant", world.featured_instant()), - Updates.set("jam_world", world.jam_world()), - Updates.set("jam_id", world.jam_id()), Updates.set("jam", Document.parse(world.jam().toString())), Updates.set("icon", world.icon()), Updates.set("last_scraped", world.last_scraped()), diff --git a/src/client/java/net/legitimoose/bot/scraper/World.java b/src/client/java/net/legitimoose/bot/scraper/World.java index c34e2b4..0128c99 100644 --- a/src/client/java/net/legitimoose/bot/scraper/World.java +++ b/src/client/java/net/legitimoose/bot/scraper/World.java @@ -25,14 +25,6 @@ public record World( String raw_description, int featured_instant, - @Deprecated - @ApiStatus.ScheduledForRemoval(inVersion = "3.0.0") // API v4 - boolean jam_world, - - @Deprecated - @ApiStatus.ScheduledForRemoval(inVersion = "3.0.0") // API v4 - int jam_id, - JsonObject jam, String icon, From 1571c07ac553d8765bef8a9b82f652d71eaecee1 Mon Sep 17 00:00:00 2001 From: Omri H Date: Sun, 1 Mar 2026 14:55:49 +0200 Subject: [PATCH 03/18] feat: show offline players in /player --- .../bot/http/endpoint/PlayerEndpoint.java | 62 ++++++++++++++----- 1 file changed, 45 insertions(+), 17 deletions(-) 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 201b1ea..524d4f3 100644 --- a/src/client/java/net/legitimoose/bot/http/endpoint/PlayerEndpoint.java +++ b/src/client/java/net/legitimoose/bot/http/endpoint/PlayerEndpoint.java @@ -8,6 +8,8 @@ import net.legitimoose.bot.scraper.Scraper; import net.legitimoose.bot.util.McUtil; +import java.io.IOException; +import java.net.URISyntaxException; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -32,19 +34,35 @@ public JsonArray handleRequest() { } } - for (String user : usernames.keySet()) { + for (String username : usernames.keySet()) { + try { + if (players.find(eq("name", username)).first() == null) { + new Player(McUtil.getUuid(username), username, Rank.Unknown, List.of()).write(); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + for (Player dbPlayer : players.find()) { JsonObject player = new JsonObject(); try { - String uuid = McUtil.getUuid(user); - Player dbPlayer = players.find(eq("uuid", uuid)).first(); Rank rank; if (dbPlayer == null) rank = Rank.Unknown; else rank = dbPlayer.rank(); - player.addProperty("uuid", uuid); - player.addProperty("name", user); + boolean online = false; + String world = ""; + if (usernames.get(dbPlayer.name()) != null) { + online = true; + world = usernames.get(dbPlayer.name()); + } + + player.addProperty("uuid", dbPlayer.uuid()); + player.addProperty("name", dbPlayer.name()); player.addProperty("rank", rank.toString()); - player.addProperty("world", usernames.get(user)); + player.addProperty("online", online); + player.addProperty("world", world); response.add(player); } catch (Exception e) { throw new RuntimeException(e); @@ -66,24 +84,34 @@ public JsonObject handleRequest(String uuid) { } } - for (String user : usernames.keySet()) { + for (String username : usernames.keySet()) { try { - if (!McUtil.getUuid(user).equals(uuid)) { - continue; + if (players.find(eq("name", username)).first() == null) { + new Player(McUtil.getUuid(username), username, Rank.Unknown, List.of()).write(); } - Player dbPlayer = players.find(eq("uuid", uuid)).first(); - Rank rank; - if (dbPlayer == null) rank = Rank.Unknown; - else rank = dbPlayer.rank(); - - response.addProperty("name", user); - response.addProperty("rank", rank.toString()); - response.addProperty("world", usernames.get(user)); } catch (Exception e) { throw new RuntimeException(e); } } + Player dbPlayer = players.find(eq("uuid", uuid)).first(); + try { + boolean online = false; + String world = ""; + if (usernames.get(dbPlayer.name()) != null) { + online = true; + world = usernames.get(dbPlayer.name()); + } + + response.addProperty("uuid", dbPlayer.uuid()); + response.addProperty("name", dbPlayer.name()); + response.addProperty("rank", dbPlayer.rank().toString()); + response.addProperty("online", online); + response.addProperty("world", world); + } catch (Exception e) { + throw new RuntimeException(e); + } + return response; } } From 57935aaa704633c6ad34ee47c494f0e8d15bf3ae Mon Sep 17 00:00:00 2001 From: Omri H Date: Sun, 1 Mar 2026 15:45:53 +0200 Subject: [PATCH 04/18] feat: scrape world owner rank, players from world owners --- .../legitimoose/bot/chat/EventHandler.java | 2 +- .../bot/http/endpoint/PlayerEndpoint.java | 5 +- .../net/legitimoose/bot/scraper/Player.java | 2 +- .../net/legitimoose/bot/scraper/Scraper.java | 48 ++++++++++++++++--- .../net/legitimoose/bot/scraper/World.java | 1 + 5 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/client/java/net/legitimoose/bot/chat/EventHandler.java b/src/client/java/net/legitimoose/bot/chat/EventHandler.java index 976ca0e..ed20a92 100644 --- a/src/client/java/net/legitimoose/bot/chat/EventHandler.java +++ b/src/client/java/net/legitimoose/bot/chat/EventHandler.java @@ -92,11 +92,11 @@ public void onRecieveMessage(Component message, boolean overlay) throws CommandS if (rank == null) rank = ""; if (players.countDocuments(new Document("uuid", uuid)) == 0) { cleanMessage = String.format("**%s** joined the server for the first time!", username); + new Player(uuid, username, Rank.getEnum(rank), List.of()).write(); } else { cleanMessage = String.format("**%s** joined the server.", username); } - new Player(uuid, username, Rank.getEnum(rank), List.of()).write(); webhook.setEmbedThumbnail(String.format("https://mc-heads.net/head/%s/50/left", username)); webhook.setContent(cleanMessage.replace("@", "")); try { 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 524d4f3..44648f3 100644 --- a/src/client/java/net/legitimoose/bot/http/endpoint/PlayerEndpoint.java +++ b/src/client/java/net/legitimoose/bot/http/endpoint/PlayerEndpoint.java @@ -7,6 +7,7 @@ import net.legitimoose.bot.scraper.Rank; import net.legitimoose.bot.scraper.Scraper; import net.legitimoose.bot.util.McUtil; +import org.bson.Document; import java.io.IOException; import java.net.URISyntaxException; @@ -36,7 +37,7 @@ public JsonArray handleRequest() { for (String username : usernames.keySet()) { try { - if (players.find(eq("name", username)).first() == null) { + if (players.countDocuments(new Document("name", username)) == 0) { new Player(McUtil.getUuid(username), username, Rank.Unknown, List.of()).write(); } } catch (Exception e) { @@ -86,7 +87,7 @@ public JsonObject handleRequest(String uuid) { for (String username : usernames.keySet()) { try { - if (players.find(eq("name", username)).first() == null) { + if (players.countDocuments(new Document("name", username)) == 0) { new Player(McUtil.getUuid(username), username, Rank.Unknown, List.of()).write(); } } catch (Exception e) { diff --git a/src/client/java/net/legitimoose/bot/scraper/Player.java b/src/client/java/net/legitimoose/bot/scraper/Player.java index 3cc154c..d41f2b4 100644 --- a/src/client/java/net/legitimoose/bot/scraper/Player.java +++ b/src/client/java/net/legitimoose/bot/scraper/Player.java @@ -23,7 +23,7 @@ public void write() { Updates.set("uuid", this.uuid), Updates.set("name", this.name), Updates.set("rank", this.rank), - Updates.set("blocked", this.blocked)); + Updates.setOnInsert("blocked", this.blocked)); players.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 c8ac415..d745008 100644 --- a/src/client/java/net/legitimoose/bot/scraper/Scraper.java +++ b/src/client/java/net/legitimoose/bot/scraper/Scraper.java @@ -11,6 +11,7 @@ import com.mongodb.client.MongoDatabase; import com.mongodb.client.model.*; import net.legitimoose.bot.util.DiscordWebhook; +import net.legitimoose.bot.util.McUtil; import net.minecraft.client.Minecraft; import net.minecraft.core.component.DataComponents; import net.minecraft.nbt.CompoundTag; @@ -44,10 +45,12 @@ public class Scraper { 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*)?(.+)"); + 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 final MongoCollection playersColl = db.getCollection("players", Player.class); + private void waitSeconds(long time) { try { @@ -78,7 +81,7 @@ private void error(String message, Exception exception) throws IOException, URIS errorWebhook.execute(); } - public void scrape() { + public void scrape() throws IOException, URISyntaxException, InterruptedException { if (!CONFIG.getBoolean("scrape", true)) return; Minecraft client = Minecraft.getInstance(); MongoCollection stats = db.getCollection("stats"); @@ -112,6 +115,7 @@ public void scrape() { LOGGER.info("Last page is: {}", max_pages); for (int i = 1; i <= max_pages; i++) { List worlds = new ArrayList<>(); + List players = new ArrayList<>(); Container inv = client.player.containerMenu.getSlot(0).container; for (int j = 0; j <= 26; j++) { @@ -131,15 +135,24 @@ public void scrape() { } String owner_name = ""; + Rank owner_rank = Rank.Unknown; int ownerLine = descriptionLines; while (!itemStack.get(DataComponents.LORE).lines().get(ownerLine).getString().startsWith("by")) { ownerLine++; } Matcher ownerNameMatcher = ownerNamePattern.matcher(itemStack.get(DataComponents.LORE).lines().get(ownerLine).getString()); if (ownerNameMatcher.find()) { - owner_name = ownerNameMatcher.group(1); + owner_name = ownerNameMatcher.group(2); + if (ownerNameMatcher.group(1) == null) { + owner_rank = Rank.Non; + } else { + owner_rank = Rank.getEnum(ownerNameMatcher.group(1)); + } } + String owner_uuid = getNbtString(publicBukkitValues, "owner"); + players.add(new Player(owner_uuid, owner_name, owner_rank, List.of())); + StringBuilder description = new StringBuilder(); for (int k = 0; k < descriptionLines; k++) { description.append(itemStack.get(DataComponents.LORE).lines().get(k).getString()); @@ -190,8 +203,9 @@ public void scrape() { getNbtBoolean(publicBukkitValues, "enforce_whitelist"), getNbtBoolean(publicBukkitValues, "locked"), - getNbtString(publicBukkitValues, "owner"), + owner_uuid, owner_name, + owner_rank, getNbtInt(publicBukkitValues, "player_count"), getNbtInt(publicBukkitValues, "max_players"), @@ -226,7 +240,7 @@ public void scrape() { worlds.add(world); LOGGER.info("Scraped World {} {}: {}", j, world.world_uuid(), world.name()); } - bulkUpsert(worlds); + bulkUpsert(worlds, players); // finally, click on next page button LOGGER.info("Scraped page #{}", i); Minecraft.getInstance() @@ -240,8 +254,9 @@ public void scrape() { LOGGER.info("Finished Scraping"); } - private void bulkUpsert(List worlds) { + private void bulkUpsert(List worlds, List players) { List> operations = new ArrayList<>(); + List> playerOperations = new ArrayList<>(); LOGGER.info("writing world"); for (World world : worlds) { Bson updates = @@ -252,6 +267,7 @@ private void bulkUpsert(List worlds) { Updates.set("locked", world.locked()), Updates.set("owner_uuid", world.owner_uuid()), Updates.set("owner_name", world.owner_name()), + Updates.set("owner_rank", world.owner_rank()), Updates.set("player_count", world.player_count()), Updates.set("max_players", world.max_players()), Updates.set("max_datapack_size", world.max_datapack_size()), @@ -276,10 +292,30 @@ private void bulkUpsert(List worlds) { )); } + for (Player player : players) { + Bson playerUpdates = + Updates.combine( + Updates.set("uuid", player.uuid()), + Updates.set("name", player.name()), + Updates.set("rank", player.rank()), + Updates.setOnInsert("blocked", player.blocked())); + + playerOperations.add(new UpdateOneModel<>( + eq("uuid", player.uuid()), + playerUpdates, + new UpdateOptions().upsert(true) + )); + } + if (!operations.isEmpty()) { coll.bulkWrite(operations); } + + if (!playerOperations.isEmpty()) { + playersColl.bulkWrite(playerOperations); + } LOGGER.info("Bulk wrote {} worlds", operations.size()); + LOGGER.info("Bulk wrote {} players", playerOperations.size()); } private String getNbtString(CompoundTag tag, String field) { diff --git a/src/client/java/net/legitimoose/bot/scraper/World.java b/src/client/java/net/legitimoose/bot/scraper/World.java index 0128c99..c9edbe3 100644 --- a/src/client/java/net/legitimoose/bot/scraper/World.java +++ b/src/client/java/net/legitimoose/bot/scraper/World.java @@ -10,6 +10,7 @@ public record World( boolean locked, String owner_uuid, String owner_name, + Rank owner_rank, int player_count, int max_players, int max_datapack_size, From c75a5329b44b8130f001b240ba2eba9c53ef09f0 Mon Sep 17 00:00:00 2001 From: Omri H Date: Sun, 1 Mar 2026 15:46:06 +0200 Subject: [PATCH 05/18] chore: bump version to 3.0.0-alpha.2 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 2b405e3..41e04bb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ org.gradle.configuration-cache=false minecraft_version=1.21.11 loader_version=0.18.4 # Mod Properties -mod_version=3.0.0-alpha.1 +mod_version=3.0.0-alpha.2 maven_group=net.legitimoose archives_base_name=Legitimoose-Bot # Dependencies From 9f80f587eb55d4baffeeb4d61df16c5952ab86b1 Mon Sep 17 00:00:00 2001 From: Omri H Date: Tue, 21 Apr 2026 12:48:36 +0300 Subject: [PATCH 06/18] feat: normalized_name world field --- .../net/legitimoose/bot/scraper/Scraper.java | 7 +- .../net/legitimoose/bot/scraper/World.java | 2 +- .../net/legitimoose/bot/util/Unicode.java | 104 ++++++++++++++++++ 3 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 src/client/java/net/legitimoose/bot/util/Unicode.java diff --git a/src/client/java/net/legitimoose/bot/scraper/Scraper.java b/src/client/java/net/legitimoose/bot/scraper/Scraper.java index a65c145..61ef356 100644 --- a/src/client/java/net/legitimoose/bot/scraper/Scraper.java +++ b/src/client/java/net/legitimoose/bot/scraper/Scraper.java @@ -13,6 +13,7 @@ import net.legitimoose.bot.LegitimooseBotClient; import net.legitimoose.bot.util.DiscordUtil; import net.legitimoose.bot.util.DiscordWebhook; +import net.legitimoose.bot.util.Unicode; import net.minecraft.client.Minecraft; import net.minecraft.core.component.DataComponents; import net.minecraft.nbt.CompoundTag; @@ -231,6 +232,8 @@ public void scrape() throws IOException, URISyntaxException, InterruptedExceptio } } + String itemName = itemStack.get(DataComponents.CUSTOM_NAME).getString(); + World world = new World( getNbtString(publicBukkitValues, "creation_date"), getNbtInt(publicBukkitValues, "creation_date_unix_seconds"), @@ -255,7 +258,8 @@ public void scrape() throws IOException, URISyntaxException, InterruptedExceptio getNbtBoolean(publicBukkitValues, "whitelist_on_version_change"), - itemStack.get(DataComponents.CUSTOM_NAME).getString(), + itemName, + Unicode.normalize(itemName), description.toString(), ComponentSerialization.CODEC.encodeStart(JsonOps.INSTANCE, itemStack.get(DataComponents.CUSTOM_NAME)) @@ -312,6 +316,7 @@ private void bulkUpsert(List worlds, List players) { Updates.set("votes", world.votes()), Updates.set("whitelist_on_version_change", world.whitelist_on_version_change()), Updates.set("name", world.name()), + Updates.set("normalized_name", world.normalized_name()), Updates.set("description", world.description()), Updates.set("raw_name", Document.parse(world.raw_name())), Updates.set("raw_description", BsonArray.parse(world.raw_description())), diff --git a/src/client/java/net/legitimoose/bot/scraper/World.java b/src/client/java/net/legitimoose/bot/scraper/World.java index c9edbe3..e3ded5a 100644 --- a/src/client/java/net/legitimoose/bot/scraper/World.java +++ b/src/client/java/net/legitimoose/bot/scraper/World.java @@ -1,7 +1,6 @@ package net.legitimoose.bot.scraper; import com.google.gson.JsonObject; -import org.jetbrains.annotations.ApiStatus; public record World( String creation_date, @@ -21,6 +20,7 @@ public record World( int votes, boolean whitelist_on_version_change, String name, + String normalized_name, String description, String raw_name, String raw_description, diff --git a/src/client/java/net/legitimoose/bot/util/Unicode.java b/src/client/java/net/legitimoose/bot/util/Unicode.java new file mode 100644 index 0000000..38eb3c6 --- /dev/null +++ b/src/client/java/net/legitimoose/bot/util/Unicode.java @@ -0,0 +1,104 @@ +package net.legitimoose.bot.util; + +import java.util.Map; + +public class Unicode { + private static final Map CONFUSABLES = Map.ofEntries( + Map.entry(" ", " "), + Map.entry("0", "⓿"), + Map.entry("1", "11⓵➊⑴¹𝟏𝟙1𝟷𝟣⒈𝟭1➀₁①❶⥠"), + Map.entry("2", "⓶⒉⑵➋ƻ²ᒿ𝟚2𝟮𝟤ᒾ𝟸Ƨ𝟐②ᴤ₂➁❷ᘝƨ"), + Map.entry("3", "³ȝჳⳌꞫ𝟑ℨ𝟛𝟯𝟥Ꝫ➌ЗȜ⓷ӠƷ3𝟹⑶⒊ʒʓǯǮƺ𝕴ᶾзᦡ➂③₃ᶚᴣᴟ❸ҘҙӬӡӭӟӞ"), + Map.entry("4", "𝟰𝟺𝟦𝟒➍ҶᏎ𝟜ҷ⓸ҸҹӴӵᶣ4чㄩ⁴➃₄④❹Ӌ⑷⒋"), + Map.entry("5", "𝟱⓹➎Ƽ𝟓𝟻𝟝𝟧5➄₅⑤⁵❺ƽ⑸⒌"), + Map.entry("6", "Ⳓ🄇𝟼Ꮾ𝟲𝟞𝟨𝟔➏⓺Ϭϭ⁶б6ᧈ⑥➅₆❻⑹⒍"), + Map.entry("7", "𝟕𝟟𝟩𝟳𝟽🄈⓻𐓒➐7⁷⑦₇❼➆⑺⒎"), + Map.entry("8", "𐌚🄉➑⓼8𝟠𝟪৪⁸₈𝟴➇⑧❽𝟾𝟖⑻⒏"), + Map.entry("9", "൭Ꝯ𝝑𝞋𝟅🄊𝟡𝟵Ⳋ⓽➒੧৭୨9𝟫𝟿𝟗⁹₉Գ➈⑨❾⑼⒐"), + Map.entry("10", "⓾❿➉➓🔟⑩⑽⒑"), + Map.entry("11", "⑪⑾⒒⓫"), + Map.entry("12", "⑫⑿⒓⓬"), + Map.entry("13", "⑬⒀⒔⓭"), + Map.entry("14", "⑭⒁⒕⓮"), + Map.entry("15", "⑮⒂⒖⓯"), + Map.entry("16", "⑯⒃⒗⓰"), + Map.entry("17", "⑰⒄⒘⓱"), + Map.entry("18", "⑱⒅⒙⓲"), + Map.entry("19", "⑲⒆⒚⓳"), + Map.entry("20", "⑳⒇⒛⓴"), + Map.entry("ae", "æ"), + Map.entry("OE", "Œ"), + Map.entry("oe", "œ"), + Map.entry("pi", "ᒆ"), + Map.entry("Nj", "Nj"), + Map.entry("AE", "ᴁ"), + Map.entry("A", "𝑨𝔄ᗄ𝖠𝗔ꓯ𝞐🄐🄰Ꭿ𐊠𝕬𝜜𝐴ꓮᎪ𝚨ꭺ𝝖🅐Å∀🇦₳🅰𝒜𝘈𝐀𝔸дǺᗅⒶAΑᾋᗩĂÃÅǍȀȂĀȺĄʌΛλƛᴀᴬДАልÄₐᕱªǞӒΆẠẢẦẨẬẮẰẲẴẶᾸᾹᾺΆᾼᾈᾉᾊᾌᾍᾎᾏἈἉἊἋἌἍἎἏḀȦǠӐÀÁÂẤẪ𝛢𝓐𝙰𝘼ᗩ"), + Map.entry("a", "∂⍺ⓐձǟᵃᶏ⒜аɒaαȃȁคǎმäɑāɐąᾄẚạảǡầẵḁȧӑӓãåάὰάăẩằẳặᾀᾁᾂᾃᾅᾆᾰᾱᾲᾳᾴᶐᾶᾷἀἁἂἃἄἅἆἇᾇậắàáâấẫǻⱥ𝐚𝑎𝒂𝒶𝓪𝔞𝕒𝖆𝖺𝗮𝘢𝙖𝚊𝛂𝛼𝜶𝝰𝞪⍶"), + Map.entry("B", "🄑𝔙𝖁ꞵ𝛃𝛽𝜷𝝱𝞫Ᏸ𐌁𝑩𝕭🄱𐊡𝖡𝘽ꓐ𝗕𝘉𝜝𐊂𝚩𝐁𝛣𝝗𝐵𝙱𝔹Ᏼᏼ𝞑Ꞵ𝔅🅑฿𝓑ᗿᗾᗽ🅱ⒷBвϐᗷƁ乃ßცჩ๖βɮБՅ๒ᙖʙᴮᵇጌḄℬΒВẞḂḆɃദᗹᗸᵝᙞᙟᙝᛒᙗᙘᴃ🇧"), + Map.entry("b", "ꮟᏏ𝐛𝘣𝒷𝔟𝓫𝖇𝖻𝑏𝙗𝕓𝒃𝗯𝚋♭ᑳᒈbᖚᕹᕺⓑḃḅҍъḇƃɓƅᖯƄЬᑲþƂ⒝ЪᶀᑿᒀᒂᒁᑾьƀҌѢѣᔎ"), + Map.entry("C", "ꞆႠ℃🄒ᏟⲤ🄲ꓚ𐊢𐌂🅲𐐕🅒☾ČÇⒸCↃƇᑕㄈ¢८↻ĈϾՇȻᙅᶜ⒞ĆҀĊ©टƆℂℭϹС匚ḈҪʗᑖᑡᑢᑣᑤᑥⅭ𝐂𝐶𝑪𝒞𝓒𝕮𝖢𝗖𝘊𝘾ᔍ"), + Map.entry("c", "🝌cⅽ𝐜𝑐𝒄𝒸𝓬𝔠𝕔𝖈𝖼𝗰𝘤𝙘𝚌ᴄϲⲥсꮯ𐐽ⲥ𐐽ꮯĉcⓒćčċçҁƈḉȼↄсርᴄϲҫ꒝ςɽϛ𝙲ᑦ᧚𝐜𝑐𝒄𝒸𝓬𝔠𝕔𝖈𝖼𝗰𝘤𝙘𝚌₵🇨ᥴᒼⅽ"), + Map.entry("D", "🄓Ꭰ🄳𝔡𝖉𝔻𝗗𝘋𝙳𝐷𝓓𝐃𝑫𝕯𝖣𝔇𝘿ꭰⅅ𝒟ꓓ🅳🅓ⒹDƉᗪƊÐԺᴅᴰↁḊĐÞⅮᗞᑯĎḌḐḒḎᗫᗬᗟᗠᶛᴆ🇩"), + Map.entry("d", "Ꮷ𝔡𝖉ᑯꓒ𝓭ᵭ₫ԃⓓdḋďḍḑḓḏđƌɖɗᵈ⒟ԁⅾᶁԀᑺᑻᑼᑽᒄᑰᑱᶑ𝕕𝖽𝑑𝘥𝒅𝙙𝐝𝗱𝚍ⅆ𝒹ʠժ"), + Map.entry("E", "£ᙓ⋿∃ⴺꓱ𝐄𝐸𝔈𝕰𝖤𝘌𝙴𝛦𝜠ꭼ🄔🄴𝙀𝔼𐊆𝚬ꓰ𝝚𝞔𝓔𝑬𝗘🅴🅔ⒺΈEƎἝᕮƐモЄᴇᴱᵉÉ乇ЁɆꂅ€ÈℰΕЕⴹᎬĒĔĖĘĚÊËԐỀẾỄỂẼḔḖẺȄȆẸỆȨḜḘḚἘἙἚἛἜῈΈӖὲέЀϵ🇪"), + Map.entry("e", "əәⅇꬲꞓ⋴𝛆𝛜𝜀𝜖𝜺𝝐𝝴𝞊𝞮𝟄ⲉꮛ𐐩ꞒⲈ⍷𝑒𝓮𝕖𝖊𝘦𝗲𝚎𝙚𝒆𝔢𝖾𝐞Ҿҿⓔe⒠èᧉéᶒêɘἔềếễ૯ǝєεēҽɛểẽḕḗĕėëẻěȅȇẹệȩɇₑęḝḙḛ℮еԑѐӗᥱёἐἑἒἓἕℯ"), + Map.entry("F", "ᖵꘘꓞꟻᖷ𝐅𝐹𝑭𝔽𝕱𝖥𝗙𝙁𝙵𝟊℉🄕🄵𐊇𝔉𝘍𐊥ꓝꞘ🅵🅕𝓕ⒻFғҒᖴƑԲϝቻḞℱϜ₣🇫Ⅎ"), + Map.entry("f", "𝐟ᵮ𝑓𝒇𝒻𝓯𝔣𝕗𝖿𝗳𝙛𝚏ꬵꞙẝ𝖋ⓕfƒḟʃբᶠ⒡ſꊰʄ∱ᶂ𝘧"), + Map.entry("G", "𝗚𝘎🄖ꓖᏳ🄶Ꮐᏻ𝔾𝓖𝑮𝕲ꮐ𝒢𝙂𝖦𝙶𝔊𝐺𝐆🅶🅖ⒼGɢƓʛĢᘜᴳǴĠԌĜḠĞǦǤԍ₲🇬⅁"), + Map.entry("g", "ᶃᶢⓖgǵĝḡğġǧģց૭ǥɠﻭﻮᵍ⒢ℊɡᧁ𝐠𝑔𝒈𝓰𝔤𝕘𝖌𝗀𝗴𝘨𝙜𝚐"), + Map.entry("H", "Ἤ🄗𝆦🄷𝜢ꓧ𝘏𝐻𝝜𝖧𐋏𝗛ꮋℍᎻℌⲎ𝑯𝞖🅷🅗ዞǶԋⒽHĤᚺḢḦȞḤḨḪĦⱧҢңҤῊΉῌἨἩἪἫἭἮἯᾘᾙᾚᾛᾜᾝᾞᾟӉӈҥΉн卄♓𝓗ℋН𝐇𝙃𝙷ʜ𝛨Η𝚮ᕼӇᴴᵸ🇭"), + Map.entry("h", "ꞕ৸𝕳ꚕᏲℏӊԊꜧᏂҺ⒣ђⓗhĥḣḧȟḥḩḫẖħⱨհһከኩኪካɦℎ𝐡𝒉𝒽𝓱𝔥𝕙𝖍𝗁𝗵𝘩𝙝𝚑իʰᑋᗁɧんɥ"), + Map.entry("I", "ⲒἿ🄘🄸ЇꀤᏆ🅸🅘إﺇٳأﺃٲٵⒾI៸ÌÍÎĨĪĬİÏḮỈǏȈȊỊĮḬƗェエῘῙῚΊἸἹἺἻἼἽἾⅠΪΊɪᶦᑊᥣ𝛪𝐈𝙄𝙸𝓵𝐼ᴵ𝚰𝑰🇮"), + Map.entry("i", "⍳ℹⅈ𝑖𝒊𝒾ı𝚤ɩιιͺ𝛊𝜄𝜾𝞲ꙇӏꭵᎥⓘiìíîĩīĭïḯỉǐȉȋịḭῐῑῒΐῖῗἰἱἲⅰⅼ∣ⵏ│׀ا١۱ߊᛁἳἴἵɨіὶίᶖ𝔦𝚒𝝸𝗂𝐢𝕚𝖎𝗶𝘪𝙞ίⁱᵢ𝓲⒤"), + Map.entry("J", "𝐉𝐽𝑱𝒥𝓙𝔍𝕁𝕵𝖩𝗝𝘑𝙅𝙹ꞲͿꓙ🄙🄹🅹🅙ⒿJЈʝᒍנフĴʆวلյʖᴊᴶﻝጋɈⱼՂๅႱįᎫȷ丿ℐℑᒘᒙᒚᒛᒴᒵᒎᒏ🇯"), + Map.entry("j", "𝚥ꭻⅉⓙjϳʲ⒥ɉĵǰјڶᶨ𝒿𝘫𝗷𝑗𝙟𝔧𝒋𝗃𝓳𝕛𝚓𝖏𝐣"), + Map.entry("K", "𝐊ꝄꝀ𝐾𝑲𝓚𝕶𝖪𝙺𝚱𝝟🄚𝗞🄺𝜥𝘒ꓗ𝙆𝕂Ⲕ𝔎𝛫Ꮶ𝞙𝒦🅺🅚₭ⓀKĸḰќƘкҠκқҟӄʞҚКҡᴋᴷᵏ⒦ᛕЌጕḲΚKҜҝҞĶḴǨⱩϗӃ🇰"), + Map.entry("k", "ⓚꝁkḱǩḳķḵƙⱪᶄ𝐤𝘬𝗄𝕜𝜅𝜘𝜿𝝒𝝹𝞌𝞳𝙠𝚔𝑘𝒌ϰ𝛋𝛞𝟆𝗸𝓴𝓀"), + Map.entry("L", "𝐋𝐿𝔏𝕃𝕷𝖫𝗟𝘓𝙇ﴼ🄛🄻𐐛Ⳑ𝑳𝙻𐑃𝓛ⳑꮮᏞꓡ🅻🅛ﺈ└ⓁւLĿᒪ乚ՆʟꓶιԼᴸˡĹረḶₗΓլĻᄂⅬℒⱢᥧᥨᒻᒶᒷᶫﺎᒺᒹᒸᒫ⎳ㄥŁⱠﺄȽ🇱"), + Map.entry("l", "𝙡ⓛlŀĺľḷḹļӀℓḽḻłレɭƚɫⱡ|Ɩ⒧ʅǀוןΙІ|ᶩӏ𝓘𝕀𝖨𝗜𝘐𝐥𝑙𝒍𝓁𝔩𝕝𝖑𝗅𝗹𝘭𝚕𝜤𝝞ı𝚤ɩι𝛊𝜄𝜾𝞲"), + Map.entry("M", "ꮇ🄜🄼𐌑𐊰ꓟⲘᎷ🅼🅜ⓂMмṂ൱ᗰ州ᘻო๓♏ʍᙏᴍᴹᵐ⒨ḾМṀ௱ⅯℳΜϺᛖӍӎ𝐌𝑀𝑴𝓜𝔐𝕄𝕸𝖬𝗠𝘔𝙈𝙼𝚳𝛭𝜧𝝡𝞛🇲"), + Map.entry("m", "₥ᵯ𝖒𝐦𝗆𝔪𝕞𝓂ⓜmനᙢ൩ḿṁⅿϻṃጠɱ៳ᶆ𝙢𝓶𝚖𝑚𝗺᧕᧗"), + Map.entry("N", "𝇙𝇚𝇜🄝𝆧𝙉🄽ℕꓠ𝛮𝝢𝙽𝚴𝑵𝑁Ⲛ𝐍𝒩𝞜𝗡𝘕𝜨𝓝𝖭🅽₦🅝ЙЍⓃҋ៷NᴎɴƝᑎ几иՈռИהЛπᴺᶰŃ刀ክṄⁿÑПΝᴨոϖǸŇṆŅṊṈทŊӢӣӤӥћѝйᥢҊᴻ🇳"), + Map.entry("n", "ոռח𝒏𝓷𝙣𝑛𝖓𝔫𝗇𝚗𝗻ᥒⓝήnǹᴒńñᾗηṅňṇɲņṋṉղຖՌƞŋ⒩ภกɳпʼnлԉȠἠἡῃդᾐᾑᾒᾓᾔᾕᾖῄῆῇῂἢἣἤἥἦἧὴήበቡቢባቤብቦȵ𝛈𝜂𝜼𝝶𝞰𝕟𝘯𝐧𝓃ᶇᵰᥥ∩"), + Map.entry("O", "𝜽⭘🔿ꭴ⭕⏺🄁🄀Ꭴ𝚯𝚹𝛩𝛳𝜣𝜭𝝝𝝧𝞗𝞡ⴱᎾᏫ⍬𝞱𝝷𝛉𝟎𝜃θ𝟘𝑂𝑶𝓞𝔒𝕆𝕺𝗢𝘖𝙊𝛰㈇ꄲ🄞🔾🄾𐊒𝟬ꓳⲞ𐐄𐊫𐓂𝞞🅞⍥◯ⵁ⊖0⊝𝝤Ѳϴ𝚶𝜪ѺӦӨӪΌʘ𝐎ǑÒŎÓÔÕȌȎㇿ❍ⓄOὋロ❤૦⊕ØФԾΘƠᴼᵒ⒪ŐÖₒ¤◊Φ〇ΟОՕଠഠ௦סỒỐỖỔṌȬṎŌṐṒȮȰȪỎỜỚỠỞỢỌỘǪǬǾƟⵔ߀៰⍜⎔⎕⦰⦱⦲⦳⦴⦵⦶⦷⦸⦹⦺⦻⦼⦽⦾⦿⧀⧁⧂⧃ὈὉὊὌὍ"), + Map.entry("o", "ంಂംං૦௦۵ℴ𝑜𝒐𝖔ꬽ𝝄𝛔𝜎𝝈𝞂ჿ𝚘০୦ዐ𝛐𝗈𝞼ဝⲟ𝙤၀𐐬𝔬𐓪𝓸🇴⍤○ϙ🅾𝒪𝖮𝟢𝟶𝙾𝘰𝗼𝕠𝜊𝐨𝝾𝞸ᐤⓞѳ᧐ᥲðoఠᦞՓòөӧóºōôǒȏŏồốȍỗổõσṍȭṏὄṑṓȯȫ๏ᴏőöѻоዐǭȱ০୦٥౦೦൦๐໐οօᴑ०੦ỏơờớỡởợọộǫøǿɵծὀὁόὸόὂὃὅ"), + Map.entry("P", "🄟🄿ꓑ𝚸𝙿𝞠𝙋ꮲⲢ𝒫𝝦𝑃𝑷𝗣𝐏𐊕𝜬𝘗𝓟𝖯𝛲Ꮲ🅟Ҏ🅿ⓅPƤᑭ尸Ṗրφքᴘᴾᵖ⒫ṔアקРየᴩⱣℙΡῬᑸᑶᑷᑹᑬᑮ🇵₱"), + Map.entry("p", "ⲣҏ℗ⓟpṕṗƥᵽῥρрƿǷῤ⍴𝓹𝓅𝐩𝑝𝒑𝔭𝕡𝖕𝗉𝗽𝘱𝙥𝚙𝛒𝝆𝞺𝜌𝞀"), + Map.entry("Q", "🅀🄠Ꝗ🆀🅠ⓆQℚⵕԚ𝐐𝑄𝑸𝒬𝓠𝚀𝘘𝙌𝖰𝕼𝔔𝗤🇶"), + Map.entry("q", "𝓆ꝗ𝗾ⓠqգ⒬۹զᑫɋɊԛ𝗊𝑞𝘲𝕢𝚚𝒒𝖖𝐪𝔮𝓺𝙦"), + Map.entry("R", "℞🄡℟ꭱᏒ𐒴ꮢᎡꓣ🆁🅡ⓇRᴙȒʀᖇя尺ŔЯરƦᴿዪṚɌʁℛℜℝṘŘȐṜŖṞⱤ𝐑𝑅𝑹𝓡𝕽𝖱𝗥𝘙𝙍𝚁ᚱ🇷ᴚ"), + Map.entry("r", "𝚛ꭇᣴℾ𝚪𝛤𝜞𝝘𝞒ⲄГᎱᒥꭈⲅꮁⓡrŕṙřȑȓṛṝŗгՐɾᥬṟɍʳ⒭ɼѓᴦᶉ𝐫𝑟𝒓𝓇𝓻𝔯𝕣𝖗𝗋𝗿𝘳𝙧ᵲґᵣ"), + Map.entry("S", "🅂🄪🄢ꇙ𝓢𝗦Ꮪ𝒮Ꮥ𝚂𝐒ꓢ𝖲𝔖𝙎𐊖𝕾𐐠𝘚𝕊𝑆𝑺🆂🅢ⓈSṨŞֆՏȘˢ⒮ЅṠŠŚṤŜṦṢടᔕᔖᔢᔡᔣᔤ"), + Map.entry("s", "ᣵⓢꜱ𐑈ꮪsśṥŝṡšṧʂṣṩѕşșȿᶊక𝐬𝑠𝒔𝓈𝓼𝔰𝕤𝖘𝗌𝘀𝘴𝙨𝚜ގ🇸"), + Map.entry("T", "🅃🄣七ፒ𝜯🆃𐌕𝚻𝛵𝕋𝕿𝑻𐊱𐊗𝖳𝙏🝨𝝩𝞣𝚃𝘛𝑇ꓔ⟙𝐓Ⲧ𝗧⊤𝔗Ꭲꭲ𝒯🅣⏇⏉ⓉTтҬҭƬイŦԵτᴛᵀイፕϮŤ⊥ƮΤТ下ṪṬȚŢṰṮ丅丁ᐪ𝛕𝜏𝝉𝞃𝞽𝓣ㄒ🇹ጥ"), + Map.entry("t", "ⓣtṫẗťṭțȶ੮էʇ†ţṱṯƭŧᵗ⒯ʈեƫ𝐭𝑡𝒕𝓉𝓽𝔱𝕥𝖙𝗍𝘁𝘵𝙩𝚝ナ"), + Map.entry("U", "🅄Џ🄤ሀꓴ𐓎꒤🆄🅤ŨŬŮᑗᑘǓǕǗǙⓊUȖᑌ凵ƱմԱꓵЦŪՄƲᙀᵁᵘ⒰ŰપÜՍÙÚÛṸṺǛỦȔƯỪỨỮỬỰỤṲŲṶṴɄᥩᑧ∪ᘮ⋃𝐔𝑈𝑼𝒰𝓤𝔘𝕌𝖀𝖴𝗨𝘜𝙐𝚄🇺"), + Map.entry("u", "𝘂𝘶𝙪𝚞ꞟꭎꭒ𝛖𝜐𝝊𝞄𝞾𐓶ὺύⓤuùũūừṷṹŭǖữᥙǚǜὗυΰนսʊǘǔúůᴜűųยûṻцሁüᵾᵤµʋủȕȗưứửựụṳṵʉῠῡῢΰῦῧὐὑϋύὒὓὔὕὖᥔ𝐮𝑢𝒖𝓊𝓾𝔲𝕦𝖚𝗎ᶙ"), + Map.entry("V", "𝑉𝒱𝕍𝗩🄥🅅ꓦ𝑽𝖵𝘝Ꮩ𝚅𝙑𝐕🆅🅥ⓋVᐯѴᵛ⒱۷ṾⅴⅤṼ٧ⴸѶᐺᐻ🇻𝓥"), + Map.entry("v", "∨⌄⋁ⅴ𝐯𝑣𝒗𝓋𝔳𝕧𝖛𝗏ꮩሀⓥv𝜐𝝊ṽṿ౮งѵעᴠνטᵥѷ៴ᘁ𝙫𝚟𝛎𝜈𝝂𝝼𝞶𝘷𝘃𝓿"), + Map.entry("W", "𝐖𝑊𝓦𝔚𝕎𝖂𝖶𝗪𝙒𝚆🄦🅆ᏔᎳ𝑾ꓪ𝒲𝘞🆆Ⓦ🅦wWẂᾧᗯᥕ山ѠຟచաЩШώщฬшᙎᵂʷ⒲ฝሠẄԜẀŴẆẈധᘺѿᙡƜ₩🇼"), + Map.entry("w", "𝐰ꝡ𝑤𝒘𝓌𝔀𝔴𝕨𝖜𝗐𝘄𝘸𝙬𝚠աẁꮃẃⓦ⍵ŵẇẅẘẉⱳὼὠὡὢὣωὤὥὦὧῲῳῴῶῷⱲѡԝᴡώᾠᾡᾢᾣᾤᾥᾦɯ𝝕𝟉𝞏"), + Map.entry("X", "ꭓꭕ𝛘𝜒𝝌𝞆𝟀ⲭ🞨𝑿𝛸🄧🞩🞪🅇🞫🞬𐌗Ⲭꓫ𝖃𝞦𝘟𐊐𝚾𝝬𝜲Ꭓ𐌢𝖷𝑋𝕏𝔛𐊴𝗫🆇🅧❌Ⓧ𝓧XẊ᙭χㄨ𝒳ӾჯӼҳЖΧҲᵡˣ⒳אሸẌꊼⅩХ╳᙮ᕁᕽⅹᚷⵝ𝙓𝚇乂𝐗🇽"), + Map.entry("x", "᙮ⅹ𝑥𝒙𝓍𝔵𝕩𝖝𝗑𝘅ᕁᕽⓧxхẋ×ₓ⤫⤬⨯ẍᶍ𝙭ӽ𝘹𝐱𝚡⨰メ𝔁"), + Map.entry("Y", "𝒴🄨𝓨𝔜𝖄𝖸𝘠𝙔𝚼𝛶𝝪𝞤УᎩᎽⲨ𝚈𝑌𝗬𝐘ꓬ𝒀𝜰𐊲🆈🅨ⓎYὛƳㄚʏ⅄ϔ¥¥ՎϓγץӲЧЎሃŸɎϤΥϒҮỲÝŶỸȲẎỶỴῨῩῪΎὙὝὟΫΎӮӰҰұ𝕐🇾"), + Map.entry("y", "𝐲𝑦𝒚𝓎𝔂𝔶𝕪𝖞𝗒𝘆𝘺𝙮𝚢ʏỿꭚγℽ𝛄𝛾𝜸𝝲𝞬🅈ᎽᎩⓨyỳýŷỹȳẏÿỷуყẙỵƴɏᵞɣʸᶌү⒴ӳӱӯўУʎ"), + Map.entry("Z", "🄩🅉ꓜ𝗭𝐙☡Ꮓ𝘡🆉🅩ⓏZẔƵ乙ẐȤᶻ⒵ŹℤΖŻŽẒⱫ🇿"), + Map.entry("z", "𝑍𝒁𝒵𝓩𝖹𝙕𝚉𝚭𝛧𝜡𝝛𝞕ᵶꮓ𝐳𝑧𝒛𝓏𝔃𝔷𝕫𝖟𝗓𝘇𝘻𝙯𝚣ⓩzźẑżžẓẕƶȥɀᴢጊʐⱬᶎʑᙆ") + ); + + public static String normalize(String orig) { + StringBuilder normalized = new StringBuilder(); + for (char c : orig.toCharArray()) { + String cStr = Character.toString(c); + normalized.append( + CONFUSABLES + .entrySet() + .stream() + .filter(e -> e.getValue().contains(cStr)) + .map(Map.Entry::getKey) + .findFirst().orElse(cStr) + ); + } + return normalized.toString(); + } +} From 4be0057701a0da9717cf77e3804cc949bc4b3c84 Mon Sep 17 00:00:00 2001 From: Omri H Date: Tue, 21 Apr 2026 12:49:00 +0300 Subject: [PATCH 07/18] chore: bump version to 3.0.0-alpha.4 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 6cf3f93..2fe1407 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ org.gradle.configuration-cache=false minecraft_version=1.21.11 loader_version=0.18.4 # Mod Properties -mod_version=3.0.0-alpha.3 +mod_version=3.0.0-alpha.4 maven_group=net.legitimoose archives_base_name=Legitimoose-Bot # Dependencies From a79311e69445ea317dd364966ca44f1685084e0c Mon Sep 17 00:00:00 2001 From: Omri H Date: Mon, 25 May 2026 19:26:35 +0300 Subject: [PATCH 08/18] feat: scrape lobby to api --- .../net/legitimoose/bot/scraper/Scraper.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/client/java/net/legitimoose/bot/scraper/Scraper.java b/src/client/java/net/legitimoose/bot/scraper/Scraper.java index b0165ea..6446b7b 100644 --- a/src/client/java/net/legitimoose/bot/scraper/Scraper.java +++ b/src/client/java/net/legitimoose/bot/scraper/Scraper.java @@ -132,6 +132,52 @@ public void scrape() throws IOException, URISyntaxException, InterruptedExceptio client.player.closeContainer(); + + World lobby = new World( + "Nov 28, 2023, 4:57 PM", + 1701219420, + + false, + true, + + "5f4641cb-a718-4556-8c87-fbf153a8cc9a", + "Legitermoose", + Rank.Moose, + + Minecraft.getInstance().getConnection().getOnlinePlayers().size(), + + 100, + 100, + + "", + "lobby", + + // Change on moose/bot update + "1.21.10", + + 300000, + 300000, + + false, + + "Legitimoose Lobby", + Unicode.normalize("Legitimoose Lobby"), + "The legitimoose.com lobby.", + + "{\"text\":\"Legitimoose Lobby\",\"color\":\"white\",\"italic\":false}", + "[{\"text\":\"The legitimoose.com lobby.\",\"color\":\"white\",\"italic\":false}]", + + -1, + + new JsonObject(), + + "minecraft:grass_block", + + System.currentTimeMillis() / 1000L, + System.currentTimeMillis() + ); + bulkUpsert(List.of(lobby), List.of()); + client.player.connection.sendCommand("worlds"); waitSeconds(1); @@ -285,6 +331,7 @@ public void scrape() throws IOException, URISyntaxException, InterruptedExceptio System.currentTimeMillis() / 1000L, System.currentTimeMillis() ); + worlds.add(world); LOGGER.info("Scraped World {} {}: {}", j, world.world_uuid(), world.name()); } From 6cc498d90b325e03d7d5ae1daeddd1348aea5aec Mon Sep 17 00:00:00 2001 From: Omri H Date: Mon, 25 May 2026 19:26:56 +0300 Subject: [PATCH 09/18] chore: bump version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 2fe1407..5a4dfc1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ org.gradle.configuration-cache=false minecraft_version=1.21.11 loader_version=0.18.4 # Mod Properties -mod_version=3.0.0-alpha.4 +mod_version=3.0.0-alpha.5 maven_group=net.legitimoose archives_base_name=Legitimoose-Bot # Dependencies From 1d7a905923740d9b3a08fb6f1a7595594f7afeca Mon Sep 17 00:00:00 2001 From: Omri H Date: Mon, 25 May 2026 21:56:48 +0300 Subject: [PATCH 10/18] feat: scrape lobby description from server motd --- gradle.properties | 2 +- .../java/net/legitimoose/bot/LegitimooseBotClient.java | 2 +- src/client/java/net/legitimoose/bot/scraper/Scraper.java | 9 +++++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/gradle.properties b/gradle.properties index 5a4dfc1..77b2493 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ org.gradle.configuration-cache=false minecraft_version=1.21.11 loader_version=0.18.4 # Mod Properties -mod_version=3.0.0-alpha.5 +mod_version=3.0.0-alpha.6 maven_group=net.legitimoose archives_base_name=Legitimoose-Bot # Dependencies diff --git a/src/client/java/net/legitimoose/bot/LegitimooseBotClient.java b/src/client/java/net/legitimoose/bot/LegitimooseBotClient.java index f0c53a9..ffdd00d 100644 --- a/src/client/java/net/legitimoose/bot/LegitimooseBotClient.java +++ b/src/client/java/net/legitimoose/bot/LegitimooseBotClient.java @@ -142,7 +142,7 @@ private static void rejoin() { if ((now - lastJoinTimestamp) >= REJOIN_COOLDOWN_MS) { lastJoinTimestamp = now; LOGGER.info("Attempting to reconnect to server"); - ServerData info = new ServerData("Server", "legitimoose.com", ServerData.Type.OTHER); + ServerData info = new ServerData("Legitimoose", "legitimoose.com", ServerData.Type.OTHER); ConnectScreen.startConnecting( new JoinMultiplayerScreen(null), Minecraft.getInstance(), diff --git a/src/client/java/net/legitimoose/bot/scraper/Scraper.java b/src/client/java/net/legitimoose/bot/scraper/Scraper.java index 6446b7b..a91ba55 100644 --- a/src/client/java/net/legitimoose/bot/scraper/Scraper.java +++ b/src/client/java/net/legitimoose/bot/scraper/Scraper.java @@ -132,6 +132,8 @@ public void scrape() throws IOException, URISyntaxException, InterruptedExceptio client.player.closeContainer(); + String lobbyRawDescription = "["; + World lobby = new World( "Nov 28, 2023, 4:57 PM", @@ -162,10 +164,13 @@ public void scrape() throws IOException, URISyntaxException, InterruptedExceptio "Legitimoose Lobby", Unicode.normalize("Legitimoose Lobby"), - "The legitimoose.com lobby.", + Minecraft.getInstance().getCurrentServer().motd.getString(), "{\"text\":\"Legitimoose Lobby\",\"color\":\"white\",\"italic\":false}", - "[{\"text\":\"The legitimoose.com lobby.\",\"color\":\"white\",\"italic\":false}]", + lobbyRawDescription + ComponentSerialization.CODEC.encodeStart(JsonOps.INSTANCE, Minecraft.getInstance().getCurrentServer().motd) + .result() + .get() + .toString() + "]", -1, From 513d7bf6f5842cb664b931a03e880772b70761b0 Mon Sep 17 00:00:00 2001 From: Omri H Date: Sun, 31 May 2026 21:09:35 +0300 Subject: [PATCH 11/18] feat: deleted field --- gradle.properties | 2 +- .../net/legitimoose/bot/scraper/Database.java | 21 +++++++---- .../net/legitimoose/bot/scraper/Scraper.java | 21 +++++++---- .../net/legitimoose/bot/scraper/World.java | 4 ++- .../legitimoose/bot/util/JsonObjectCodec.java | 36 +++++++++++++++++++ 5 files changed, 69 insertions(+), 15 deletions(-) create mode 100644 src/client/java/net/legitimoose/bot/util/JsonObjectCodec.java diff --git a/gradle.properties b/gradle.properties index 01474d7..ee48a37 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ org.gradle.configuration-cache=false minecraft_version=26.1.2 loader_version=0.19.2 # Mod Properties -mod_version=3.0.0-alpha.6 +mod_version=3.0.0-alpha.7 maven_group=net.legitimoose archives_base_name=Legitimoose-Bot # Dependencies diff --git a/src/client/java/net/legitimoose/bot/scraper/Database.java b/src/client/java/net/legitimoose/bot/scraper/Database.java index 1c0a352..da3dcaf 100644 --- a/src/client/java/net/legitimoose/bot/scraper/Database.java +++ b/src/client/java/net/legitimoose/bot/scraper/Database.java @@ -1,11 +1,16 @@ package net.legitimoose.bot.scraper; -import static net.legitimoose.bot.LegitimooseBot.CONFIG; +import com.mongodb.MongoClientSettings; import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClients; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; +import net.legitimoose.bot.util.JsonObjectCodec; import org.bson.Document; +import org.bson.codecs.configuration.CodecRegistries; +import org.bson.codecs.configuration.CodecRegistry; + +import static net.legitimoose.bot.LegitimooseBot.CONFIG; public class Database { @@ -19,13 +24,17 @@ public class Database { private final MongoDatabase database = mongoClient.getDatabase(DATABASE_NAME); - private MongoCollection worlds; - private MongoCollection players; - private MongoCollection stats; - private MongoCollection bans; + private final MongoCollection worlds; + private final MongoCollection players; + private final MongoCollection stats; + private final MongoCollection bans; private Database() { - worlds = database.getCollection("worlds", World.class); + CodecRegistry codecRegistry = CodecRegistries.fromRegistries( + CodecRegistries.fromCodecs(new JsonObjectCodec()), + MongoClientSettings.getDefaultCodecRegistry()); + + worlds = database.getCollection("worlds", World.class).withCodecRegistry(codecRegistry); players = database.getCollection("players", Player.class); stats = database.getCollection("stats"); bans = database.getCollection("bans", Ban.class); diff --git a/src/client/java/net/legitimoose/bot/scraper/Scraper.java b/src/client/java/net/legitimoose/bot/scraper/Scraper.java index ebf9274..1f8f281 100644 --- a/src/client/java/net/legitimoose/bot/scraper/Scraper.java +++ b/src/client/java/net/legitimoose/bot/scraper/Scraper.java @@ -97,7 +97,6 @@ public void scrape() throws IOException, URISyntaxException, InterruptedExceptio 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))); Database.getWorlds().createIndexes(indexes); // Please ignore the nulls. Only the 'input' is actually used @@ -169,8 +168,7 @@ public void scrape() throws IOException, URISyntaxException, InterruptedExceptio "{\"text\":\"Legitimoose Lobby\",\"color\":\"white\",\"italic\":false}", lobbyRawDescription + ComponentSerialization.CODEC.encodeStart(JsonOps.INSTANCE, Minecraft.getInstance().getCurrentServer().motd) .result() - .get() - .toString() + "]", + .get() + "]", -1, @@ -179,7 +177,8 @@ public void scrape() throws IOException, URISyntaxException, InterruptedExceptio "minecraft:grass_block", System.currentTimeMillis() / 1000L, - System.currentTimeMillis() + System.currentTimeMillis(), + false ); bulkUpsert(List.of(lobby), List.of()); @@ -334,7 +333,8 @@ public void scrape() throws IOException, URISyntaxException, InterruptedExceptio itemStack.toString().substring(2), System.currentTimeMillis() / 1000L, - System.currentTimeMillis() + System.currentTimeMillis(), + false ); worlds.add(world); @@ -359,6 +359,12 @@ private void bulkUpsert(List worlds, List players) { List> playerOperations = new ArrayList<>(); LOGGER.info("writing world"); for (World world : worlds) { + // TODO: REMOVE TTL INDEX ON RELEASE + boolean deleted = false; + World worldPrev = Database.getWorlds().find(eq("world_uuid", world.world_uuid())).first(); + if (worldPrev != null && System.currentTimeMillis() - worldPrev.last_scraped_ms() > TimeUnit.HOURS.toMillis(24)) { + deleted = true; + } Bson updates = Updates.combine( Updates.set("creation_date", world.creation_date()), @@ -382,10 +388,11 @@ private void bulkUpsert(List worlds, List players) { Updates.set("raw_name", Document.parse(world.raw_name())), Updates.set("raw_description", BsonArray.parse(world.raw_description())), Updates.set("featured_instant", world.featured_instant()), - Updates.set("jam", Document.parse(world.jam().toString())), + Updates.set("jam", world.jam()), Updates.set("icon", world.icon()), Updates.set("last_scraped", world.last_scraped()), - Updates.set("last_scraped_ms", new BsonDateTime(world.last_scraped_ms()))); + Updates.set("last_scraped_ms", new BsonDateTime(world.last_scraped_ms())), + Updates.set("deleted", deleted)); operations.add(new UpdateOneModel<>( eq("world_uuid", world.world_uuid()), updates, diff --git a/src/client/java/net/legitimoose/bot/scraper/World.java b/src/client/java/net/legitimoose/bot/scraper/World.java index e3ded5a..1d55973 100644 --- a/src/client/java/net/legitimoose/bot/scraper/World.java +++ b/src/client/java/net/legitimoose/bot/scraper/World.java @@ -33,6 +33,8 @@ public record World( @Deprecated long last_scraped, - long last_scraped_ms + long last_scraped_ms, + + boolean deleted ) { } diff --git a/src/client/java/net/legitimoose/bot/util/JsonObjectCodec.java b/src/client/java/net/legitimoose/bot/util/JsonObjectCodec.java new file mode 100644 index 0000000..a514cc9 --- /dev/null +++ b/src/client/java/net/legitimoose/bot/util/JsonObjectCodec.java @@ -0,0 +1,36 @@ +package net.legitimoose.bot.util; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.bson.BsonReader; +import org.bson.BsonWriter; +import org.bson.Document; +import org.bson.codecs.Codec; +import org.bson.codecs.DecoderContext; +import org.bson.codecs.DocumentCodec; +import org.bson.codecs.EncoderContext; + +public class JsonObjectCodec implements Codec { + private static final DocumentCodec DOCUMENT_CODEC = new DocumentCodec(); + + @Override + public void encode(BsonWriter writer, + JsonObject value, + EncoderContext encoderContext) { + Document document = Document.parse(value.toString()); + + DOCUMENT_CODEC.encode(writer, document, encoderContext); + } + + @Override + public JsonObject decode(BsonReader reader, + DecoderContext decoderContext) { + Document doc = DOCUMENT_CODEC.decode(reader, decoderContext); + return JsonParser.parseString(doc.toJson()).getAsJsonObject(); + } + + @Override + public Class getEncoderClass() { + return JsonObject.class; + } +} \ No newline at end of file From a47a8a22711db0e4cc60501a2e2883e4e71e9814 Mon Sep 17 00:00:00 2001 From: Omri H Date: Sun, 31 May 2026 21:12:55 +0300 Subject: [PATCH 12/18] fix: remove previous TTL index for deleting >24h --- src/client/java/net/legitimoose/bot/scraper/Scraper.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/client/java/net/legitimoose/bot/scraper/Scraper.java b/src/client/java/net/legitimoose/bot/scraper/Scraper.java index 1f8f281..e7c7481 100644 --- a/src/client/java/net/legitimoose/bot/scraper/Scraper.java +++ b/src/client/java/net/legitimoose/bot/scraper/Scraper.java @@ -95,6 +95,8 @@ public void scrape() throws IOException, URISyntaxException, InterruptedExceptio Minecraft client = Minecraft.getInstance(); MongoCollection stats = Database.getStats(); stats.createIndex(Indexes.descending("timestamp")); + + Database.getWorlds().dropIndex(new IndexModel(Indexes.ascending("last_scraped_ms"), new IndexOptions().expireAfter(24L, TimeUnit.HOURS)).getKeys()); List indexes = new ArrayList<>(); indexes.add(new IndexModel(Indexes.ascending("world_uuid"))); Database.getWorlds().createIndexes(indexes); @@ -359,7 +361,6 @@ private void bulkUpsert(List worlds, List players) { List> playerOperations = new ArrayList<>(); LOGGER.info("writing world"); for (World world : worlds) { - // TODO: REMOVE TTL INDEX ON RELEASE boolean deleted = false; World worldPrev = Database.getWorlds().find(eq("world_uuid", world.world_uuid())).first(); if (worldPrev != null && System.currentTimeMillis() - worldPrev.last_scraped_ms() > TimeUnit.HOURS.toMillis(24)) { From 774c2e6951ad04f226b5c17df37b7f30f3d7fc17 Mon Sep 17 00:00:00 2001 From: Omri H Date: Thu, 18 Jun 2026 21:32:47 +0300 Subject: [PATCH 13/18] fuck you (mongodb is being a lil bitch) --- .../net/legitimoose/bot/scraper/Database.java | 6 +--- .../net/legitimoose/bot/scraper/Scraper.java | 31 ++++++++++++++----- .../net/legitimoose/bot/scraper/World.java | 6 ++-- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/client/java/net/legitimoose/bot/scraper/Database.java b/src/client/java/net/legitimoose/bot/scraper/Database.java index cbc204d..386a815 100644 --- a/src/client/java/net/legitimoose/bot/scraper/Database.java +++ b/src/client/java/net/legitimoose/bot/scraper/Database.java @@ -31,11 +31,7 @@ public class Database { private final MongoCollection bans; private Database() { - CodecRegistry codecRegistry = CodecRegistries.fromRegistries( - CodecRegistries.fromCodecs(new JsonObjectCodec()), - MongoClientSettings.getDefaultCodecRegistry()); - - worlds = database.getCollection("worlds", World.class).withCodecRegistry(codecRegistry); + worlds = database.getCollection("worlds", World.class); worldStats = database.getCollection("world_stats"); players = database.getCollection("players", Player.class); stats = database.getCollection("stats"); diff --git a/src/client/java/net/legitimoose/bot/scraper/Scraper.java b/src/client/java/net/legitimoose/bot/scraper/Scraper.java index 00b3fbb..a058e18 100644 --- a/src/client/java/net/legitimoose/bot/scraper/Scraper.java +++ b/src/client/java/net/legitimoose/bot/scraper/Scraper.java @@ -12,6 +12,7 @@ import net.legitimoose.bot.util.DiscordWebhook; import net.legitimoose.bot.util.Unicode; import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.PlayerInfo; import net.minecraft.core.component.DataComponents; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; @@ -19,6 +20,7 @@ import net.minecraft.world.Container; import net.minecraft.world.inventory.ContainerInput; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.scores.*; import org.bson.BsonArray; import org.bson.BsonDateTime; import org.bson.Document; @@ -28,6 +30,7 @@ import java.net.URISyntaxException; import java.time.Instant; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; @@ -132,6 +135,18 @@ public void scrape() throws IOException, URISyntaxException, InterruptedExceptio ); }); + Collection playerList = + Minecraft.getInstance().getConnection().getOnlinePlayers(); + Scoreboard scoreboard = Minecraft.getInstance().level.getScoreboard(); + Objective listObjective = scoreboard.getDisplayObjective(DisplaySlot.LIST); + for (PlayerInfo player : playerList) { + ReadOnlyScoreInfo score = scoreboard.getPlayerScoreInfo(ScoreHolder.fromGameProfile(player.getProfile()), listObjective); + if (score == null) continue; + int legiticoins = score.value(); + Database.getPlayers().updateOne(eq("name", player.getProfile().name()), Updates.set("legiticoins", legiticoins)); + } + + client.player.closeContainer(); String lobbyRawDescription = "["; @@ -175,12 +190,12 @@ public void scrape() throws IOException, URISyntaxException, InterruptedExceptio -1, - new JsonObject(), + "", "minecraft:grass_block", System.currentTimeMillis() / 1000L, - System.currentTimeMillis(), + Instant.now(), false ); bulkUpsert(List.of(lobby), List.of()); @@ -334,11 +349,11 @@ public void scrape() throws IOException, URISyntaxException, InterruptedExceptio featured_instant, - jam, + jam.toString(), itemStack.toString().substring(2), System.currentTimeMillis() / 1000L, - System.currentTimeMillis(), + Instant.now(), false ); @@ -366,7 +381,7 @@ private void bulkUpsert(List worlds, List players) { for (World world : worlds) { boolean deleted = false; World worldPrev = Database.getWorlds().find(eq("world_uuid", world.world_uuid())).first(); - if (worldPrev != null && System.currentTimeMillis() - worldPrev.last_scraped_ms() > TimeUnit.HOURS.toMillis(24)) { + if (worldPrev != null && System.currentTimeMillis() - worldPrev.last_scraped_ms().toEpochMilli() > TimeUnit.HOURS.toMillis(24)) { deleted = true; } @@ -405,10 +420,10 @@ private void bulkUpsert(List worlds, List players) { Updates.set("raw_name", Document.parse(world.raw_name())), Updates.set("raw_description", BsonArray.parse(world.raw_description())), Updates.set("featured_instant", world.featured_instant()), - Updates.set("jam", world.jam()), + Updates.set("jam", Document.parse(world.jam())), Updates.set("icon", world.icon()), Updates.set("last_scraped", world.last_scraped()), - Updates.set("last_scraped_ms", new BsonDateTime(world.last_scraped_ms())), + Updates.set("last_scraped_ms", world.last_scraped_ms()), Updates.set("deleted", deleted)); operations.add(new UpdateOneModel<>( eq("world_uuid", world.world_uuid()), @@ -446,7 +461,7 @@ private void bulkUpsert(List worlds, List players) { } private void writeWorldStats(World world, JsonObject statsObj) { - statsObj.addProperty("timestamp", world.last_scraped_ms()); + statsObj.addProperty("timestamp", world.last_scraped_ms().toEpochMilli()); statsObj.addProperty("visits", world.visits()); statsObj.addProperty("votes", world.votes()); diff --git a/src/client/java/net/legitimoose/bot/scraper/World.java b/src/client/java/net/legitimoose/bot/scraper/World.java index 1d55973..2954734 100644 --- a/src/client/java/net/legitimoose/bot/scraper/World.java +++ b/src/client/java/net/legitimoose/bot/scraper/World.java @@ -2,6 +2,8 @@ import com.google.gson.JsonObject; +import java.time.Instant; + public record World( String creation_date, int creation_date_unix_seconds, @@ -26,14 +28,14 @@ public record World( String raw_description, int featured_instant, - JsonObject jam, + String jam, String icon, @Deprecated long last_scraped, - long last_scraped_ms, + Instant last_scraped_ms, boolean deleted ) { From bd81b99fa665a3e2898ad35e4493fc48a6455192 Mon Sep 17 00:00:00 2001 From: Omri Date: Tue, 25 Aug 2026 19:34:45 +0300 Subject: [PATCH 14/18] Revert "fuck you (mongodb is being a lil bitch)" This reverts commit 774c2e6951ad04f226b5c17df37b7f30f3d7fc17. --- .../net/legitimoose/bot/scraper/Database.java | 6 +++- .../net/legitimoose/bot/scraper/Scraper.java | 31 +++++-------------- .../net/legitimoose/bot/scraper/World.java | 6 ++-- 3 files changed, 15 insertions(+), 28 deletions(-) diff --git a/src/client/java/net/legitimoose/bot/scraper/Database.java b/src/client/java/net/legitimoose/bot/scraper/Database.java index 386a815..cbc204d 100644 --- a/src/client/java/net/legitimoose/bot/scraper/Database.java +++ b/src/client/java/net/legitimoose/bot/scraper/Database.java @@ -31,7 +31,11 @@ public class Database { private final MongoCollection bans; private Database() { - worlds = database.getCollection("worlds", World.class); + CodecRegistry codecRegistry = CodecRegistries.fromRegistries( + CodecRegistries.fromCodecs(new JsonObjectCodec()), + MongoClientSettings.getDefaultCodecRegistry()); + + worlds = database.getCollection("worlds", World.class).withCodecRegistry(codecRegistry); worldStats = database.getCollection("world_stats"); players = database.getCollection("players", Player.class); stats = database.getCollection("stats"); diff --git a/src/client/java/net/legitimoose/bot/scraper/Scraper.java b/src/client/java/net/legitimoose/bot/scraper/Scraper.java index a058e18..00b3fbb 100644 --- a/src/client/java/net/legitimoose/bot/scraper/Scraper.java +++ b/src/client/java/net/legitimoose/bot/scraper/Scraper.java @@ -12,7 +12,6 @@ import net.legitimoose.bot.util.DiscordWebhook; import net.legitimoose.bot.util.Unicode; import net.minecraft.client.Minecraft; -import net.minecraft.client.multiplayer.PlayerInfo; import net.minecraft.core.component.DataComponents; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; @@ -20,7 +19,6 @@ import net.minecraft.world.Container; import net.minecraft.world.inventory.ContainerInput; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.scores.*; import org.bson.BsonArray; import org.bson.BsonDateTime; import org.bson.Document; @@ -30,7 +28,6 @@ import java.net.URISyntaxException; import java.time.Instant; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; @@ -135,18 +132,6 @@ public void scrape() throws IOException, URISyntaxException, InterruptedExceptio ); }); - Collection playerList = - Minecraft.getInstance().getConnection().getOnlinePlayers(); - Scoreboard scoreboard = Minecraft.getInstance().level.getScoreboard(); - Objective listObjective = scoreboard.getDisplayObjective(DisplaySlot.LIST); - for (PlayerInfo player : playerList) { - ReadOnlyScoreInfo score = scoreboard.getPlayerScoreInfo(ScoreHolder.fromGameProfile(player.getProfile()), listObjective); - if (score == null) continue; - int legiticoins = score.value(); - Database.getPlayers().updateOne(eq("name", player.getProfile().name()), Updates.set("legiticoins", legiticoins)); - } - - client.player.closeContainer(); String lobbyRawDescription = "["; @@ -190,12 +175,12 @@ public void scrape() throws IOException, URISyntaxException, InterruptedExceptio -1, - "", + new JsonObject(), "minecraft:grass_block", System.currentTimeMillis() / 1000L, - Instant.now(), + System.currentTimeMillis(), false ); bulkUpsert(List.of(lobby), List.of()); @@ -349,11 +334,11 @@ public void scrape() throws IOException, URISyntaxException, InterruptedExceptio featured_instant, - jam.toString(), + jam, itemStack.toString().substring(2), System.currentTimeMillis() / 1000L, - Instant.now(), + System.currentTimeMillis(), false ); @@ -381,7 +366,7 @@ private void bulkUpsert(List worlds, List players) { for (World world : worlds) { boolean deleted = false; World worldPrev = Database.getWorlds().find(eq("world_uuid", world.world_uuid())).first(); - if (worldPrev != null && System.currentTimeMillis() - worldPrev.last_scraped_ms().toEpochMilli() > TimeUnit.HOURS.toMillis(24)) { + if (worldPrev != null && System.currentTimeMillis() - worldPrev.last_scraped_ms() > TimeUnit.HOURS.toMillis(24)) { deleted = true; } @@ -420,10 +405,10 @@ private void bulkUpsert(List worlds, List players) { Updates.set("raw_name", Document.parse(world.raw_name())), Updates.set("raw_description", BsonArray.parse(world.raw_description())), Updates.set("featured_instant", world.featured_instant()), - Updates.set("jam", Document.parse(world.jam())), + Updates.set("jam", world.jam()), Updates.set("icon", world.icon()), Updates.set("last_scraped", world.last_scraped()), - Updates.set("last_scraped_ms", world.last_scraped_ms()), + Updates.set("last_scraped_ms", new BsonDateTime(world.last_scraped_ms())), Updates.set("deleted", deleted)); operations.add(new UpdateOneModel<>( eq("world_uuid", world.world_uuid()), @@ -461,7 +446,7 @@ private void bulkUpsert(List worlds, List players) { } private void writeWorldStats(World world, JsonObject statsObj) { - statsObj.addProperty("timestamp", world.last_scraped_ms().toEpochMilli()); + statsObj.addProperty("timestamp", world.last_scraped_ms()); statsObj.addProperty("visits", world.visits()); statsObj.addProperty("votes", world.votes()); diff --git a/src/client/java/net/legitimoose/bot/scraper/World.java b/src/client/java/net/legitimoose/bot/scraper/World.java index 2954734..1d55973 100644 --- a/src/client/java/net/legitimoose/bot/scraper/World.java +++ b/src/client/java/net/legitimoose/bot/scraper/World.java @@ -2,8 +2,6 @@ import com.google.gson.JsonObject; -import java.time.Instant; - public record World( String creation_date, int creation_date_unix_seconds, @@ -28,14 +26,14 @@ public record World( String raw_description, int featured_instant, - String jam, + JsonObject jam, String icon, @Deprecated long last_scraped, - Instant last_scraped_ms, + long last_scraped_ms, boolean deleted ) { From b8e1d2f8bd5a3f347d077284f25bb9cbc23ef1a8 Mon Sep 17 00:00:00 2001 From: Omri Date: Tue, 25 Aug 2026 19:47:41 +0300 Subject: [PATCH 15/18] fix: write legiticoins for scraped players --- .../net/legitimoose/bot/scraper/Scraper.java | 1 + .../legitimoose/bot/util/JsonObjectCodec.java | 36 ------------------- 2 files changed, 1 insertion(+), 36 deletions(-) delete mode 100644 src/client/java/net/legitimoose/bot/util/JsonObjectCodec.java diff --git a/src/client/java/net/legitimoose/bot/scraper/Scraper.java b/src/client/java/net/legitimoose/bot/scraper/Scraper.java index 82d4c83..663766d 100644 --- a/src/client/java/net/legitimoose/bot/scraper/Scraper.java +++ b/src/client/java/net/legitimoose/bot/scraper/Scraper.java @@ -456,6 +456,7 @@ private void bulkUpsert(List worlds, List players) { Updates.set("rank", player.rank()), Updates.set("streak", player.streak()), Updates.set("last_joined", new BsonDateTime(player.last_joined().toEpochMilli())), + Updates.set("legiticoins", player.legiticoins()), Updates.setOnInsert("blocked", player.blocked())); playerOperations.add(new UpdateOneModel<>( diff --git a/src/client/java/net/legitimoose/bot/util/JsonObjectCodec.java b/src/client/java/net/legitimoose/bot/util/JsonObjectCodec.java deleted file mode 100644 index a514cc9..0000000 --- a/src/client/java/net/legitimoose/bot/util/JsonObjectCodec.java +++ /dev/null @@ -1,36 +0,0 @@ -package net.legitimoose.bot.util; - -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import org.bson.BsonReader; -import org.bson.BsonWriter; -import org.bson.Document; -import org.bson.codecs.Codec; -import org.bson.codecs.DecoderContext; -import org.bson.codecs.DocumentCodec; -import org.bson.codecs.EncoderContext; - -public class JsonObjectCodec implements Codec { - private static final DocumentCodec DOCUMENT_CODEC = new DocumentCodec(); - - @Override - public void encode(BsonWriter writer, - JsonObject value, - EncoderContext encoderContext) { - Document document = Document.parse(value.toString()); - - DOCUMENT_CODEC.encode(writer, document, encoderContext); - } - - @Override - public JsonObject decode(BsonReader reader, - DecoderContext decoderContext) { - Document doc = DOCUMENT_CODEC.decode(reader, decoderContext); - return JsonParser.parseString(doc.toJson()).getAsJsonObject(); - } - - @Override - public Class getEncoderClass() { - return JsonObject.class; - } -} \ No newline at end of file From 64b4c784a34147ec633706312cc26a59b0ea967b Mon Sep 17 00:00:00 2001 From: Omri Date: Tue, 25 Aug 2026 20:07:55 +0300 Subject: [PATCH 16/18] fix: failing to write/read jam object --- src/client/java/net/legitimoose/bot/scraper/Scraper.java | 4 ++-- src/client/java/net/legitimoose/bot/scraper/World.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client/java/net/legitimoose/bot/scraper/Scraper.java b/src/client/java/net/legitimoose/bot/scraper/Scraper.java index 663766d..c225771 100644 --- a/src/client/java/net/legitimoose/bot/scraper/Scraper.java +++ b/src/client/java/net/legitimoose/bot/scraper/Scraper.java @@ -192,7 +192,7 @@ public void scrape() throws Exception { -1, - new JsonObject(), + new org.bson.json.JsonObject("{}"), "minecraft:grass_block", @@ -365,7 +365,7 @@ public void scrape() throws Exception { featured_instant, - jam, + new org.bson.json.JsonObject(jam.toString()), itemStack.get(DataComponents.ITEM_MODEL).toString(), System.currentTimeMillis() / 1000L, diff --git a/src/client/java/net/legitimoose/bot/scraper/World.java b/src/client/java/net/legitimoose/bot/scraper/World.java index 1d55973..3913110 100644 --- a/src/client/java/net/legitimoose/bot/scraper/World.java +++ b/src/client/java/net/legitimoose/bot/scraper/World.java @@ -1,6 +1,6 @@ package net.legitimoose.bot.scraper; -import com.google.gson.JsonObject; +import org.bson.json.JsonObject; public record World( String creation_date, From 99bd56dad5ebb5d588ec82ae7e0e5cebec89ea33 Mon Sep 17 00:00:00 2001 From: Omri Date: Tue, 25 Aug 2026 20:08:07 +0300 Subject: [PATCH 17/18] chore: bump version to 3.0.0-alpha.8 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 37abd0f..9b3f09f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ org.gradle.configuration-cache=false minecraft_version=26.2 loader_version=0.19.3 # Mod Properties -mod_version=3.0.0-alpha.7 +mod_version=3.0.0-alpha.8 maven_group=net.legitimoose archives_base_name=Legitimoose-Bot # Dependencies From 0efe7a46a2076a09990cb083d8d87ceb10f9501e Mon Sep 17 00:00:00 2001 From: Omri Date: Tue, 1 Sep 2026 21:08:26 +0300 Subject: [PATCH 18/18] rename deleted to legitidevs.deleted --- src/client/java/net/legitimoose/bot/scraper/World.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/client/java/net/legitimoose/bot/scraper/World.java b/src/client/java/net/legitimoose/bot/scraper/World.java index 3913110..1cad3c3 100644 --- a/src/client/java/net/legitimoose/bot/scraper/World.java +++ b/src/client/java/net/legitimoose/bot/scraper/World.java @@ -1,5 +1,6 @@ package net.legitimoose.bot.scraper; +import org.bson.codecs.pojo.annotations.BsonProperty; import org.bson.json.JsonObject; public record World( @@ -35,6 +36,7 @@ public record World( long last_scraped_ms, + @BsonProperty("legitidevs.deleted") boolean deleted ) { }