diff --git a/.idea/jsonSchemas.xml b/.idea/jsonSchemas.xml
new file mode 100644
index 0000000..e873468
--- /dev/null
+++ b/.idea/jsonSchemas.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/me/stuffy/stuffybot/Bot.java b/src/main/java/me/stuffy/stuffybot/Bot.java
index fa6f087..366922c 100644
--- a/src/main/java/me/stuffy/stuffybot/Bot.java
+++ b/src/main/java/me/stuffy/stuffybot/Bot.java
@@ -1,9 +1,9 @@
package me.stuffy.stuffybot;
-import me.stuffy.stuffybot.commands.*;
import me.stuffy.stuffybot.events.ActiveEvents;
import me.stuffy.stuffybot.events.UpdateBotStatsEvent;
+import me.stuffy.stuffybot.interactions.InteractionHandler;
import me.stuffy.stuffybot.utils.DiscordUtils;
import me.stuffy.stuffybot.utils.Logger;
import net.dv8tion.jda.api.JDA;
@@ -14,6 +14,12 @@
import net.dv8tion.jda.api.events.guild.GuildJoinEvent;
import net.dv8tion.jda.api.events.guild.GuildLeaveEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
+import net.dv8tion.jda.api.interactions.commands.OptionType;
+import net.dv8tion.jda.api.interactions.commands.build.CommandData;
+import net.dv8tion.jda.api.interactions.commands.build.Commands;
+import net.dv8tion.jda.api.interactions.commands.build.OptionData;
+
+import java.util.ArrayList;
public class Bot extends ListenerAdapter {
private static Bot INSTANCE;
@@ -33,29 +39,21 @@ public Bot() throws InterruptedException {
// Initialize home guild
this.homeGuild = jda.getGuildById("795108903733952562");
-// this.homeGuild = jda.getGuildById("818238263110008863");
assert this.homeGuild != null : "Failed to find home guild";
// Log startup
String time = DiscordUtils.discordTimeNow();
String self = jda.getSelfUser().getAsMention();
- Logger logger = new Logger();
- logger.log(" Bot " + self + " started successfully " + time + ".");
+ Logger.log(" Bot " + self + " started successfully " + time + ".");
- // Register commands
+ // Listen for interactions
jda.addEventListener(
- new VerifyCommand("verify", "Links your discord account to your Minecraft account"),
- new MaxedGamesCommand("maxes", "Find the games with all achievements unlocked"),
- new TournamentCommand("tournament", "Shows tournament stats"),
- new AchievementCommand("achievements", "Shows achievements progress for a user"),
- new StatsCommand("stats", "Shows overall hypixel stats for a user"),
- new PitCommand("pit", "Shows pit stats for a user"),
- new TkrCommand("tkr", "Shows completed maps in TKR"),
- new MegaWallsCommand("megawalls", "Shows Mega Walls stats for a user")
+ new InteractionHandler()
);
-
+ // Register commands "global"ly or "local"ly
+ registerCommands("local");
// Start events
new UpdateBotStatsEvent().startFixedRateEvent();
@@ -94,4 +92,53 @@ public void onGuildLeave(GuildLeaveEvent event) {
Guild leftGuild = event.getGuild();
Logger.log(" Bot left guild: " + leftGuild.getName() + " (" + leftGuild.getId() + ")");
}
+
+ public void registerCommands(String scope) {
+ OptionData ignOption = new OptionData(OptionType.STRING, "ign", "The player's IGN", false);
+ // Create a list of commands first
+ ArrayList commandList = new ArrayList<>();
+// commandList.add(Commands.slash("help", "*Should* show a help message"));
+ commandList.add(Commands.slash("pit", "Get Pit stats for a player")
+ .addOptions(ignOption));
+ commandList.add(Commands.slash("stats", "Get Hypixel stats for a player")
+ .addOptions(ignOption));
+ commandList.add(Commands.slash("tkr", "Get TKR stats for a player")
+ .addOptions(ignOption));
+ commandList.add(Commands.slash("maxes", "Get maxed games for a player")
+ .addOptions(ignOption));
+ commandList.add(Commands.slash("blitz", "Get Blitz Ultimate Kit xp for a player")
+ .addOptions(ignOption));
+ commandList.add(Commands.slash("megawalls", "Get Mega Walls skins for a player")
+ .addOptions(ignOption)
+ .addOptions(new OptionData(OptionType.STRING, "skins", "Which skins to look at", false).setAutoComplete(true)));
+ commandList.add(Commands.slash("tournament", "Get tournament stats for a player")
+ .addOptions(ignOption)
+ .addOptions(new OptionData(OptionType.INTEGER, "tournament", "Which tournament to look at (Leave empty for latest)", false).setAutoComplete(true)));
+
+
+ if (scope.equals("local")) {
+ //clearLocalCommands();
+ this.homeGuild.updateCommands().addCommands(
+ commandList
+ ).queue();
+ Logger.log(" Successfully Registered commands in guild.");
+ } else if (scope.equals("global")){
+ jda.updateCommands().addCommands(
+ commandList
+ ).queue();
+ Logger.log(" Successfully Registered commands globally.");
+ } else {
+ throw new IllegalArgumentException("Invalid scope: " + scope);
+ }
+ }
+
+ public void clearCommands() {
+ jda.updateCommands().queue();
+ Logger.log(" Successfully cleared commands.");
+ }
+
+ public void clearLocalCommands() {
+ this.homeGuild.updateCommands().queue();
+ Logger.log(" Successfully cleared local commands.");
+ }
}
diff --git a/src/main/java/me/stuffy/stuffybot/commands/AchievementCommand.java b/src/main/java/me/stuffy/stuffybot/commands/AchievementCommand.java
deleted file mode 100644
index 3cc543a..0000000
--- a/src/main/java/me/stuffy/stuffybot/commands/AchievementCommand.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package me.stuffy.stuffybot.commands;
-
-import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
-import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
-import net.dv8tion.jda.api.interactions.commands.OptionType;
-import net.dv8tion.jda.api.interactions.commands.build.OptionData;
-
-import static me.stuffy.stuffybot.utils.DiscordUtils.getUsername;
-
-public class AchievementCommand extends BaseCommand{
- public AchievementCommand(String name, String description){
- super(name, description,
- new OptionData(OptionType.STRING, "ign", "Minecraft Username", false),
- new OptionData(OptionType.STRING, "game", "The game", false)
- .addChoice("Skyblock", "skyblock"),
- new OptionData(OptionType.STRING, "type", "Challenge/Tiered", false)
- .addChoice("Challenge", "challenge")
- .addChoice("Tiered", "tiered"
- )
- );
- }
-
-
- protected void onCommand(SlashCommandInteractionEvent event) {
- String ign = getUsername(event);
- if (event.getOption("game") == null) {
- event.getHook().sendMessage("no game provided").queue();
- return;
- }
- String game = event.getOption("game").getAsString();
- event.getHook().sendMessage("game: " + game).queue();
- }
-
- public void onButton(ButtonInteractionEvent event) {
- }
-
- @Override
- protected void cleanupEventResources(String messageId) {
-
- }
-}
diff --git a/src/main/java/me/stuffy/stuffybot/commands/BaseCommand.java b/src/main/java/me/stuffy/stuffybot/commands/BaseCommand.java
deleted file mode 100644
index 152bc10..0000000
--- a/src/main/java/me/stuffy/stuffybot/commands/BaseCommand.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package me.stuffy.stuffybot.commands;
-
-import me.stuffy.stuffybot.Bot;
-import me.stuffy.stuffybot.utils.Logger;
-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.dv8tion.jda.api.interactions.commands.OptionMapping;
-import net.dv8tion.jda.api.interactions.commands.build.OptionData;
-import org.jetbrains.annotations.NotNull;
-
-import java.time.Instant;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-public abstract class BaseCommand extends ListenerAdapter {
- private String name;
- private String description;
- private final Map latestValidInteraction = new HashMap<>();
- private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
-
-
- public BaseCommand(String name, String description, OptionData... options) {
- this.name = name;
- this.description = description;
- Bot bot = Bot.getInstance();
- bot.getHomeGuild().upsertCommand(name, description)
- .addOptions(options)
- .queue();
- Logger.log(" Registered command " + name + ": " + description);
- }
-
- @Override
- public void onSlashCommandInteraction(SlashCommandInteractionEvent event) {
- if (event.getName().equals(this.name)) {
- event.deferReply().queue();
- String options = "";
- for (OptionMapping option : event.getOptions()) {
- options += option.getName() + ": " + option.getAsString() + ", ";
- }
- Logger.log(" @" + event.getUser().getName() + ": /" + this.name + " " + options);
- this.onCommand(event);
- latestValidInteraction.put(event.getHook().getId(), Instant.now());
- scheduler.scheduleAtFixedRate(this::endEvent, 0, 1, TimeUnit.SECONDS);
- }
-
- // StatisticsManager.incrementTotalCommandsRun();
- }
-
- private void endEvent() {
- latestValidInteraction.forEach((messageId, time) -> {
- if (Instant.now().getEpochSecond() - time.getEpochSecond() > 30) {
- latestValidInteraction.remove(messageId);
- cleanupEventResources(messageId);
- }
- });
- }
-
- protected abstract void onCommand(SlashCommandInteractionEvent event);
-
- @Override
- public void onButtonInteraction(@NotNull ButtonInteractionEvent event) {
- this.onButton(event);
- }
-
- protected abstract void onButton(ButtonInteractionEvent event);
-
- protected abstract void cleanupEventResources(String messageId);
-}
diff --git a/src/main/java/me/stuffy/stuffybot/commands/BlitzCommand.java b/src/main/java/me/stuffy/stuffybot/commands/BlitzCommand.java
new file mode 100644
index 0000000..d6a184f
--- /dev/null
+++ b/src/main/java/me/stuffy/stuffybot/commands/BlitzCommand.java
@@ -0,0 +1,47 @@
+package me.stuffy.stuffybot.commands;
+
+import me.stuffy.stuffybot.interactions.InteractionId;
+import me.stuffy.stuffybot.profiles.HypixelProfile;
+import me.stuffy.stuffybot.utils.APIException;
+import net.dv8tion.jda.api.entities.MessageEmbed;
+import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
+import net.dv8tion.jda.api.utils.messages.MessageCreateData;
+
+import java.text.DecimalFormat;
+import java.util.Map;
+
+import static me.stuffy.stuffybot.utils.APIUtils.getHypixelProfile;
+import static me.stuffy.stuffybot.utils.DiscordUtils.makeStatsEmbed;
+
+public class BlitzCommand {
+ public static MessageCreateData blitz(InteractionId interactionId) throws APIException {
+ String ign = interactionId.getOptions().get("ign");
+ HypixelProfile hypixelProfile = getHypixelProfile(ign);
+ String username = hypixelProfile.getDisplayName();
+
+ Map blitzStats = hypixelProfile.getBlitzStats();
+
+ DecimalFormat df = new DecimalFormat("#,###");
+ DecimalFormat df2 = new DecimalFormat("#,###.##");
+ StringBuilder embedContent = new StringBuilder();
+
+ for (Map.Entry entry : blitzStats.entrySet()) {
+ String key = entry.getKey();
+ Integer value = entry.getValue();
+ if (value < 10000) {
+ embedContent.append(key).append(": **").append(df.format(value)).append("**/10,000 (").append(df2.format(value / 100)).append("%)\n");
+ } else {
+ embedContent.append("~~").append(key).append(": **").append(df.format(value)).append("**/10,000~~\n");
+ }
+ }
+
+ MessageEmbed blitzEmbed = makeStatsEmbed(
+ "Blitz Ultimate Kit Xp for " + username,
+ embedContent.toString()
+ );
+
+ return new MessageCreateBuilder()
+ .addEmbeds(blitzEmbed)
+ .build();
+ }
+}
diff --git a/src/main/java/me/stuffy/stuffybot/commands/MaxedGamesCommand.java b/src/main/java/me/stuffy/stuffybot/commands/MaxedGamesCommand.java
deleted file mode 100644
index 7c1f4eb..0000000
--- a/src/main/java/me/stuffy/stuffybot/commands/MaxedGamesCommand.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package me.stuffy.stuffybot.commands;
-
-import me.stuffy.stuffybot.profiles.HypixelProfile;
-import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
-import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
-import net.dv8tion.jda.api.interactions.commands.OptionType;
-import net.dv8tion.jda.api.interactions.commands.build.OptionData;
-
-import static me.stuffy.stuffybot.utils.APIUtils.getHypixelProfile;
-import static me.stuffy.stuffybot.utils.DiscordUtils.getUsername;
-import static me.stuffy.stuffybot.utils.DiscordUtils.makeErrorEmbed;
-
-public class MaxedGamesCommand extends BaseCommand{
-
- public MaxedGamesCommand(String name, String description) {
- super(name, description,
- new OptionData(OptionType.STRING, "ign", "Your Minecraft Username", false)
- );
- }
- @Override
- protected void onCommand(SlashCommandInteractionEvent event) {
- String ign = getUsername(event);
- HypixelProfile hypixelProfile;
- try {
- hypixelProfile = getHypixelProfile(ign);
- }catch (Exception e){
- event.getHook().sendMessage("").addEmbeds(
- makeErrorEmbed(
- "API Error",
- "An error occurred while fetching your Hypixel profile. Please try again later."
- )
- ).queue();
- return;
- }
- hypixelProfile.getMaxedGames();
- }
-
- public void onButton(ButtonInteractionEvent event) {
- }
-
- @Override
- protected void cleanupEventResources(String messageId) {
-
- }
-}
diff --git a/src/main/java/me/stuffy/stuffybot/commands/MaxesCommand.java b/src/main/java/me/stuffy/stuffybot/commands/MaxesCommand.java
new file mode 100644
index 0000000..0eb645c
--- /dev/null
+++ b/src/main/java/me/stuffy/stuffybot/commands/MaxesCommand.java
@@ -0,0 +1,45 @@
+package me.stuffy.stuffybot.commands;
+
+import me.stuffy.stuffybot.interactions.InteractionId;
+import me.stuffy.stuffybot.profiles.HypixelProfile;
+import me.stuffy.stuffybot.utils.APIException;
+import net.dv8tion.jda.api.entities.MessageEmbed;
+import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
+import net.dv8tion.jda.api.utils.messages.MessageCreateData;
+
+import java.util.ArrayList;
+
+import static me.stuffy.stuffybot.utils.APIUtils.getHypixelProfile;
+import static me.stuffy.stuffybot.utils.DiscordUtils.makeStatsEmbed;
+import static me.stuffy.stuffybot.utils.MiscUtils.toReadableName;
+
+public class MaxesCommand {
+ public static MessageCreateData maxes(InteractionId interactionId) throws APIException {
+ String ign = interactionId.getOptions().get("ign");
+ HypixelProfile hypixelProfile = getHypixelProfile(ign);
+ String username = hypixelProfile.getDisplayName();
+
+ ArrayList maxesArray = hypixelProfile.getMaxGames();
+
+ StringBuilder embedContent = new StringBuilder();
+ String subtitle = username + " has " + maxesArray.size() + " Maxed Games";
+ for (String game : maxesArray) {
+ embedContent.append(toReadableName(game)).append("\n");
+ }
+
+ if (maxesArray.isEmpty()) {
+ subtitle = "No Maxed Games found. :(";
+ }
+
+ MessageEmbed maxesEmbed = makeStatsEmbed(
+ "Maxed Games for " + username,
+ subtitle,
+ embedContent.toString()
+ );
+
+ return new MessageCreateBuilder()
+ .addEmbeds(maxesEmbed)
+ .build();
+
+ }
+}
diff --git a/src/main/java/me/stuffy/stuffybot/commands/MegaWallsCommand.java b/src/main/java/me/stuffy/stuffybot/commands/MegaWallsCommand.java
index 8cae1d7..ec63d05 100644
--- a/src/main/java/me/stuffy/stuffybot/commands/MegaWallsCommand.java
+++ b/src/main/java/me/stuffy/stuffybot/commands/MegaWallsCommand.java
@@ -1,92 +1,137 @@
package me.stuffy.stuffybot.commands;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import me.stuffy.stuffybot.interactions.InteractionId;
import me.stuffy.stuffybot.profiles.HypixelProfile;
-import net.dv8tion.jda.api.events.Event;
-import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
-import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
-import net.dv8tion.jda.api.interactions.commands.OptionType;
-import net.dv8tion.jda.api.interactions.commands.build.OptionData;
-import net.dv8tion.jda.api.interactions.components.selections.SelectOption;
-import net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu;
+import me.stuffy.stuffybot.utils.APIException;
+import me.stuffy.stuffybot.utils.InvalidOptionException;
+import net.dv8tion.jda.api.entities.MessageEmbed;
+import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
+import net.dv8tion.jda.api.utils.messages.MessageCreateData;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
-import java.util.HashMap;
import java.util.Map;
import static me.stuffy.stuffybot.utils.APIUtils.getHypixelProfile;
-import static me.stuffy.stuffybot.utils.DiscordUtils.*;
+import static me.stuffy.stuffybot.utils.DiscordUtils.makeStatsEmbed;
-public class MegaWallsCommand extends BaseCommand{
- private final Map activeEvents = new HashMap<>();
- public MegaWallsCommand(String name, String description) {
- super(name, description,
- new OptionData(OptionType.STRING, "ign", "The username of the player you want to look up", false));
- }
+public class MegaWallsCommand {
- @Override
- protected void onCommand(SlashCommandInteractionEvent event) {
- activeEvents.put(event.getHook().getId(), event);
- String ign = getUsername(event);
- HypixelProfile hypixelProfile;
- try {
- hypixelProfile = getHypixelProfile(ign);
- }catch (Exception e){
- event.getHook().sendMessage("").addEmbeds(
- makeErrorEmbed(
- "API Error",
- "An error occurred while fetching your Hypixel profile. Please try again later."
- )
- ).queue();
- return;
+ private static JsonObject getMwClasses() {
+ try (InputStream inputStream = MegaWallsCommand.class.getResourceAsStream("/data/mwclasses.json")) {
+ assert inputStream != null;
+ try (InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
+ return JsonParser.parseReader(reader).getAsJsonObject();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
}
+ }
+ public static MessageCreateData megawalls(InteractionId interactionId) throws APIException, InvalidOptionException {
+ String ign = interactionId.getOption("ign");
+ HypixelProfile hypixelProfile = getHypixelProfile(ign);
String username = hypixelProfile.getDisplayName();
+
+ String mwClass = interactionId.getOption("skins", "all");
Map legendary_skins = hypixelProfile.getMegaWallsLegendaries();
int legendary_skins_unlocked = 0;
- for (Boolean unlocked : legendary_skins.values()){
- if (unlocked){
- legendary_skins_unlocked++;
- }
- }
-
- Integer wins = hypixelProfile.getMegaWallsWins();
- Integer finalKills = hypixelProfile.getMegaWallsFinalKills();
- Integer classPoints = hypixelProfile.getMegaWallsClassPoints();
- String className = hypixelProfile.getMegaWallsSelectedClass();
+ DecimalFormat dfPercent = new DecimalFormat("##.##");
+ DecimalFormat dfComma = new DecimalFormat("#,###");
- DecimalFormat df = new DecimalFormat("#,###");
- event.getHook().sendMessage("").addEmbeds(
- makeStatsEmbed(
+ MessageEmbed response = null;
+ switch (mwClass.toLowerCase()) {
+ case "legendary" -> {
+ StringBuilder legendarySkins = new StringBuilder();
+ for (Map.Entry entry : legendary_skins.entrySet()) {
+ if (entry.getValue()) {
+ legendarySkins.append("✅ ");
+ legendary_skins_unlocked++;
+ } else {
+ legendarySkins.append("❌ ");
+ }
+ legendarySkins.append(entry.getKey()).append("\n");
+ }
+ response = makeStatsEmbed(
+ "Legendary Skins for " + username,
+ "Unlocked: **" + legendary_skins_unlocked + "**/27 (" + dfPercent.format(legendary_skins_unlocked / 27.0 * 100) + "%)",
+ legendarySkins.toString()
+ );
+ }
+ case "all" -> {
+ Integer wins = hypixelProfile.getMegaWallsWins();
+ Integer finalKills = hypixelProfile.getMegaWallsFinalKills();
+ Integer classPoints = hypixelProfile.getMegaWallsClassPoints();
+ String selectedClass = hypixelProfile.getMegaWallsSelectedClass();
+ for (Boolean unlocked : legendary_skins.values()) {
+ if (unlocked) {
+ legendary_skins_unlocked++;
+ }
+ }
+ response = makeStatsEmbed(
"Mega Walls Stats for " + username,
- "Wins: **" + df.format(wins) + "**\n" +
- "Final Kills: **" + df.format(finalKills) + "**\n" +
- "Total Class Points: **" + df.format(classPoints) + "**\n\n" +
- "Selected Class: **" + className + "**\n" +
+ "Wins: **" + dfComma.format(wins) + "**\n" +
+ "Final Kills: **" + dfComma.format(finalKills) + "**\n" +
+ "Total Class Points: **" + dfComma.format(classPoints) + "**\n\n" +
+ "Selected Class: **" + selectedClass + "**\n" +
"Legendary Skins Unlocked: **" + legendary_skins_unlocked + "**/27"
+ );
+ }
+ default -> {
+ JsonObject mwClasses = getMwClasses();
+ if (mwClasses == null) {
+ throw new APIException("Stuffy", "Failed to load Mega Walls classes data");
+ }
+ for (JsonElement element : mwClasses.getAsJsonArray("classes")) {
+ JsonObject mwClassObject = element.getAsJsonObject();
+ String id = mwClassObject.get("id").getAsString();
+ if (id.equals(mwClass)) {
+ String className = mwClassObject.get("name").getAsString();
+ JsonArray skins = mwClassObject.getAsJsonArray("skins");
+ int skinCount = skins.size();
- )
- ).addActionRow(
- StringSelectMenu.create("megawalls-select")
- .addOption("Legendaries", "legendaries")
- .addOptions(
- SelectOption.of("Overall", "overall").withDefault(true)
- ).build()
- ).queue();
-
- }
-
- @Override
- protected void onButton(ButtonInteractionEvent event) {
+ String[] breakdown = new String[skinCount];
+ DecimalFormat df = new DecimalFormat("#,###");
+ for (int i = 0; i < skinCount; i++) {
+ StringBuilder skinString = new StringBuilder();
+ JsonObject skin = skins.get(i).getAsJsonObject();
+ String skinName = skin.get("name").getAsString();
+ Integer requiredStats = skin.get("max").getAsInt();
+ Integer playerProgress = 0;
+ for (JsonElement value : skin.get("values").getAsJsonArray()) {
+ playerProgress += hypixelProfile.getMegaWallsStat(value.getAsString());
+ }
- }
+ if (playerProgress >= requiredStats) {
+ skinString.append("~~").append(skinName).append(" **").append(df.format(playerProgress)).append("**/").append(df.format(requiredStats)).append("~~");
+ } else {
+ skinString.append(skinName).append(" **").append(df.format(playerProgress)).append("**/").append(df.format(requiredStats));
+ }
+ breakdown[i] = skinString.toString();
+ }
- @Override
- protected void cleanupEventResources(String messageId) {
- Event event = activeEvents.remove(messageId);
- if (event instanceof SlashCommandInteractionEvent slashEvent) {
- slashEvent.getHook().retrieveOriginal().queue(message -> {
- message.editMessageComponents().queue();
- });
+ response = makeStatsEmbed(
+ className + " Skins for " + username,
+ skinCount + " tracked skins",
+ String.join("\n", breakdown)
+ );
+ break;
+ }
+ }
+ if (response == null) {
+ throw new InvalidOptionException("skins", mwClass);
+ }
+ }
}
+ return new MessageCreateBuilder()
+ .addEmbeds(response)
+ .build();
}
}
diff --git a/src/main/java/me/stuffy/stuffybot/commands/PitCommand.java b/src/main/java/me/stuffy/stuffybot/commands/PitCommand.java
index d71fd9b..e26ad92 100644
--- a/src/main/java/me/stuffy/stuffybot/commands/PitCommand.java
+++ b/src/main/java/me/stuffy/stuffybot/commands/PitCommand.java
@@ -1,54 +1,27 @@
package me.stuffy.stuffybot.commands;
import kotlin.Triple;
+import me.stuffy.stuffybot.interactions.InteractionId;
import me.stuffy.stuffybot.profiles.HypixelProfile;
-import net.dv8tion.jda.api.EmbedBuilder;
+import me.stuffy.stuffybot.utils.APIException;
import net.dv8tion.jda.api.entities.MessageEmbed;
-import net.dv8tion.jda.api.events.Event;
-import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
-import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
-import net.dv8tion.jda.api.interactions.commands.OptionType;
-import net.dv8tion.jda.api.interactions.commands.build.OptionData;
-import net.dv8tion.jda.api.interactions.components.buttons.ButtonInteraction;
+import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
+import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import java.text.DecimalFormat;
-import java.util.HashMap;
-import java.util.Map;
import static me.stuffy.stuffybot.utils.APIUtils.getHypixelProfile;
-import static me.stuffy.stuffybot.utils.DiscordUtils.*;
+import static me.stuffy.stuffybot.utils.DiscordUtils.makeStatsEmbed;
import static me.stuffy.stuffybot.utils.MiscUtils.convertToRomanNumeral;
-import static net.dv8tion.jda.api.interactions.components.buttons.Button.*;
+import static net.dv8tion.jda.api.interactions.components.buttons.Button.secondary;
-public class PitCommand extends BaseCommand{
- private final Map originalEmbeds = new HashMap<>();
- private final Map originalDetailedEmbeds = new HashMap<>();
- private final Map activeEvents = new HashMap<>();
-
- public PitCommand(String name, String description) {
- super(name, description,
- new OptionData(OptionType.STRING, "ign", "Your Minecraft Username", false)
- );
- }
-
- @Override
- protected void onCommand(SlashCommandInteractionEvent event) {
- activeEvents.put(event.getHook().getId(), event);
- String ign = getUsername(event);
- HypixelProfile hypixelProfile;
- try {
- hypixelProfile = getHypixelProfile(ign);
- }catch (Exception e){
- event.getHook().sendMessage("").addEmbeds(
- makeErrorEmbed(
- "API Error",
- "An error occurred while fetching your Hypixel profile. Please try again later."
- )
- ).queue();
- return;
- }
+public class PitCommand {
+ public static MessageCreateData pit(InteractionId interactionId) throws APIException {
+ String ign = interactionId.getOptions().get("ign");
+ HypixelProfile hypixelProfile = getHypixelProfile(ign);
String username = hypixelProfile.getDisplayName();
+
String pitPrestige = convertToRomanNumeral(hypixelProfile.getPit("prestige"));
Long totalPitXp = hypixelProfile.getPitXP();
@@ -72,18 +45,18 @@ protected void onCommand(SlashCommandInteractionEvent event) {
embedContent
);
- originalEmbeds.put(event.getHook().getId(), pitStats);
-
-
- String runnerId = event.getUser().getId();
- event.getHook().sendMessage("")
+ String newInteractionId = InteractionId.newCommand("pitDetailed", interactionId).getInteractionString();
+ return new MessageCreateBuilder()
+ .addEmbeds(pitStats)
.addActionRow(
- secondary("pitDetailed:" + runnerId, "Challenge Achievement Progress")
+ secondary(newInteractionId, "Challenge Achievement Progress")
)
- .addEmbeds(
- pitStats
- ).queue();
+ .build();
+ }
+ public static MessageCreateData pitDetailed(InteractionId interactionId) throws APIException{
+ String ign = interactionId.getOptions().get("ign");
+ HypixelProfile hypixelProfile = getHypixelProfile(ign);
Triple[] challengeAchievements = new Triple[]{
new Triple<>("Ingots Collector", hypixelProfile.getPit("ingots_collector"), 2000),
@@ -96,59 +69,28 @@ protected void onCommand(SlashCommandInteractionEvent event) {
new Triple<>("Raging Hunter", hypixelProfile.getPit("raging_hunter"), 100),
new Triple<>("Bounty Hunter", hypixelProfile.getPit("bounty_hunter"), 30)
};
-
- StringBuilder embedContent2 = new StringBuilder();
+ DecimalFormat df = new DecimalFormat("#,###");
+ StringBuilder embedContent = new StringBuilder();
for (Triple challenge : challengeAchievements) {
if (challenge.getSecond() >= challenge.getThird()) {
- embedContent2.append("~~").append(challenge.getFirst()).append(": **").append(df.format(challenge.getSecond())).append("** / ").append(df.format(challenge.getThird())).append("~~\n");
+ embedContent.append("~~").append(challenge.getFirst()).append(": **").append(df.format(challenge.getSecond())).append("** / ").append(df.format(challenge.getThird())).append("~~\n");
} else {
String percentage = df.format((double) challenge.getSecond() / challenge.getThird() * 100);
- embedContent2.append(challenge.getFirst()).append(": **").append(df.format(challenge.getSecond())).append("** / ").append(df.format(challenge.getThird())).append(" (").append(percentage).append("%)\n");
+ embedContent.append(challenge.getFirst()).append(": **").append(df.format(challenge.getSecond())).append("** / ").append(df.format(challenge.getThird())).append(" (").append(percentage).append("%)\n");
}
}
MessageEmbed extraPitStats = makeStatsEmbed(
- "Pit Achievement stats for " + username,
- embedContent2.toString()
+ "Pit Achievement stats for " + hypixelProfile.getDisplayName(),
+ embedContent.toString()
);
- originalDetailedEmbeds.put(event.getHook().getId(), extraPitStats);
- }
-
- public void onButton(ButtonInteractionEvent event) {
- String[] parts = event.getComponentId().split(":");
- String action = parts[0];
- String userId = parts[1];
-
- if (action.equals("pitDetailed")) {
- MessageEmbed detailedButton = originalDetailedEmbeds.get(event.getHook().getId());
- if(detailedButton == null) {
- return;
- }
- event.editMessageEmbeds(detailedButton)
- .setActionRow(secondary("go_back:" + userId, "Go Back"))
- .queue();
- }
- if (action.equals("go_back")) {
- MessageEmbed backButton = originalEmbeds.get(event.getHook().getId());
- if(backButton == null) {
- return;
- }
- event.editMessageEmbeds(backButton)
- .setActionRow(secondary("pitDetailed:" + userId, "Challenge Achievement Progress"))
- .queue();
- }
- }
-
- @Override
- protected void cleanupEventResources(String messageId) {
- originalEmbeds.remove(messageId);
- originalDetailedEmbeds.remove(messageId);
- Event event = activeEvents.remove(messageId);
- if (event instanceof SlashCommandInteractionEvent slashEvent) {
- slashEvent.getHook().retrieveOriginal().queue(message -> {
- message.editMessageComponents().queue();
- });
- }
+ String newInteractionId = InteractionId.newCommand("pit", interactionId).getInteractionString();
+ return new MessageCreateBuilder()
+ .addEmbeds(extraPitStats)
+ .addActionRow(
+ secondary(newInteractionId, "Back to Pit Stats")
+ )
+ .build();
}
}
diff --git a/src/main/java/me/stuffy/stuffybot/commands/StatsCommand.java b/src/main/java/me/stuffy/stuffybot/commands/StatsCommand.java
index df99c51..c0c4e59 100644
--- a/src/main/java/me/stuffy/stuffybot/commands/StatsCommand.java
+++ b/src/main/java/me/stuffy/stuffybot/commands/StatsCommand.java
@@ -1,38 +1,21 @@
package me.stuffy.stuffybot.commands;
+import me.stuffy.stuffybot.interactions.InteractionId;
import me.stuffy.stuffybot.profiles.HypixelProfile;
-import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
-import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
-import net.dv8tion.jda.api.interactions.commands.OptionType;
-import net.dv8tion.jda.api.interactions.commands.build.OptionData;
+import me.stuffy.stuffybot.utils.APIException;
+import net.dv8tion.jda.api.entities.MessageEmbed;
+import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
+import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import java.text.DecimalFormat;
import static me.stuffy.stuffybot.utils.APIUtils.getHypixelProfile;
-import static me.stuffy.stuffybot.utils.DiscordUtils.*;
-
-public class StatsCommand extends BaseCommand{
- public StatsCommand(String name, String description){
- super(name, description,
- new OptionData(OptionType.STRING, "ign", "The username of the player you want to look up", false));
- }
-
- @Override
- protected void onCommand(SlashCommandInteractionEvent event) {
- String ign = getUsername(event);
- HypixelProfile hypixelProfile;
- try{
- hypixelProfile = getHypixelProfile(ign);
- } catch (Exception e){
- event.getHook().sendMessage("").addEmbeds(
- makeErrorEmbed(
- "API Error",
- "An error occurred while fetching your Hypixel profile. Please try again later."
- )
- ).queue();
- return;
- }
+import static me.stuffy.stuffybot.utils.DiscordUtils.makeStatsEmbed;
+public class StatsCommand {
+ public static MessageCreateData stats(InteractionId interactionId) throws APIException {
+ String ign = interactionId.getOptions().get("ign");
+ HypixelProfile hypixelProfile = getHypixelProfile(ign);
String username = hypixelProfile.getDisplayName();
DecimalFormat df = new DecimalFormat("#,###");
@@ -55,29 +38,24 @@ protected void onCommand(SlashCommandInteractionEvent event) {
String embedContent =
onlineStatus + "\n" +
- "Network Level: **" + df2.format(networkLevel) + "**\n" +
- "Karma: **" + df.format(karma) + "**\n\n" +
- "Achievement Points: **" + df.format(achievementPoints) + "** (+" + df.format(legacyAchievementPoints) + " legacy)" + "\n" +
- "Quests Completed: **" + df.format(questsCompleted) + "**\n" +
- "Challenges Completed: **" + df.format(challenges) + "**\n\n" +
- "Reward Streak|Record: **" + df.format(rewardStreak) + "** | " + df.format(rewardRecord) + "\n" +
- "Wins: **" + df.format(wins) + "**\n" +
- "Kills: **" + df.format(kills) + "**\n" +
- "First Login: " + firstLogin + "\n";
-
- event.getHook().sendMessage("").addEmbeds(
- makeStatsEmbed(
- "Hypixel Stats for " + username,
- embedContent
- )
- ).queue();
- }
-
- public void onButton(ButtonInteractionEvent event) {
- }
-
- @Override
- protected void cleanupEventResources(String messageId) {
+ "Network Level: **" + df2.format(networkLevel) + "**\n" +
+ "Karma: **" + df.format(karma) + "**\n\n" +
+ "Achievement Points: **" + df.format(achievementPoints) + "** (+" + df.format(legacyAchievementPoints) + " legacy)" + "\n" +
+ "Quests Completed: **" + df.format(questsCompleted) + "**\n" +
+ "Challenges Completed: **" + df.format(challenges) + "**\n\n" +
+ "Reward Streak|Record: **" + df.format(rewardStreak) + "** | " + df.format(rewardRecord) + "\n" +
+ "Wins: **" + df.format(wins) + "**\n" +
+ "Kills: **" + df.format(kills) + "**\n" +
+ "First Login: " + firstLogin + "\n";
+
+ MessageEmbed stats = makeStatsEmbed(
+ "Stats for " + username,
+ embedContent
+ );
+
+ return new MessageCreateBuilder()
+ .addEmbeds(stats)
+ .build();
}
}
diff --git a/src/main/java/me/stuffy/stuffybot/commands/TkrCommand.java b/src/main/java/me/stuffy/stuffybot/commands/TkrCommand.java
index c6157fd..9d2959b 100644
--- a/src/main/java/me/stuffy/stuffybot/commands/TkrCommand.java
+++ b/src/main/java/me/stuffy/stuffybot/commands/TkrCommand.java
@@ -1,43 +1,25 @@
package me.stuffy.stuffybot.commands;
+import me.stuffy.stuffybot.interactions.InteractionId;
import me.stuffy.stuffybot.profiles.HypixelProfile;
-import net.dv8tion.jda.api.EmbedBuilder;
-import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
-import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
-import net.dv8tion.jda.api.interactions.commands.OptionType;
-import net.dv8tion.jda.api.interactions.commands.build.OptionData;
+import me.stuffy.stuffybot.utils.APIException;
+import net.dv8tion.jda.api.entities.MessageEmbed;
+import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
+import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import java.util.Map;
import static me.stuffy.stuffybot.utils.APIUtils.getHypixelProfile;
-import static me.stuffy.stuffybot.utils.DiscordUtils.*;
-
-public class TkrCommand extends BaseCommand{
- public TkrCommand(String name, String description) {
- super(name, description,
- new OptionData(OptionType.STRING, "ign", "The username of the player you want to look up", false));
- }
-
- @Override
- protected void onCommand(SlashCommandInteractionEvent event) {
- String ign = getUsername(event);
- HypixelProfile hypixelProfile;
- try{
- hypixelProfile = getHypixelProfile(ign);
- } catch (Exception e){
- event.getHook().sendMessage("").addEmbeds(
- makeErrorEmbed(
- "API Error",
- "An error occurred while fetching your Hypixel profile. Please try again later."
- )
- ).queue();
- return;
- }
+import static me.stuffy.stuffybot.utils.DiscordUtils.makeStatsEmbed;
+public class TkrCommand {
+ public static MessageCreateData tkr(InteractionId interactionId) throws APIException {
+ String ign = interactionId.getOptions().get("ign");
+ HypixelProfile hypixelProfile = getHypixelProfile(ign);
String username = hypixelProfile.getDisplayName();
Map uniqueGolds = hypixelProfile.getTkrMaps();
- String embedContent = "";
+ StringBuilder embedContent = new StringBuilder();
int uniqueGoldCount = 0;
for (Map.Entry entry : uniqueGolds.entrySet()) {
@@ -46,28 +28,20 @@ protected void onCommand(SlashCommandInteractionEvent event) {
if (value) {
uniqueGoldCount++;
}
- embedContent += (value ? "✅ " : "❌ ") + key + "\n";
+ embedContent.append(value ? "✅ " : "❌ ").append(key).append("\n");
}
-
- embedContent = "Unique Gold Medals: **" + uniqueGoldCount + "**/5\n\n" + embedContent;
-
-
- event.getHook().sendMessage("").addEmbeds(
- makeStatsEmbed(
- "TKR Unique Gold Medals for " + username,
- embedContent
- )
- ).queue();
- }
-
- @Override
- protected void onButton(ButtonInteractionEvent event) {
-
- }
-
- @Override
- protected void cleanupEventResources(String messageId) {
-
+ MessageEmbed tkrStats = makeStatsEmbed(
+ "TKR Stats for " + username,
+ "Unique Gold Medals: **" + uniqueGoldCount + "**/5",
+ embedContent.toString()
+ );
+
+ return new MessageCreateBuilder()
+ .addEmbeds(tkrStats)
+// .addActionRow(
+// secondary("achievements:" + interactionId.getUserId() + ":game=tkr," + interactionId.getOptionsString(), "Looking for TKR Achievements?")
+// )
+ .build();
}
}
diff --git a/src/main/java/me/stuffy/stuffybot/commands/TournamentCommand.java b/src/main/java/me/stuffy/stuffybot/commands/TournamentCommand.java
index 8cf191e..8e715fc 100644
--- a/src/main/java/me/stuffy/stuffybot/commands/TournamentCommand.java
+++ b/src/main/java/me/stuffy/stuffybot/commands/TournamentCommand.java
@@ -1,30 +1,160 @@
package me.stuffy.stuffybot.commands;
-import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
-import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
-import net.dv8tion.jda.api.interactions.commands.OptionType;
-import net.dv8tion.jda.api.interactions.commands.build.OptionData;
-
-public class TournamentCommand extends BaseCommand{
- public TournamentCommand(String name, String description){
- super(name, description,
- new OptionData(OptionType.STRING, "tournament", "The tournament, defaults to most recent", false).addChoices(
-
- ),
- new OptionData(OptionType.STRING, "username", "Player's username", false));
- }
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import me.stuffy.stuffybot.interactions.InteractionId;
+import me.stuffy.stuffybot.profiles.HypixelProfile;
+import me.stuffy.stuffybot.utils.APIException;
+import me.stuffy.stuffybot.utils.InvalidOptionException;
+import net.dv8tion.jda.api.interactions.components.buttons.Button;
+import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle;
+import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
+import net.dv8tion.jda.api.utils.messages.MessageCreateData;
- @Override
- protected void onCommand(SlashCommandInteractionEvent event) {
- String tournament = event.getOption("tournament").getAsString();
- String username = event.getOption("username").getAsString();
- }
+import java.util.ArrayList;
+import java.util.Collection;
- protected void onButton(ButtonInteractionEvent event) {
- }
+import static me.stuffy.stuffybot.utils.APIUtils.getHypixelProfile;
+import static me.stuffy.stuffybot.utils.APIUtils.getTournamentData;
+import static me.stuffy.stuffybot.utils.DiscordUtils.discordTimeUnix;
+import static me.stuffy.stuffybot.utils.DiscordUtils.makeStatsEmbed;
+import static me.stuffy.stuffybot.utils.MiscUtils.minutesFormatted;
+
+public class TournamentCommand {
+
+ public static MessageCreateData tournament(InteractionId interactionId) throws APIException, InvalidOptionException {
+ String ign = interactionId.getOption("ign");
+ HypixelProfile hypixelProfile = getHypixelProfile(ign);
+ String username = hypixelProfile.getDisplayName();
+
+ int tournamentId = interactionId.getOption("tournament", -1);
+
+ JsonObject tournamentData = getTournamentData();
+ if (tournamentData == null) {
+ throw new APIException("Stuffy", "Failed to load tournament data.");
+ }
+
+ JsonObject selectedTournament = null;
+ int highestId = -1;
+ for (JsonElement entry : tournamentData.getAsJsonArray("tournaments")) {
+ JsonObject tournament = entry.getAsJsonObject();
+ int id = tournament.get("id").getAsInt();
+ if (id > highestId) {
+ highestId = id;
+ if (tournamentId == -1) {
+ selectedTournament = tournament;
+ }
+ }
+ if (id == tournamentId) {
+ selectedTournament = tournament;
+ }
+ }
+ if (tournamentId == -1) {
+ tournamentId = highestId;
+ }
+
+ if (selectedTournament == null) {
+ throw new InvalidOptionException("tournament", "Invalid tournament ID.");
+ }
+
+ String tournamentName = selectedTournament.get("name").getAsString();
+ int tournamentIteration = selectedTournament.get("iteration").getAsInt();
+ JsonObject tournamentDuration = selectedTournament.get("duration").getAsJsonObject();
+ long tournamentStartTime = tournamentDuration.get("start").getAsLong();
+ long tournamentEndTime = tournamentDuration.get("end").getAsLong();
+ long currentTime = System.currentTimeMillis() / 1000;
+
+ JsonObject selectedTournamentData = selectedTournament.get("data").getAsJsonObject();
+ String tournamentField = selectedTournamentData.get("tourneyField").getAsString();
+
+ int tributes = hypixelProfile.getTourneyTributesEarned(tournamentField);
+ int playtime = hypixelProfile.getTourneyTimePlayed(tournamentField);
+
+ StringBuilder title = new StringBuilder();
+ title.append(tournamentName);
+ if (tournamentIteration != 0) {
+ title.append(" #").append(tournamentIteration + 1);
+ }
+
+ String subtitle = " From " + discordTimeUnix(tournamentStartTime * 1000) + " to " +
+ discordTimeUnix(tournamentEndTime * 1000) + "\n-# ID:" + tournamentId;
+
+ StringBuilder description = new StringBuilder();
+ description.append("### **").append(username).append("**'s Tournament Stats\n");
+ if(selectedTournamentData.has("gameLimit")){
+ int gameLimit = selectedTournamentData.get("gameLimit").getAsInt();
+ int gamesPlayed = hypixelProfile.getTourneyGamesPlayed(tournamentField);
+ description.append(gamesPlayed).append("/").append(gameLimit).append(" Games Played\n");
+ } else {
+ int timeLimit = selectedTournamentData.get("timeLimit").getAsInt();
+ description.append("Time Played: ").append(minutesFormatted(playtime)).append("/").append(minutesFormatted(timeLimit)).append("\n");
+ }
+ description.append(tributes).append("/100 Tributes Earned\n");
+ description.append("Playtime: ").append(minutesFormatted(playtime)).append("\n");
+ description.append("\n");
+
+ if (selectedTournamentData.has("wins")) {
+ String winsField = selectedTournamentData.get("wins").getAsString();
+ description.append("Wins: ").append(hypixelProfile.getStat(winsField));
+ if (selectedTournamentData.has("losses")) {
+ String lossesField = selectedTournamentData.get("losses").getAsString();
+ description.append(", Losses: ").append(hypixelProfile.getStat(lossesField)).append("\n");
+ } else {
+ description.append("\n");
+ }
+ if (selectedTournamentData.has("winstreak")) {
+ description.append("Win Streak: ").append(hypixelProfile.getStat(selectedTournamentData.get("winstreak").getAsString())).append("\n");
+ }
+ }
+
+ description.append("\n");
+
+ if (selectedTournamentData.has("kills")) {
+ description.append("Kills: ").append(hypixelProfile.getStat(selectedTournamentData.get("kills").getAsString()));
+ if (selectedTournamentData.has("killstreak")) {
+ description.append(", Kill Streak: ").append(hypixelProfile.getStat(selectedTournamentData.get("killstreak").getAsString())).append("\n");
+ } else {
+ description.append("\n");
+ }
+ }
+
+ if (selectedTournamentData.has("assists")) {
+ description.append("Assists: ").append(hypixelProfile.getStat(selectedTournamentData.get("assists").getAsString())).append("\n");
+ }
+ if (selectedTournamentData.has("deaths")) {
+ description.append("Deaths: ").append(hypixelProfile.getStat(selectedTournamentData.get("deaths").getAsString())).append("\n");
+ }
+
+
+ String future = ":date:";
+ String past = ":checkered_flag:";
+ String active = ":video_game:";
+
+ String emoji;
+
+ if (currentTime < tournamentStartTime) {
+ emoji = future;
+ } else if (currentTime > tournamentEndTime) {
+ emoji = past;
+ } else {
+ emoji = active;
+ }
- @Override
- protected void cleanupEventResources(String messageId) {
+ Collection