diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..f37a3ef --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,15 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: stuffydev # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry +polar: # Replace with a single Polar username +buy_me_a_coffee: # Replace with a single Buy Me a Coffee username +thanks_dev: # Replace with a single thanks.dev username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.gitignore b/.gitignore index 524f096..fe809a4 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* replay_pid* + +# Configuration files +*.properties \ No newline at end of file diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..c03fdf3 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +worker: java -jar ./target/stuffybot-java-1.0-SNAPSHOT.jar \ No newline at end of file diff --git a/pom.xml b/pom.xml index 6dfc478..46c65cc 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,7 @@ + org.apache.maven.plugins maven-compiler-plugin @@ -23,13 +24,56 @@ 17 + + org.apache.maven.plugins + maven-jar-plugin + 3.2.0 + + + + me.stuffy.stuffybot.Bot + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + + + me.stuffy.stuffybot.Bot + + + + + + + + + + com.fasterxml.jackson + jackson-bom + 2.15.4 + pom + import + + + net.dv8tion JDA - 5.0.0 + 6.2.0 com.google.code.gson @@ -39,7 +83,17 @@ com.google.guava guava - 30.1-jre + 33.4.8-jre + + + org.kohsuke + github-api + 1.324 + + + com.opencsv + opencsv + 5.5.2 \ 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 366922c..4d3acd6 100644 --- a/src/main/java/me/stuffy/stuffybot/Bot.java +++ b/src/main/java/me/stuffy/stuffybot/Bot.java @@ -4,7 +4,8 @@ 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.profiles.GlobalData; +import me.stuffy.stuffybot.utils.Config; import me.stuffy.stuffybot.utils.Logger; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDABuilder; @@ -14,38 +15,61 @@ 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.IntegrationType; +import net.dv8tion.jda.api.interactions.InteractionContextType; +import net.dv8tion.jda.api.interactions.commands.Command; +import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; 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 net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; +import org.kohsuke.github.GitHub; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; +import static me.stuffy.stuffybot.utils.APIUtils.connectToGitHub; +import static me.stuffy.stuffybot.utils.APIUtils.uploadLogs; + public class Bot extends ListenerAdapter { private static Bot INSTANCE; private final JDA jda; - private Guild homeGuild; + private final Guild homeGuild; + private static GitHub GITHUB; + private static GlobalData GLOBAL_DATA; public Bot() throws InterruptedException { INSTANCE = this; // Get token from env variable String token = System.getenv("BOT_TOKEN"); - JDABuilder builder = JDABuilder.createDefault(token) ; - builder.setActivity(Activity.customStatus("hating slash commands")); + JDABuilder builder = JDABuilder.createDefault(token); +// builder.enableIntents(GatewayIntent.MESSAGE_CONTENT); // # TODO: Remove intents when possible + String customStatus = Config.getCustomStatus(); + builder.setActivity(Activity.customStatus(customStatus)); builder.addEventListeners(this); JDA jda = builder.build().awaitReady(); this.jda = jda; + String homeGuildID = Config.getHomeGuildId(); // Initialize home guild - this.homeGuild = jda.getGuildById("795108903733952562"); + this.homeGuild = jda.getGuildById(homeGuildID); assert this.homeGuild != null : "Failed to find home guild"; - // Log startup - String time = DiscordUtils.discordTimeNow(); - String self = jda.getSelfUser().getAsMention(); - Logger.log(" Bot " + self + " started successfully " + time + "."); + String startupTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss")); + String self = jda.getSelfUser().getName(); + Logger.setLogName(startupTime); + String environment = Config.getEnvironment(); + Logger.log(" Bot " + self + " started successfully " + startupTime + ". Environment: " + environment); + + // Initialize GitHub + GITHUB = connectToGitHub(); + + // Initialize Global Data + GLOBAL_DATA = new GlobalData(); // Listen for interactions jda.addEventListener( @@ -53,23 +77,65 @@ public Bot() throws InterruptedException { ); // Register commands "global"ly or "local"ly - registerCommands("local"); + String environmentScope = switch (Config.getEnvironment()) { + case "development" -> "local"; + case "production", "development_global" -> "global"; + default -> throw new IllegalArgumentException("Invalid environment: " + Config.getEnvironment()); + }; + registerCommands(environmentScope); // Start events new UpdateBotStatsEvent().startFixedRateEvent(); new ActiveEvents().startFixedRateEvent(); + + // Handle SIGTERM + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + Logger.log(" Bot shutting down, saving data..."); + + // Close JDA + jda.shutdown(); + + // Update Bot Stats + try{ + UpdateBotStatsEvent.publicExecute(); + Logger.log(" Bot Stats saved, allowing for shutdown."); + } catch (Exception e) { + Logger.log(" Failed to save Bot Stats, allowing for shutdown."); + } + try { + uploadLogs(); + Logger.log(" Logs uploaded, allowing for shutdown."); + } + catch (Exception e) { + Logger.log(" Failed to upload logs, allowing for shutdown."); + } + })); } public static Bot getInstance() { return INSTANCE; } + public static GitHub getGitHub() { + return GITHUB; + } + + public static GlobalData getGlobalData() { + return GLOBAL_DATA; + } + public Guild getHomeGuild() { return this.homeGuild; } public Role getVerifiedRole() { - return this.homeGuild.getRoleById("795118862940635216"); + String roleID = Config.getVerifiedRoleId(); + return this.homeGuild.getRoleById(roleID); + } + + public Role getNotVerifiedRole() { + String roleID = Config.getNotVerifiedRoleId(); + return this.homeGuild.getRoleById(roleID); } public static void main(String[] args) throws InterruptedException { @@ -93,31 +159,53 @@ public void onGuildLeave(GuildLeaveEvent event) { Logger.log(" Bot left guild: " + leftGuild.getName() + " (" + leftGuild.getId() + ")"); } - public void registerCommands(String scope) { + private SlashCommandData createSlashCommand(String name, String description) { + return Commands.slash(name, description).setContexts(InteractionContextType.ALL).setIntegrationTypes(IntegrationType.ALL); + } + + private void registerCommands(String scope) { OptionData ignOption = new OptionData(OptionType.STRING, "ign", "The player's IGN", false); + OptionData ignOptionRequired = new OptionData(OptionType.STRING, "ign", "The player's IGN", true); // 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") + commandList.add(createSlashCommand("help", "Learn about the bot and its commands")); + commandList.add(createSlashCommand("pit", "Get Pit stats for a player") .addOptions(ignOption)); - commandList.add(Commands.slash("stats", "Get Hypixel stats for a player") + commandList.add(createSlashCommand("stats", "Get Hypixel stats for a player") .addOptions(ignOption)); - commandList.add(Commands.slash("tkr", "Get TKR stats for a player") + commandList.add(createSlashCommand("tkr", "Get TKR stats for a player") .addOptions(ignOption)); - commandList.add(Commands.slash("maxes", "Get maxed games for a player") + commandList.add(createSlashCommand("maxes", "Get maxed games for a player") .addOptions(ignOption)); - commandList.add(Commands.slash("blitz", "Get Blitz Ultimate Kit xp for a player") + commandList.add(createSlashCommand("blitz", "Get Blitz Ultimate Kit xp for a player") .addOptions(ignOption)); - commandList.add(Commands.slash("megawalls", "Get Mega Walls skins for a player") + commandList.add(createSlashCommand("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") + commandList.add(createSlashCommand("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))); + commandList.add(createSlashCommand("achievements", "Get achievement stats for a player") + .addOptions(ignOption) + .addOptions(new OptionData(OptionType.STRING, "game", "Which game to look at", false).setAutoComplete(true)) + .addOptions(new OptionData(OptionType.STRING, "type", "Which achievements to look at", false).addChoices( + new Command.Choice("All", "all"), + new Command.Choice("Challenge", "challenge"), + new Command.Choice("Tiered", "tiered") + ) + )); + commandList.add(createSlashCommand("link", "Link a Minecraft account so you don't have to type your IGN every time") + .addOptions(ignOptionRequired)); + commandList.add(createSlashCommand("playcommand", "Lookup the command to quickly hop into a game") + .addOptions(new OptionData(OptionType.STRING, "game", "Search for a play command", true).setAutoComplete(true))); + commandList.add(createSlashCommand("search", "Search for an achievement by name, or description.") + .addOptions(new OptionData(OptionType.STRING, "search", "Search for an Achievement", true).setAutoComplete(true))); + commandList.add(createSlashCommand("uuid", "Get UUID info for a Minecraft player") + .addOptions(ignOptionRequired)); if (scope.equals("local")) { - //clearLocalCommands(); + jda.updateCommands().queue(); this.homeGuild.updateCommands().addCommands( commandList ).queue(); @@ -130,15 +218,14 @@ public void registerCommands(String scope) { } 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."); + // Setup commands for home guild only + this.homeGuild.upsertCommand( + Commands.slash("setup", "Home guild setup command") + .setDefaultPermissions(DefaultMemberPermissions.DISABLED) + .addOptions(new OptionData(OptionType.STRING, "tosetup", "Which thing you wish to Setup", true).addChoices( + new Command.Choice("Verify", "verify") + )) + ).queue(); } } diff --git a/src/main/java/me/stuffy/stuffybot/commands/AchievementsCommand.java b/src/main/java/me/stuffy/stuffybot/commands/AchievementsCommand.java new file mode 100644 index 0000000..92d7c6b --- /dev/null +++ b/src/main/java/me/stuffy/stuffybot/commands/AchievementsCommand.java @@ -0,0 +1,204 @@ +package me.stuffy.stuffybot.commands; + +import com.google.gson.JsonArray; +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.components.actionrow.ActionRow; +import net.dv8tion.jda.api.components.buttons.Button; +import net.dv8tion.jda.api.components.buttons.ButtonStyle; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; + +import java.text.DecimalFormat; +import java.util.*; + +import static me.stuffy.stuffybot.utils.APIUtils.getAchievementsResources; +import static me.stuffy.stuffybot.utils.APIUtils.getHypixelProfile; +import static me.stuffy.stuffybot.utils.DiscordUtils.makeStatsEmbed; +import static me.stuffy.stuffybot.utils.MiscUtils.*; + +public class AchievementsCommand { + private static final Map gameDataCache = new HashMap<>(); + + public static MessageCreateData achievements(InteractionId interactionId) throws APIException, InvalidOptionException { + String ign = interactionId.getOption("ign"); + HypixelProfile hypixelProfile = getHypixelProfile(ign); + String username = hypixelProfile.getDisplayName(); + + String readableName = interactionId.getOption("game", "none"); + String viewType = interactionId.getOption("type", "all"); + + JsonObject gameAchievements = null; + + if (readableName.equals("none")) { + StringBuilder content = new StringBuilder(); + int unlockedAchievements = hypixelProfile.getAchievementsUnlocked(); + int maxAchievements = getMaxAchievements(); + int achievementPoints = hypixelProfile.getAchievementPoints(); + int maxAchievementPoints = getMaxAchievementPoints(); + + int legacyUnlocked = hypixelProfile.getLegacyAchievementsUnlocked(); + int legacyPoints = hypixelProfile.getLegacyAchievementPoints(); + + DecimalFormat thousands = new DecimalFormat("#,###"); + DecimalFormat percentage = new DecimalFormat("#.##"); + + content.append("Unlocked: **").append(thousands.format(unlockedAchievements)).append("**/").append(thousands.format(maxAchievements)).append(" (").append(percentage.format((double) unlockedAchievements / maxAchievements * 100)).append("%)\n"); + content.append("Points: **").append(thousands.format(achievementPoints)).append("**/").append(thousands.format(maxAchievementPoints)).append(" (").append(percentage.format((double) achievementPoints / maxAchievementPoints * 100)).append("%)\n\n"); + content.append("Legacy Unlocked: **").append(thousands.format(legacyUnlocked)).append("**\n"); + content.append("Legacy Points: **").append(thousands.format(legacyPoints)).append("**"); + +// content.append("\n\nEasiest challenge: **").append(hypixelProfile.getEasiestChallenge()).append("**\n"); +// content.append("Closest tiered: **").append(hypixelProfile.getEasiestTiered()).append("**\n"); + + return new MessageCreateBuilder() + .addEmbeds(makeStatsEmbed("Achievement Data for " + username, content.toString())) + .build(); + } + + JsonObject achievementData = getAchievementsResources().getAsJsonObject(); + String gameId = fromReadableName(readableName); + + String _id = interactionId.getId(); + if (gameDataCache.containsKey(_id)) { + gameAchievements = gameDataCache.get(_id); + } else { + for (String game : achievementData.keySet()) { + if (Objects.equals(game, gameId)) { + gameAchievements = achievementData.getAsJsonObject(game); + gameDataCache.put(_id, gameAchievements); + } + } + } + + if (gameAchievements == null) { + throw new InvalidOptionException("game", readableName); + } + + MessageCreateBuilder messageCreateBuilder = new MessageCreateBuilder(); + + + Button allButton = Button.of(ButtonStyle.PRIMARY, interactionId.setOption("type", "all").getInteractionString(), "All"); + Button challengeButton = Button.of(ButtonStyle.PRIMARY, interactionId.setOption("type", "challenge").getInteractionString(), "Challenge"); + Button tieredButton = Button.of(ButtonStyle.PRIMARY, interactionId.setOption("type", "tiered").getInteractionString(), "Tiered"); + + String embedTitle = ""; + String embedContent = ""; + + List challengeUnlocked = new ArrayList<>(); + List challengeLocked = new ArrayList<>(); + int challengeMaxUnlocked = 0; + int challengeMaxPoints = 0; + int challengeUnlockedPoints = 0; + JsonArray challengeAchievements = hypixelProfile.getAchievements().get("achievementsOneTime").getAsJsonArray(); + List playerOneTimeString = new ArrayList<>(); + for (JsonElement element : challengeAchievements) { + try { + playerOneTimeString.add(element.getAsString()); + } catch (Exception ignored) { + } + } + for (String achievement : gameAchievements.get("one_time").getAsJsonObject().keySet()) { + String inData = gameId + "_" + achievement.toLowerCase(); + JsonObject achievementObject = gameAchievements.get("one_time").getAsJsonObject().get(achievement).getAsJsonObject(); + String readableAchievement = achievementObject.get("name").getAsString() + achievementObject.get("points").getAsString(); + int points = achievementObject.get("points").getAsInt(); + if(achievementObject.has("legacy")) { + if (achievementObject.get("legacy").getAsBoolean()) { + continue; + } + } + challengeMaxUnlocked++; + challengeMaxPoints += points; + + + if (playerOneTimeString.contains(inData)) { + challengeUnlockedPoints += points; + challengeUnlocked.add(readableAchievement); + } else { + challengeLocked.add(readableAchievement); + } + } + + int tieredMaxUnlocked = 0; + int tieredMaxPoints = 0; + int tieredUnlockedPoints = 0; + int tieredTotalUnlocked = 0; + + + JsonObject tieredAchievements = hypixelProfile.getAchievements().get("achievementsTiered").getAsJsonObject(); + for (String achievement : gameAchievements.get("tiered").getAsJsonObject().keySet()) { + JsonObject achievementObject = gameAchievements.get("tiered").getAsJsonObject().get(achievement).getAsJsonObject(); + if(achievementObject.has("legacy")) { + if (achievementObject.get("legacy").getAsBoolean()) { + continue; + } + } + String readableAchievement = achievementObject.get("name").getAsString(); + JsonArray tieredTiers = achievementObject.get("tiers").getAsJsonArray(); + String inData = gameId + "_" + achievement.toLowerCase(); + + int tierCount = 0; + int tieredUnlocked = 0; + for (JsonElement tier : tieredTiers) { + JsonObject tierObject = tier.getAsJsonObject(); + int points = tierObject.get("points").getAsInt(); + int amount = tierObject.get("amount").getAsInt(); + tieredMaxUnlocked++; + tieredMaxPoints += points; + tierCount++; + + if (tieredAchievements.has(inData)) { + int playerProgress = tieredAchievements.get(inData).getAsInt(); + if (playerProgress >= amount) { + tieredUnlocked++; + tieredUnlockedPoints += points; + } + } + } + tieredTotalUnlocked += tieredUnlocked; + } + + + if (Objects.equals(viewType, "challenge")) { + challengeButton = challengeButton.asDisabled(); + embedTitle = readableName + " Challenge Achievements for " + username; + + embedContent += "Unlocked: " + challengeUnlocked.size() + "/" + challengeMaxUnlocked + "\n"; + embedContent += "Points: " + challengeUnlockedPoints + "/" + challengeMaxPoints + "\n\n"; + } + if (Objects.equals(viewType, "tiered")) { + tieredButton = tieredButton.asDisabled(); + embedTitle = readableName + " Tiered Achievements for " + username; + + embedContent += "Unlocked: " + tieredTotalUnlocked + "/" + tieredMaxUnlocked + "\n"; + embedContent += "Points: " + tieredUnlockedPoints + "/" + tieredMaxPoints + "\n\n"; + } + if (Objects.equals(viewType, "all")) { + allButton = allButton.asDisabled(); + embedTitle = readableName + " Achievement Summary for " + username; + + int totalUnlocked = challengeUnlocked.size() + tieredTotalUnlocked; + int totalMaxUnlocked = challengeMaxUnlocked + tieredMaxUnlocked; + int totalPoints = challengeUnlockedPoints + tieredUnlockedPoints; + int totalMaxPoints = challengeMaxPoints + tieredMaxPoints; + embedContent += "Total Unlocked: " + totalUnlocked + "/" + totalMaxUnlocked + "\n"; + embedContent += "Challenge Unlocked: " + challengeUnlocked.size() + "/" + challengeMaxUnlocked + "\n"; + embedContent += "Tiered Unlocked: " + tieredTotalUnlocked + "/" + tieredMaxUnlocked + "\n\n"; + + embedContent += "Total Points: " + totalPoints + "/" + totalMaxPoints + "\n"; + embedContent += "Challenge Points: " + challengeUnlockedPoints + "/" + challengeMaxPoints + "\n"; + embedContent += "Tiered Points: " + tieredUnlockedPoints + "/" + tieredMaxPoints + "\n\n"; + } + + messageCreateBuilder.setComponents(ActionRow.of(allButton, challengeButton, tieredButton)); + + messageCreateBuilder.addEmbeds(makeStatsEmbed(embedTitle, embedContent)); + + return messageCreateBuilder.build(); + } +} diff --git a/src/main/java/me/stuffy/stuffybot/commands/HelpCommand.java b/src/main/java/me/stuffy/stuffybot/commands/HelpCommand.java new file mode 100644 index 0000000..176e43c --- /dev/null +++ b/src/main/java/me/stuffy/stuffybot/commands/HelpCommand.java @@ -0,0 +1,44 @@ +package me.stuffy.stuffybot.commands; + +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; + +import java.util.HashMap; +import java.util.Map; + +import static me.stuffy.stuffybot.utils.DiscordUtils.makeEmbed; + +public class HelpCommand { + public static MessageCreateData help() { + Map commands = new HashMap<>(); + commands.put("`/link`", "Set a Default account to check when running commands"); + commands.put("`/stats`", "Get your Hypixel stats"); + commands.put("`/maxes`", "Get your maxed games"); + commands.put("`/playcommand`", "Lookup the command to quickly hop into a game"); + commands.put("`/tournament`", "Get your tournament stats"); + + StringBuilder commandList = new StringBuilder(); + for (Map.Entry entry : commands.entrySet()) { + commandList.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); + } + + String helpText = "-# Bot Commands\n" + commandList; + helpText += """ + ...and more! + + -# Discord Server + Join the [discord server](https://discord.gg/X6WJT7WNVz) for help, feedback, and more! + We also announce Staff Rank changes, Hypixel leaks, and more! + + -# Source Code + The source code for this bot, its ToS, and Privacy Policy are + available on [GitHub](https://github.com/stuffyerface/stuffybot-java). + Feel free to contribute, report bugs, or suggest new features! + Don't forget to follow, star, and check out our API! + """; + + return new MessageCreateBuilder().addEmbeds( + makeEmbed("Stuffy Bot Help Menu", null, helpText, 0x570FF4, 30) + ).build(); + } +} diff --git a/src/main/java/me/stuffy/stuffybot/commands/LinkCommand.java b/src/main/java/me/stuffy/stuffybot/commands/LinkCommand.java new file mode 100644 index 0000000..f91b5f8 --- /dev/null +++ b/src/main/java/me/stuffy/stuffybot/commands/LinkCommand.java @@ -0,0 +1,71 @@ +package me.stuffy.stuffybot.commands; + +import me.stuffy.stuffybot.Bot; +import me.stuffy.stuffybot.interactions.InteractionId; +import me.stuffy.stuffybot.profiles.GlobalData; +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.HashMap; +import java.util.Map; +import java.util.UUID; + +import static me.stuffy.stuffybot.utils.APIUtils.getHypixelProfile; +import static me.stuffy.stuffybot.utils.APIUtils.updateLinkedDB; +import static me.stuffy.stuffybot.utils.DiscordUtils.makeEmbed; +import static me.stuffy.stuffybot.utils.DiscordUtils.makeErrorEmbed; + +public class LinkCommand { + static Map linkDelay = new HashMap<>(); + static int linkDelayTime = 60000; + public static MessageCreateData link(InteractionId interactionId) throws APIException { + String ign = interactionId.getOptions().get("ign"); + String discordId = interactionId.getUserId(); + + HypixelProfile hypixelProfile = getHypixelProfile(ign); + + String mcUsername = hypixelProfile.getDisplayName(); + UUID uuid = hypixelProfile.getUuid(); + + // #TODO: Check if the user is verified + long current = System.currentTimeMillis(); + if (linkDelay.containsKey(discordId) && current - linkDelay.get(discordId) < linkDelayTime) { + return new MessageCreateBuilder() + .addEmbeds(makeErrorEmbed("Account linking failed", "Please wait a bit before trying to link your account again.")) + .build(); + } + + GlobalData globalData = Bot.getGlobalData(); + Map linkedAccounts = globalData.getLinkedAccounts(); + + if (linkedAccounts.containsKey(discordId) && linkedAccounts.get(discordId).equals(uuid)){ + return new MessageCreateBuilder() + .addEmbeds(makeErrorEmbed("Account linking failed", "You are already linked to this account.")) + .build(); + } + + linkDelay.put(discordId, current); + + try { + updateLinkedDB(discordId, uuid, mcUsername); + } catch (Exception e) { + e.printStackTrace(); + return new MessageCreateBuilder() + .addEmbeds(makeErrorEmbed("Account linking failed", "An error occurred while linking your account. Please try again later.")) + .build(); + } + + MessageEmbed linkEmbed = makeEmbed( + "Account Linked", + "Successfully linked as **" + mcUsername + "**!", + "You can now run commands without the ign parameter.", + 0x6AC672 + ); + + return new MessageCreateBuilder() + .addEmbeds(linkEmbed).build(); + } +} diff --git a/src/main/java/me/stuffy/stuffybot/commands/PitCommand.java b/src/main/java/me/stuffy/stuffybot/commands/PitCommand.java index e26ad92..4763075 100644 --- a/src/main/java/me/stuffy/stuffybot/commands/PitCommand.java +++ b/src/main/java/me/stuffy/stuffybot/commands/PitCommand.java @@ -4,6 +4,7 @@ import me.stuffy.stuffybot.interactions.InteractionId; import me.stuffy.stuffybot.profiles.HypixelProfile; import me.stuffy.stuffybot.utils.APIException; +import net.dv8tion.jda.api.components.actionrow.ActionRow; import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import net.dv8tion.jda.api.utils.messages.MessageCreateData; @@ -13,7 +14,7 @@ import static me.stuffy.stuffybot.utils.APIUtils.getHypixelProfile; 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.secondary; +import static net.dv8tion.jda.api.components.buttons.Button.secondary; public class PitCommand { @@ -48,9 +49,9 @@ public static MessageCreateData pit(InteractionId interactionId) throws APIExcep String newInteractionId = InteractionId.newCommand("pitDetailed", interactionId).getInteractionString(); return new MessageCreateBuilder() .addEmbeds(pitStats) - .addActionRow( + .setComponents(ActionRow.of( secondary(newInteractionId, "Challenge Achievement Progress") - ) + )) .build(); } @@ -88,9 +89,9 @@ public static MessageCreateData pitDetailed(InteractionId interactionId) throws String newInteractionId = InteractionId.newCommand("pit", interactionId).getInteractionString(); return new MessageCreateBuilder() .addEmbeds(extraPitStats) - .addActionRow( + .setComponents(ActionRow.of( secondary(newInteractionId, "Back to Pit Stats") - ) + )) .build(); } } diff --git a/src/main/java/me/stuffy/stuffybot/commands/PlayCommandCommand.java b/src/main/java/me/stuffy/stuffybot/commands/PlayCommandCommand.java new file mode 100644 index 0000000..60298ed --- /dev/null +++ b/src/main/java/me/stuffy/stuffybot/commands/PlayCommandCommand.java @@ -0,0 +1,53 @@ +package me.stuffy.stuffybot.commands; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import me.stuffy.stuffybot.interactions.InteractionId; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; + +import static me.stuffy.stuffybot.utils.APIUtils.getPlayCommands; +import static me.stuffy.stuffybot.utils.DiscordUtils.makeErrorEmbed; +import static me.stuffy.stuffybot.utils.DiscordUtils.makeStatsEmbed; + +public class PlayCommandCommand { + public static MessageCreateData playCommand(InteractionId interactionId) { + String input = interactionId.getOption("game"); + + JsonElement gameData = getPlayCommands().getAsJsonObject().get("gameData"); + if (gameData == null) { + return new MessageCreateBuilder().setContent("Failed to load play command data.").build(); + } + + JsonArray games = gameData.getAsJsonArray(); + for (JsonElement game : games) { + String gameName = game.getAsJsonObject().get("name").getAsString(); + JsonArray modes = game.getAsJsonObject().get("modes").getAsJsonArray(); + for (JsonElement mode : modes) { + if(!mode.getAsJsonObject().has("identifier") || !mode.getAsJsonObject().has("name")) { + continue; + } + String modeName = mode.getAsJsonObject().get("name").getAsString(); + String identifier = mode.getAsJsonObject().get("identifier").getAsString(); + String fullName; + if (gameName.equals(modeName)) { + fullName = gameName; + } else { + fullName = gameName + ": " + modeName; + } + if (input.equals(identifier)) { + return new MessageCreateBuilder().addEmbeds( + makeStatsEmbed("Play Command Search", "-# Use this to quickly join a game from anywhere.\n" + + "\n**" + fullName + "**\n `/play " + identifier + "`") + ).build(); + } + } + } + + return new MessageCreateBuilder().addEmbeds( + makeErrorEmbed("Invalid Game or Mode", "I don't have a play command for that game or mode." + + "\n-# Try using the search feature.") + ).build(); + } +} diff --git a/src/main/java/me/stuffy/stuffybot/commands/SearchCommand.java b/src/main/java/me/stuffy/stuffybot/commands/SearchCommand.java new file mode 100644 index 0000000..c745038 --- /dev/null +++ b/src/main/java/me/stuffy/stuffybot/commands/SearchCommand.java @@ -0,0 +1,117 @@ +package me.stuffy.stuffybot.commands; + +import com.google.gson.JsonArray; +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.Logger; +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.ArrayList; + +import static me.stuffy.stuffybot.utils.APIUtils.getAchievementsResources; +import static me.stuffy.stuffybot.utils.APIUtils.getHypixelProfile; +import static me.stuffy.stuffybot.utils.DiscordUtils.makeErrorEmbed; +import static me.stuffy.stuffybot.utils.DiscordUtils.makeStatsEmbed; +import static me.stuffy.stuffybot.utils.MiscUtils.toReadableName; + +public class SearchCommand { + public static MessageCreateData search(InteractionId interactionId) { + try{ + String searchTerm = interactionId.getOptions().get("search"); + + String[] parts = searchTerm.split("_", 2); + String gameID = parts[0].toLowerCase(); + String achievementID = parts[1]; + + JsonObject achievementResources = getAchievementsResources().getAsJsonObject(); + JsonObject gameAchievements = achievementResources.get(gameID).getAsJsonObject(); + JsonObject gameOneTimes = gameAchievements.get("one_time").getAsJsonObject(); + JsonObject gameTiered = gameAchievements.get("tiered").getAsJsonObject(); + + String gameType = toReadableName(gameID); + + StringBuilder achievementBody = new StringBuilder(); + + if (gameOneTimes.has(achievementID)) { + // CHALLENGE ACHIEVEMENTS + JsonObject searchedAchievement = gameOneTimes.get(achievementID).getAsJsonObject(); + String achievementName = searchedAchievement.get("name").getAsString(); + String achievementDescription = searchedAchievement.get("description").getAsString(); + Integer achievementPoints = searchedAchievement.get("points").getAsInt(); + + achievementBody.append("-# ").append(gameType).append(" Challenge Achievement Found.\n"); + achievementBody.append("**").append(achievementName).append("** (+**").append(achievementPoints).append("** points)\n"); + achievementBody.append(achievementDescription).append("\n"); + + if (searchedAchievement.has("gamePercentUnlocked") && searchedAchievement.has("globalPercentUnlocked")){ + DecimalFormat df = new DecimalFormat("#0.00"); + Double gamePercentUnlocked = searchedAchievement.get("gamePercentUnlocked").getAsDouble(); + Double globalPercentUnlocked = searchedAchievement.get("globalPercentUnlocked").getAsDouble(); + achievementBody.append("\nUnlocked by `"); + achievementBody.append(df.format(gamePercentUnlocked)); + achievementBody.append("%` of ").append(gameType).append(", `"); + achievementBody.append(df.format(globalPercentUnlocked)); + achievementBody.append("%` of all players"); + } + + if(searchedAchievement.has("legacy") && searchedAchievement.get("legacy").getAsBoolean()){ + achievementBody.append("\n-# Legacy Achievement"); + } + } else if (gameTiered.has(achievementID)) { + // TIERED ACHIEVEMENTS + JsonObject searchedAchievement = gameTiered.get(achievementID).getAsJsonObject(); + String achievementName = searchedAchievement.get("name").getAsString(); + String achievementDescription = searchedAchievement.get("description").getAsString(); + + achievementBody.append("-# ").append(gameType).append(" Tiered Achievement Found.\n"); + achievementBody.append("**").append(achievementName).append("**\n"); + + DecimalFormat df = new DecimalFormat("#,###"); + JsonArray tiers = searchedAchievement.get("tiers").getAsJsonArray(); + Integer maxReq = 0; + StringBuilder tiersBuilder = new StringBuilder(); + tiersBuilder.append("```"); + for (JsonElement tier : tiers){ + int points = tier.getAsJsonObject().get("points").getAsInt(); + int amount = tier.getAsJsonObject().get("amount").getAsInt(); + tiersBuilder.append("\n ").append(df.format(amount)).append(" | ").append(points).append(" points"); + + if(amount > maxReq) { + maxReq = amount; + } + } + tiersBuilder.append("```"); + + achievementBody.append(achievementDescription.replace("%s", df.format(maxReq))).append("\n"); + achievementBody.append(tiersBuilder); + + if(searchedAchievement.has("legacy") && searchedAchievement.get("legacy").getAsBoolean()){ + achievementBody.append("\n-# Legacy Achievement"); + } + } else { + throw new Exception(); + } + + achievementBody.append("\n-# Internal ID: `").append(searchTerm).append("`."); + + return new MessageCreateBuilder().addEmbeds( + makeStatsEmbed("Achievement Search Result", achievementBody.toString()) + ).build(); + } catch (Exception e) { + Logger.logError(e.getMessage()); + MessageEmbed errorEmbed = makeErrorEmbed( + "Something went wrong", + "You should report this!" + ); + return new MessageCreateBuilder() + .addEmbeds(errorEmbed) + .build(); + } + } +} diff --git a/src/main/java/me/stuffy/stuffybot/commands/SetupCommand.java b/src/main/java/me/stuffy/stuffybot/commands/SetupCommand.java new file mode 100644 index 0000000..28e75bb --- /dev/null +++ b/src/main/java/me/stuffy/stuffybot/commands/SetupCommand.java @@ -0,0 +1,43 @@ +package me.stuffy.stuffybot.commands; + +import me.stuffy.stuffybot.utils.Config; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.components.actionrow.ActionRow; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.components.buttons.Button; +import net.dv8tion.jda.api.components.buttons.ButtonStyle; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; + +public class SetupCommand { + public static void setupLinkingButton(SlashCommandInteractionEvent event) { + String linkCommandId = Config.getLinkCommandId(); + EmbedBuilder embedBuilder = new EmbedBuilder(); + embedBuilder.setTitle("Verify or Update"); + embedBuilder.setDescription( + "Verification gives you permission to chat in this discord and have linked roles based on your Hypixel Stats. You must have a discord linked in game to do that. You can always use the button below to unverify your account, but you will lose all perks of being a verified user.\n" + + "-# :globe_with_meridians: You do __not__ need to verify your account to use commands inside or outside of this discord, receive announcements from Stuffy Bot, or any other feature we offer.\n" + + "\n" + + "If you just wish to link your account so slash commands will automatically assume your username for the `ign` field, you may use use " + + "" + + ", which will not require verifying in game.\n" + + "\n" + + "If you've earned new accomplishments and want to update them, click the update button below.\n" + + "-# :warning: Stuffy Bot and Staff of this Discord will __never__ ask for your passwords or other personal information, please protect yourself online." + ); + embedBuilder.setFooter("Stuffy Bot by @stuffy"); + embedBuilder.setColor(0x3d84a2); + embedBuilder.build(); + + + MessageCreateData toBeSent = new MessageCreateBuilder().addEmbeds( + embedBuilder.build() + ).setComponents(ActionRow.of( + Button.of(ButtonStyle.SECONDARY, "000:verify:null", "Verify"), + Button.of(ButtonStyle.SECONDARY, "000:update:null", "Update"), + Button.of(ButtonStyle.DANGER, "000:unverify:null", "Unverify") + )).build(); + + event.getChannel().sendMessage(toBeSent).queue(); + } +} diff --git a/src/main/java/me/stuffy/stuffybot/commands/TournamentCommand.java b/src/main/java/me/stuffy/stuffybot/commands/TournamentCommand.java index 8e715fc..c66ee4a 100644 --- a/src/main/java/me/stuffy/stuffybot/commands/TournamentCommand.java +++ b/src/main/java/me/stuffy/stuffybot/commands/TournamentCommand.java @@ -6,8 +6,9 @@ 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.components.actionrow.ActionRow; +import net.dv8tion.jda.api.components.buttons.Button; +import net.dv8tion.jda.api.components.buttons.ButtonStyle; import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import net.dv8tion.jda.api.utils.messages.MessageCreateData; @@ -154,7 +155,7 @@ public static MessageCreateData tournament(InteractionId interactionId) throws A return new MessageCreateBuilder() .addEmbeds(makeStatsEmbed(emoji + " " + title.toString(), subtitle, description.toString())) - .addActionRow(buttons) + .setComponents(ActionRow.of(buttons)) .build(); } } diff --git a/src/main/java/me/stuffy/stuffybot/commands/UuidCommand.java b/src/main/java/me/stuffy/stuffybot/commands/UuidCommand.java new file mode 100644 index 0000000..3279a2e --- /dev/null +++ b/src/main/java/me/stuffy/stuffybot/commands/UuidCommand.java @@ -0,0 +1,68 @@ +package me.stuffy.stuffybot.commands; + +import me.stuffy.stuffybot.interactions.InteractionId; +import me.stuffy.stuffybot.profiles.MojangProfile; +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.UUID; + +import static me.stuffy.stuffybot.utils.APIUtils.getMojangProfile; +import static me.stuffy.stuffybot.utils.DiscordUtils.makeStatsEmbed; + +public class UuidCommand { + public static MessageCreateData uuid(InteractionId interactionId) throws APIException { + String ign = interactionId.getOptions().get("ign"); + MojangProfile mojangProfile = getMojangProfile(ign); + UUID uuid = mojangProfile.getUuid(); + UUIDStats uuidStats = new UUIDStats(mojangProfile.getUsername(), uuid); + + + MessageEmbed uuidEmbed = makeStatsEmbed("UUID Data", + "`" + uuidStats.username + "`'s UUID is `" + uuidStats.uuid + "`\n" + + "That's better than `" + uuidStats.getFormattedBetterThanPercentage() + "`% of all UUIDs!" + + "\n-# Position `#" + String.format("%,d", uuidStats.estimatedRank(65340094)) + "`." + ); + + return new MessageCreateBuilder() + .addEmbeds(uuidEmbed) + .build(); + + } + + private static class UUIDStats { + private final String username; + private final UUID uuid; + private final double betterThanPercentage; + + public UUIDStats(String username, UUID uuid) { + this.username = username; + this.uuid = uuid; + this.betterThanPercentage = getBetterThanPercentage(); + } + + private double getBetterThanPercentage() { + String characterRanking = "0123456789abcdef"; + String uuid = this.uuid.toString().replace("-", ""); + double totalScore = 0; + double remainingScore = 100.0; + for (int i = 0; i < uuid.length(); i++) { + int totalChars = characterRanking.length(); + int pos = characterRanking.indexOf(uuid.charAt(i)); + totalScore += remainingScore * (pos / (double) totalChars); + remainingScore = remainingScore / 16; + } + return totalScore; + } + + public int estimatedRank(int totalPlayers) { + return (int) (Math.ceil((1 - (this.betterThanPercentage / 100)) * totalPlayers)); + } + + public String getFormattedBetterThanPercentage() { + return String.format("%.2f", this.betterThanPercentage); + } + } +} diff --git a/src/main/java/me/stuffy/stuffybot/events/BaseEvent.java b/src/main/java/me/stuffy/stuffybot/events/BaseEvent.java index 5eeaeee..f5632a1 100644 --- a/src/main/java/me/stuffy/stuffybot/events/BaseEvent.java +++ b/src/main/java/me/stuffy/stuffybot/events/BaseEvent.java @@ -21,7 +21,7 @@ public BaseEvent(String name, long interval, TimeUnit timeUnit) { } public void startFixedRateEvent() { - scheduler.scheduleAtFixedRate(this::execute, 0, interval, timeUnit); + scheduler.scheduleAtFixedRate(this::execute, interval, interval, timeUnit); } protected abstract void execute(); diff --git a/src/main/java/me/stuffy/stuffybot/events/UpdateBotStatsEvent.java b/src/main/java/me/stuffy/stuffybot/events/UpdateBotStatsEvent.java index 2c6ec7c..00e5076 100644 --- a/src/main/java/me/stuffy/stuffybot/events/UpdateBotStatsEvent.java +++ b/src/main/java/me/stuffy/stuffybot/events/UpdateBotStatsEvent.java @@ -1,23 +1,57 @@ package me.stuffy.stuffybot.events; import me.stuffy.stuffybot.Bot; +import me.stuffy.stuffybot.profiles.GlobalData; import me.stuffy.stuffybot.utils.Logger; +import net.dv8tion.jda.api.entities.Guild; +import java.util.Map; import java.util.concurrent.TimeUnit; +import static me.stuffy.stuffybot.utils.APIUtils.updateBotStats; +import static me.stuffy.stuffybot.utils.APIUtils.updateUsersStats; + public class UpdateBotStatsEvent extends BaseEvent{ public UpdateBotStatsEvent() { - super("UpdateBotStats", 1, TimeUnit.HOURS); + super("UpdateBotStats", 2, TimeUnit.HOURS); } @Override protected void execute() { - // How many servers the bot is in - // How many commands have been run + publicExecute(); + } + + public static void publicExecute() { + Logger.log(" Updating bot stats."); Bot bot = Bot.getInstance(); - int totalServers = bot.getJDA().getGuilds().size(); - Logger.log(" Total servers: " + totalServers); + GlobalData globalData = Bot.getGlobalData(); + + Guild[] guilds = bot.getJDA().getGuilds().toArray(new Guild[0]); + int totalUsers = 0; + int totalServers = 0; + for (Guild guild : guilds) { + totalServers++; + totalUsers += guild.getMemberCount(); + } + + Map uniqueUsers = globalData.getSessionUniqueUsers(); + Map commandsRun = globalData.getSessionCommandsRun(); + Map userCommandsRun = globalData.getSessionUserCommandsRun(); + + if(commandsRun.isEmpty()) { + Logger.log(" No data to update."); + } else { + updateBotStats(totalServers, totalUsers, commandsRun); + Logger.log(" Updated bot stats."); + } + if(uniqueUsers.isEmpty()) { + Logger.log(" No unique users to update."); + } else { + updateUsersStats(uniqueUsers, userCommandsRun); + Logger.log(" Updated user stats."); + } -// int totalCommandsRun = bot.getStatisticsManager().getTotalCommandsRun(); + globalData.clearCommandsRun(); + globalData.clearUniqueUsers(); } } diff --git a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java index ae495fa..8d74a7f 100644 --- a/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java +++ b/src/main/java/me/stuffy/stuffybot/interactions/InteractionHandler.java @@ -2,10 +2,9 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import me.stuffy.stuffybot.utils.InteractionException; -import me.stuffy.stuffybot.utils.Logger; -import me.stuffy.stuffybot.utils.StatisticsManager; -import net.dv8tion.jda.api.entities.Message; +import me.stuffy.stuffybot.Bot; +import me.stuffy.stuffybot.profiles.GlobalData; +import me.stuffy.stuffybot.utils.*; import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; @@ -22,7 +21,6 @@ import net.dv8tion.jda.api.utils.messages.MessageEditData; import org.jetbrains.annotations.NotNull; -import java.time.Instant; import java.util.*; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -31,11 +29,12 @@ import java.util.regex.Pattern; import java.util.stream.Stream; +import static me.stuffy.stuffybot.commands.SetupCommand.setupLinkingButton; import static me.stuffy.stuffybot.interactions.InteractionManager.getResponse; -import static me.stuffy.stuffybot.utils.APIUtils.getTournamentData; +import static me.stuffy.stuffybot.utils.APIUtils.*; import static me.stuffy.stuffybot.utils.DiscordUtils.*; -import static me.stuffy.stuffybot.utils.MiscUtils.genBase64; -import static me.stuffy.stuffybot.utils.MiscUtils.requiresIgn; +import static me.stuffy.stuffybot.utils.MiscUtils.*; +import static me.stuffy.stuffybot.utils.Verification.verifyModal; public class InteractionHandler extends ListenerAdapter { private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); @@ -49,16 +48,31 @@ public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { event.deferReply().queue(); ArrayList optionsArray = new ArrayList(); + if (commandName.equals("setup")) { + String tosetup = event.getOption("tosetup").getAsString(); + if (tosetup.equals("verify")) { + setupLinkingButton(event); + MessageEmbed successEmbed = makeEmbed("Verification Setup", "Successful setup", "The Verify Embed has been setup successfully.", 0x3d84a2); + event.getHook().setEphemeral(true).sendMessageEmbeds(successEmbed).queue(); + return; + } + } - if(requiresIgn(commandName) && event.getOption("ign") == null){ - String ign = getUsername(event); + + if (event.getOption("ign") == null) { + String ign = null; + try { + ign = getUsername(event); + } catch (APIException e) { + event.getHook().sendMessageEmbeds(makeErrorEmbed(e.getAPIType() + " API Error", e.getMessage())).setEphemeral(true).queue(); + } optionsArray.add("ign=" + ign); } Pattern pattern = Pattern.compile("[,=:]"); for (OptionMapping option : event.getOptions()) { String optionString = option.getAsString(); - if(pattern.matcher(optionString).find()){ + if (pattern.matcher(optionString).find()) { MessageEmbed errorEmbed = makeErrorEmbed("Slash Command Error", "An error occurred while processing your command.\n-# Invalid character in option `" + option.getName() + "`"); event.getHook().sendMessageEmbeds(errorEmbed).setEphemeral(true).queue(); return; @@ -68,24 +82,32 @@ public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { InteractionId interactionId = new InteractionId(id, commandName, event.getUser().getId(), optionsArray); - Logger.log(" @" + event.getUser().getName() + ": /" + commandName + " " + optionsArray.toString()); + if (event.getIntegrationOwners().isUserIntegration()) { + Logger.log(" @" + event.getUser().getName() + ": /" + commandName + " " + optionsArray); + } else { + Logger.log(" @" + event.getUser().getName() + ": /" + commandName + " " + optionsArray); + } + + GlobalData globalData = Bot.getGlobalData(); + globalData.incrementCommandsRun(event.getUser().getId(), commandName); + globalData.addUniqueUser(event.getUser().getId(), event.getUser().getName()); - MessageCreateData response = null; + MessageCreateData response; try { - response = getResponse(interactionId);; + response = getResponse(interactionId); } catch (InteractionException e) { MessageEmbed errorEmbed = makeErrorEmbed("Slash Command Error", "An error occurred while processing your command.\n-# " + e.getMessage()); event.getHook().sendMessageEmbeds(errorEmbed).setEphemeral(true).queue(); return; } catch (Exception e) { MessageEmbed errorEmbed = makeErrorEmbed("Unknown Error", "Uh Oh! I have no idea what went wrong, report this.\n-# Everybody makes mistakes."); - Logger.logError("Unknown error in command: " + commandName + " " + optionsArray.toString() + " " + e.getMessage()); + Logger.logError("Unknown error in command: " + commandName + " " + optionsArray + " " + e.getMessage()); e.printStackTrace(); event.getHook().sendMessageEmbeds(errorEmbed).setEphemeral(true).queue(); return; } - if(response != null) { + if (response != null) { event.getHook().sendMessage(response).queue(); } @@ -93,7 +115,11 @@ public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { String uid = interactionId.getId(); InteractionHook hook = event.getHook(); ScheduledFuture scheduledFuture = scheduler.schedule(() -> { - hook.editOriginalComponents().queue(); + try { + hook.editOriginalComponents().queue(); + } catch (Exception e) { + Logger.logError("Unable to remove original components, the message may have been deleted."); + } }, 30, TimeUnit.SECONDS); scheduledTasks.put(uid, scheduledFuture); @@ -134,15 +160,35 @@ public void onButtonInteraction(@NotNull ButtonInteractionEvent event) { } catch (InteractionException e) { event.deferEdit().queue(); MessageEmbed errorEmbed = makeErrorEmbed("Invalid Button Ownership", - "You can't use modify commands run by others.\n-# " + e.getMessage()); + "You can't use buttons on commands run by others.\n-# " + e.getMessage()); event.getHook().sendMessageEmbeds(errorEmbed).setEphemeral(true).queue(); return; } } - if (interactionId.getCommand().equals("verify")){ - verifyButton(event); - return; + // Verify Button + switch (interactionId.getCommand()) { + case "verify" -> { + Verification.verifyButton(event); + return; + } + + + // Update Button + case "update" -> { + MessageCreateData data = new MessageCreateBuilder() + .setEmbeds(makeErrorEmbed("OOPS!", "This feature is currently unavailable in preparation for an overhaul.")).build(); + event.reply(data).setEphemeral(true).queue(); + + return; + } + + + // Unverify Button + case "unverify" -> { + Verification.unverifyButton(event); + return; + } } event.deferEdit().queue(); @@ -182,25 +228,13 @@ public void onButtonInteraction(@NotNull ButtonInteractionEvent event) { public void onModalInteraction(@NotNull ModalInteractionEvent event) { String toLog = " @" + event.getUser().getName() + ": `" + event.getModalId() + "`"; for (ModalMapping mapping : event.getValues()) { - toLog += " `" + mapping.getId() + "=" + mapping.getAsString() + "`"; + toLog += " `" + mapping.getCustomId() + "=" + mapping.getAsString() + "`"; } Logger.log(toLog); - if(event.getModalId().equals("verify")) { - String ign = Objects.requireNonNull(event.getValue("ign")).getAsString(); - String captcha = Objects.requireNonNull(event.getValue("captcha")).getAsString(); - if(!captcha.equals("stuffy")) { - // TODO: Make this actually time out for 5 minutes - MessageEmbed errorEmbed = makeErrorEmbed("Verification Error", "You entered the CAPTCHA incorrectly.\n-# Try again in " + discordTimeUnix(Instant.now().plusSeconds(300).toEpochMilli())); - MessageCreateData data = new MessageCreateBuilder() - .addEmbeds(errorEmbed) - .build(); - event.reply(data).setEphemeral(true).queue(); - return; - } - event.reply("You got the captcha right, " + ign).setEphemeral(true).queue(); + if (event.getModalId().equals("verify")) { + verifyModal(event); } - } @Override @@ -208,16 +242,15 @@ public void onCommandAutoCompleteInteraction(CommandAutoCompleteInteractionEvent String commandName = e.getName(); String commandOption = e.getFocusedOption().getName(); String currentInput = e.getFocusedOption().getValue(); - switch (commandName) { case "megawalls" -> { if (commandOption.equals("skins")) { List options = new ArrayList<>(); List skins = Arrays.asList("Legendary", "Angel", "Arcanist", "Assassin", "Automaton", "Blaze", "Cow", "Creeper", "Dragon", - "Dreadlord", "Enderman", "Golem", "Herobrine", "Hunter", "Moleman", "Phoenix", "Pigman", "Pirate", "Renegade", - "Shaman", "Shark", "Sheep", "Skeleton", "Snowman", "Spider", "Squid", "Werewolf", "Zombie"); + "Dreadlord", "Enderman", "Golem", "Herobrine", "Hunter", "Moleman", "Phoenix", "Pigman", "Pirate", "Renegade", + "Shaman", "Shark", "Sheep", "Skeleton", "Snowman", "Spider", "Squid", "Werewolf", "Zombie"); for (String skin : skins) { - if (skin.toLowerCase().startsWith(currentInput.toLowerCase())) { + if (skin.toLowerCase().contains(currentInput.toLowerCase())) { options.add(skin); } } @@ -232,19 +265,114 @@ public void onCommandAutoCompleteInteraction(CommandAutoCompleteInteractionEvent .toList(); e.replyChoices(choices).queue(); - break; } } case "tournament" -> { if (commandOption.equals("tournament")) { List choices = new ArrayList<>(); tournamentMap.forEach((name, id) -> { - if (name.toLowerCase().startsWith(currentInput.toLowerCase()) && choices.size() <= 25){ + if (name.toLowerCase().contains(currentInput.toLowerCase()) && choices.size() <= 25) { choices.add(new Command.Choice(name, id)); } }); e.replyChoices(choices).queue(); - break; + } + } + case "playcommand" -> { + if (commandOption.equals("game")) { + JsonElement gameData = getPlayCommands().getAsJsonObject().get("gameData"); + if (gameData == null) { + e.replyChoices(Collections.emptyList()).queue(); + break; + } + + List choices = new ArrayList<>(); + for (JsonElement entry : gameData.getAsJsonArray()) { + String gameName = entry.getAsJsonObject().get("name").getAsString(); + JsonElement modes = entry.getAsJsonObject().get("modes"); + if (modes == null) { + continue; + } + + for (JsonElement modeEntry : modes.getAsJsonArray()) { + if (!modeEntry.getAsJsonObject().has("name") || !modeEntry.getAsJsonObject().has("identifier")) { + continue; + } + String modeName = modeEntry.getAsJsonObject().get("name").getAsString(); + String fullGameName; + if (gameName.equals(modeName)) { + fullGameName = gameName; + } else { + fullGameName = gameName + ": " + modeName; + } + String identifier = modeEntry.getAsJsonObject().get("identifier").getAsString(); + if (fullGameName.toLowerCase().contains(currentInput.toLowerCase())) { + choices.add(new Command.Choice(fullGameName, identifier)); + } + } + } + + if (choices.size() > 25) { + choices = choices.subList(0, 24); + } + + e.replyChoices(choices).queue(); + } + } + case "achievements" -> { + if (commandOption.equals("game")) { + Map gameData = autoCompleteAchGames(); + List choices = new ArrayList<>(); + for (Map.Entry entry : gameData.entrySet()) { + if (entry.getValue().toLowerCase().contains(currentInput.toLowerCase())) { + choices.add(new Command.Choice(entry.getValue(), entry.getValue())); + } + } + + if (choices.size() > 25) { + choices = choices.subList(0, 24); + } + + e.replyChoices(choices).queue(); + } + } + case "search" -> { + if (commandOption.equals("search")) { + int searchCount = 0; + JsonObject achievementsResources = getAchievementsResources().getAsJsonObject(); + List choices = new ArrayList<>(); + + for (String game : achievementsResources.keySet()) { + if (searchCount == 25) { break; } + JsonObject gameAchievements = achievementsResources.get(game).getAsJsonObject(); + JsonObject gameOneTime = gameAchievements.get("one_time").getAsJsonObject(); + JsonObject gameTiered = gameAchievements.get("tiered").getAsJsonObject(); + for(String oneTimeID : gameOneTime.keySet()){ + if (searchCount == 25) { break; } + JsonObject oneTimeAchievement = gameOneTime.get(oneTimeID).getAsJsonObject(); + String achievementName = oneTimeAchievement.get("name").getAsString(); + String achievementDescription = oneTimeAchievement.get("description").getAsString(); + + if(achievementName.toLowerCase().contains(currentInput.toLowerCase()) || achievementDescription.toLowerCase().contains(currentInput.toLowerCase())) { + choices.add(new Command.Choice(toReadableName(game) + ": " + achievementName, game.toUpperCase() + "_" + oneTimeID)); + searchCount++; + } + } + + for(String tieredID : gameTiered.keySet()){ + if (searchCount == 25) { break; } + JsonObject tieredAchievement = gameTiered.get(tieredID).getAsJsonObject(); + String achievementName = tieredAchievement.get("name").getAsString(); + String achievementDescription = tieredAchievement.get("description").getAsString(); + + if(achievementName.toLowerCase().contains(currentInput.toLowerCase()) || achievementDescription.toLowerCase().contains(currentInput.toLowerCase())) { + choices.add(new Command.Choice(toReadableName(game) + ": " + achievementName, game.toUpperCase() + "_" + tieredID)); + searchCount++; + } + } + } + + e.replyChoices(choices).queue(); } } default -> { @@ -275,12 +403,15 @@ public void onMessageReceived(@NotNull MessageReceivedEvent event) { if (event.getAuthor().isBot()) { return; } - Message.suppressContentIntentWarning(); +// String authorId = event.getAuthor().getId(); +// String authorName = event.getAuthor().getName(); +// Bot.getGlobalData().addUniqueUser(authorId, authorName); + String message = event.getMessage().getContentRaw(); if (message.toLowerCase().startsWith("ap!")) { Logger.logError(" @" + event.getAuthor().getName() + ": " + message); MessageCreateData data = new MessageCreateBuilder() - .addEmbeds(makeErrorEmbed("Outdated Command", "We no longer support chat based commands,\nInstead try using slash commands.\n-# Join our [Discord](https://discord.gg/zqVkUrUmzN) for more info.")) + .addEmbeds(makeErrorEmbed("Outdated Command", "We no longer support chat based commands,\nInstead try using slash commands.\n-# Join our [Discord](https://discord.gg/8jdmT5Db3Y) for more info.")) .build(); event.getMessage().reply( data diff --git a/src/main/java/me/stuffy/stuffybot/interactions/InteractionManager.java b/src/main/java/me/stuffy/stuffybot/interactions/InteractionManager.java index c9bc6ba..80f33bb 100644 --- a/src/main/java/me/stuffy/stuffybot/interactions/InteractionManager.java +++ b/src/main/java/me/stuffy/stuffybot/interactions/InteractionManager.java @@ -41,6 +41,12 @@ public static MessageCreateData getResponse(InteractionId interactionId) throws case "blitz" -> BlitzCommand.blitz(interactionId); case "megawalls" -> MegaWallsCommand.megawalls(interactionId); case "tournament" -> TournamentCommand.tournament(interactionId); + case "achievements" -> AchievementsCommand.achievements(interactionId); + case "link" -> LinkCommand.link(interactionId); + case "playcommand" -> PlayCommandCommand.playCommand(interactionId); + case "help" -> HelpCommand.help(); + case "search" -> SearchCommand.search(interactionId); + case "uuid" -> UuidCommand.uuid(interactionId); default -> throw new InteractionException("Invalid command"); }; } catch (APIException e) { diff --git a/src/main/java/me/stuffy/stuffybot/profiles/Achievement.java b/src/main/java/me/stuffy/stuffybot/profiles/Achievement.java new file mode 100644 index 0000000..b92bdb1 --- /dev/null +++ b/src/main/java/me/stuffy/stuffybot/profiles/Achievement.java @@ -0,0 +1,43 @@ +package me.stuffy.stuffybot.profiles; + +import com.google.gson.JsonElement; + +public class Achievement { + private final String name; + private final String description; + private final int points; + private final boolean legacy; + private final boolean secret; + private final Type type; + + private enum Type { + CHALLENGE, + TIERED + }; + + public Achievement(JsonElement achievementData) { + this.name = achievementData.getAsJsonObject().get("name").getAsString(); + this.description = achievementData.getAsJsonObject().get("description").getAsString(); + this.points = achievementData.getAsJsonObject().get("points").getAsInt(); + this.legacy = achievementData.getAsJsonObject().get("legacy").getAsBoolean(); + this.secret = achievementData.getAsJsonObject().get("secret").getAsBoolean(); + this.type = Type.valueOf(achievementData.getAsJsonObject().get("type").getAsString()); + + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public int getPoints() { + return points; + } + + public boolean isLegacy() { + return legacy; + } +} diff --git a/src/main/java/me/stuffy/stuffybot/profiles/GlobalData.java b/src/main/java/me/stuffy/stuffybot/profiles/GlobalData.java new file mode 100644 index 0000000..7bd42dd --- /dev/null +++ b/src/main/java/me/stuffy/stuffybot/profiles/GlobalData.java @@ -0,0 +1,105 @@ +package me.stuffy.stuffybot.profiles; + +import com.opencsv.CSVReader; + +import java.io.StringReader; +import java.lang.reflect.Array; +import java.util.*; + +import static me.stuffy.stuffybot.utils.APIUtils.*; + +public class GlobalData { + private final Map linkedAccounts; + private final Map sessionCommandsRun; + private final Map sessionUniqueUsers; + private final Map sessionUserCommandsRun; + private final ArrayList verifiedAccounts; + + public GlobalData() { + String linkedContent = readFile(Objects.requireNonNull(getGitHubFile(getPrivateApiRepo(), "apis/linkeddb.csv"))); + + List csvData = new ArrayList<>(); + try (CSVReader reader = new CSVReader(new StringReader(linkedContent))) { + csvData = reader.readAll(); + } catch (Exception e) { + e.printStackTrace(); + } + + boolean firstRow = true; + Map linkedAccounts = new HashMap<>(); + ArrayList verifiedAccounts = new ArrayList(); + for (String[] row : csvData) { + if(firstRow) { + firstRow = false; + continue; + } + try { + if(Objects.equals(row[2], "")){ + continue; + } + linkedAccounts.put(row[0], UUID.fromString(row[2])); + } catch (Exception e) { + e.printStackTrace(); + } + if(Objects.equals(row[4], "TRUE")){ + verifiedAccounts.add(row[0]); + } + } + + this.linkedAccounts = linkedAccounts; + this.verifiedAccounts = verifiedAccounts; + this.sessionCommandsRun = new HashMap<>(); + this.sessionUniqueUsers = new HashMap<>(); + this.sessionUserCommandsRun = new HashMap<>(); + } + + public Map getLinkedAccounts() { + return this.linkedAccounts; + } + + public void addLinkedAccount(String discordId, UUID uuid) { + this.linkedAccounts.put(discordId, uuid); + } + + public void incrementCommandsRun(String runnerId, String commandName) { + this.sessionCommandsRun.put(commandName, this.sessionCommandsRun.getOrDefault(commandName, 0) + 1); + this.sessionUserCommandsRun.put(runnerId, this.sessionUserCommandsRun.getOrDefault(runnerId, 0) + 1); + } + + public Map getSessionCommandsRun() { + return this.sessionCommandsRun; + } + + public void clearCommandsRun() { + this.sessionCommandsRun.clear(); + this.sessionUserCommandsRun.clear(); + } + + public void addUniqueUser(String discordId, String discordName) { + this.sessionUniqueUsers.put(discordId, discordName); + } + + public Map getSessionUniqueUsers() { + return this.sessionUniqueUsers; + } + + public void clearUniqueUsers() { + this.sessionUniqueUsers.clear(); + } + + public Map getSessionUserCommandsRun() { + return this.sessionUserCommandsRun; + } + + public ArrayList getVerifiedAccounts() { + return verifiedAccounts; + } + + public void setVerifiedAccount(String discordId, boolean verified) { + if(verified) { + this.verifiedAccounts.add(discordId); + } else { + this.verifiedAccounts.remove(discordId); + } + } +} diff --git a/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java b/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java index 1f6ca0d..0c360d9 100644 --- a/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java +++ b/src/main/java/me/stuffy/stuffybot/profiles/HypixelProfile.java @@ -12,16 +12,85 @@ import static me.stuffy.stuffybot.utils.MiscUtils.*; public class HypixelProfile { - private UUID uuid; + private final UUID uuid; + private final Rank rank; + private final JsonObject profile; private String displayName; - private Rank rank; - private JsonObject profile; + private final int achievementPoints; + private int achievementsUnlocked; + private int legacyAchievementPoints; + private int legacyAchievementsUnlocked; + private String easiestChallenge; + private double easiestChallengeGlobalPercent; public HypixelProfile(JsonObject profile) { this.profile = profile.deepCopy(); this.uuid = MiscUtils.formatUUID(profile.get("uuid").getAsString()); this.displayName = profile.get("displayname").getAsString(); this.rank = determineRank(profile); + this.achievementPoints = getNestedJson(0, profile, "achievementPoints").getAsInt(); + + instantiateAchievements(); + } + + private void instantiateAchievements() { + int unlockCount = 0; + int unlockCountLegacy = 0; + int pointCountLegacy = 0; + + String easiestChallenge = null; + double easiestChallengeGlobalPercent = 0; + + JsonObject achievements = getAchievements(); + List playerOneTime = achievements.get("achievementsOneTime").getAsJsonArray().asList(); + List playerOneTimeString = new ArrayList<>(); + for (JsonElement element : playerOneTime) { + try { + playerOneTimeString.add(element.getAsString()); + } catch (Exception ignored) { + } + } + JsonObject playerTiered = achievements.get("achievementsTiered").getAsJsonObject(); + JsonElement achievementsResources = getAchievementsResources(); + for (String game : achievementsResources.getAsJsonObject().keySet()) { + for (String oneTime : getNestedJson(achievementsResources.getAsJsonObject(), game, "one_time").getAsJsonObject().keySet()) { + boolean isLegacy = getNestedJson(false, achievementsResources.getAsJsonObject(), game, "one_time", oneTime, "legacy").getAsBoolean(); + if (playerOneTimeString.contains((game + "_" + oneTime.toLowerCase()))) { + if (!isLegacy) { + unlockCount++; + } else { + unlockCountLegacy++; + pointCountLegacy += getNestedJson(achievementsResources.getAsJsonObject(), game, "one_time", oneTime, "points").getAsInt(); + } + } else { + double globalPercentUnlocked = getNestedJson(0.0, achievementsResources.getAsJsonObject(), game, "one_time", oneTime, "globalPercentUnlocked").getAsDouble(); + if (globalPercentUnlocked > easiestChallengeGlobalPercent) { + easiestChallengeGlobalPercent = globalPercentUnlocked; + easiestChallenge = getNestedJson("Unknown", achievementsResources.getAsJsonObject(), game, "one_time", oneTime, "name").getAsString(); + } + } + } + + for (String tiered : getNestedJson(achievementsResources.getAsJsonObject(), game, "tiered").getAsJsonObject().keySet()) { + boolean isLegacy = getNestedJson(false, achievementsResources.getAsJsonObject(), game, "tiered", tiered, "legacy").getAsBoolean(); + for (JsonElement tier : getNestedJson(achievementsResources.getAsJsonObject(), game, "tiered", tiered, "tiers").getAsJsonArray()) { + int tierAmount = tier.getAsJsonObject().get("amount").getAsInt(); + if (getNestedJson(0, playerTiered, game + "_" + tiered.toLowerCase()).getAsInt() >= tierAmount) { + if (!isLegacy) { + unlockCount++; + } else { + unlockCountLegacy++; + pointCountLegacy += tier.getAsJsonObject().get("points").getAsInt(); + } + } + } + } + } + this.achievementsUnlocked = unlockCount; + this.legacyAchievementsUnlocked = unlockCountLegacy; + this.legacyAchievementPoints = pointCountLegacy; + this.easiestChallenge = easiestChallenge; + this.easiestChallengeGlobalPercent = easiestChallengeGlobalPercent; } private static Rank determineRank(JsonObject profile) { @@ -59,6 +128,11 @@ public UUID getUuid() { return uuid; } + public HypixelProfile setDisplayName(String displayName) { + this.displayName = displayName; + return this; + } + public String getDisplayName() { return displayName; } @@ -71,14 +145,6 @@ public String getDiscord() { return getNestedJson(profile, "socialMedia", "links", "DISCORD").getAsString(); } - public String[] getMaxedGames() { - // return an array of strings with the maxed games - JsonElement allAchievements = getAchievementsResources(); - JsonElement achievements = getNestedJson(profile, "achievements"); - - return new String[0]; - } - public JsonObject getProfile() { return profile; } @@ -103,12 +169,8 @@ public Integer getKarma() { return getNestedJson(profile, "karma").getAsInt(); } - public Integer getAchievementPoints() { - if (!profile.has("achievementPoints")) { - return 0; - } - - return getNestedJson(profile, "achievementPoints").getAsInt(); + public int getAchievementPoints() { + return this.achievementPoints; } public String getOnlineStatus() { @@ -215,7 +277,7 @@ public Integer getWins() { // Mega Walls "Walls3.wins", - // Turbo Kart Racers TODO: Reduce to golds? + // Turbo Kart Racers "GingerBread.gold_trophy", "GingerBread.tourney_gingerbread_solo_1_gold_trophy", // SkyWars @@ -362,48 +424,6 @@ public JsonObject getAchievements() { return combined; } - public Integer getLegacyAchievementPoints() { - int legacyPoints = 0; - JsonObject achievements = getAchievements(); - List playerOneTime = achievements.get("achievementsOneTime").getAsJsonArray().asList(); - List playerOneTimeString = new ArrayList<>(); - for (JsonElement element : playerOneTime) { - try { - playerOneTimeString.add(element.getAsString()); - } catch (Exception ignored) { - } - } - JsonObject playerTiered = achievements.get("achievementsTiered").getAsJsonObject(); - JsonElement achievementsResources = getAchievementsResources(); - for (String game : achievementsResources.getAsJsonObject().keySet()) { - for (String oneTime : getNestedJson(achievementsResources.getAsJsonObject(), game, "one_time").getAsJsonObject().keySet()) { - boolean isLegacy = getNestedJson(false, achievementsResources.getAsJsonObject(), game, "one_time", oneTime, "legacy").getAsBoolean(); - if (!isLegacy) { - continue; - } - if (playerOneTimeString.contains((game + "_" + oneTime.toLowerCase()))) { - legacyPoints += getNestedJson(achievementsResources.getAsJsonObject(), game, "one_time", oneTime, "points").getAsInt(); - } - } - - for (String tiered : getNestedJson(achievementsResources.getAsJsonObject(), game, "tiered").getAsJsonObject().keySet()) { - boolean isLegacy = getNestedJson(false, achievementsResources.getAsJsonObject(), game, "tiered", tiered, "legacy").getAsBoolean(); - if (!isLegacy) { - continue; - } - - - for (JsonElement tier : getNestedJson(0, achievementsResources.getAsJsonObject(), game, "tiered", tiered, "tiers").getAsJsonArray()) { - int tierAmount = tier.getAsJsonObject().get("amount").getAsInt(); - if (getNestedJson(0, playerTiered, game + "_" + tiered.toLowerCase()).getAsInt() >= tierAmount) { - legacyPoints += tier.getAsJsonObject().get("points").getAsInt(); - } - } - } - } - return legacyPoints; - } - public Integer getPit(String stat) { JsonObject pitStats = getNestedJson(profile, "stats", "Pit").getAsJsonObject(); try { @@ -626,7 +646,7 @@ public Map getBlitzStats() { public Integer getMegaWallsStat(String asString) { try { - return getNestedJson(0, profile, "stats", "Walls3", asString).getAsJsonObject().getAsInt(); + return getNestedJson(0, profile, "stats", "Walls3", asString).getAsInt(); } catch (IllegalArgumentException e) { return 0; } @@ -663,5 +683,25 @@ public Integer getStat(String field) { return 0; } } + + public int getAchievementsUnlocked() { + return this.achievementsUnlocked; + } + + public int getLegacyAchievementsUnlocked() { + return this.legacyAchievementsUnlocked; + } + + public int getLegacyAchievementPoints() { + return this.legacyAchievementPoints; + } + + public String getEasiestChallenge() { + return this.easiestChallenge + " (" + String.format("%.2f", this.easiestChallengeGlobalPercent) + "%)"; + } + + public String getEasiestTiered() { + return "`Game: Close Tiered III` (97.78%)"; + } } diff --git a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java index cf4c675..26b250e 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/APIUtils.java @@ -3,32 +3,45 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; +import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import com.opencsv.CSVReader; +import com.opencsv.CSVWriter; +import me.stuffy.stuffybot.Bot; import me.stuffy.stuffybot.commands.TournamentCommand; import me.stuffy.stuffybot.profiles.HypixelProfile; import me.stuffy.stuffybot.profiles.MojangProfile; import org.jetbrains.annotations.NotNull; +import org.kohsuke.github.GHContent; +import org.kohsuke.github.GitHub; +import org.kohsuke.github.GitHubBuilder; -import java.io.InputStream; -import java.io.InputStreamReader; +import java.io.*; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; -import java.util.UUID; +import java.util.*; import java.util.concurrent.TimeUnit; +import static me.stuffy.stuffybot.utils.Logger.log; import static me.stuffy.stuffybot.utils.Logger.logError; public class APIUtils { static String hypixelApiUrl = "https://api.hypixel.net/v2/"; static String mojangApiUrl = "https://api.mojang.com/"; + static String mojangSessionApiUrl = "https://sessionserver.mojang.com/"; + static String privateApiRepo = "stuffybot/PrivateAPI"; + static String publicApiRepo = "stuffybot/PublicAPI"; + static String stuffyApiUrl = "https://raw.githubusercontent.com/stuffybot/PublicAPI/main/"; + public static HypixelProfile getHypixelProfile(String username) throws APIException { MojangProfile profile = getMojangProfile(username); - return getHypixelProfile(profile.getUuid()); + String ign = profile.getUsername(); + return getHypixelProfile(profile.getUuid()).setDisplayName(ign); } private static final LoadingCache hypixelProfileCache = CacheBuilder.newBuilder() @@ -82,18 +95,22 @@ public static HypixelProfile fetchHypixelProfile(UUID uuid) throws APIException return new HypixelProfile(object.get("player").getAsJsonObject()); } case 400 -> { + logError(response.body()); logError("Hypixel API Error [Status Code: " + response.statusCode() + "] [UUID: " + uuid + "]"); throw new APIException("Hypixel", "A field is missing, this should never happen."); } case 403 -> { + logError(response.body()); logError("Hypixel API Error [Status Code: " + response.statusCode() + "] [UUID: " + uuid + "]"); throw new APIException("Hypixel", "Invalid API Key, contact the Stuffy immediately."); } case 429 -> { + logError(response.body()); logError("Hypixel API Error [Status Code: " + response.statusCode() + "] [UUID: " + uuid + "]"); throw new APIException("Hypixel", "Rate limited by Hypixel API, try again later."); } default -> { + logError(response.body()); logError("Unknown Hypixel API Error [Status Code: " + response.statusCode() + "] [UUID: " + uuid + "]"); throw new APIException("Hypixel", "I've never seen this error before."); } @@ -104,7 +121,7 @@ public static HypixelProfile fetchHypixelProfile(UUID uuid) throws APIException .expireAfterWrite(1, TimeUnit.HOURS) .build( new CacheLoader() { - public MojangProfile load(String username) { + public MojangProfile load(@NotNull String username) { try{ return fetchMojangProfile(username); } catch (APIException e){ @@ -164,6 +181,61 @@ public static MojangProfile fetchMojangProfile(String username) throws APIExcept return profile; } + private static final LoadingCache mojangProfileUUIDCache = CacheBuilder.newBuilder() + .expireAfterWrite(1, TimeUnit.HOURS) + .build( + new CacheLoader() { + public MojangProfile load(@NotNull UUID uuid) { + try{ + return fetchMojangProfile(uuid); + } catch (APIException e){ + throw new RuntimeException(e); + } + } + } + ); + + public static MojangProfile getMojangProfile(UUID uuid) throws APIException{ + try{ + return mojangProfileUUIDCache.getUnchecked(uuid); + } catch (RuntimeException e){ + if (e.getCause().getCause() instanceof APIException) { + throw (APIException) e.getCause().getCause(); + } else { + throw e; + } + } + } + + public static MojangProfile fetchMojangProfile(UUID uuid) throws APIException { + HttpRequest getRequest = HttpRequest.newBuilder() + .uri(URI.create(mojangSessionApiUrl + "session/minecraft/profile/" + uuid.toString())) + .build(); + HttpClient client = HttpClient.newHttpClient(); + HttpResponse response = client.sendAsync(getRequest, HttpResponse.BodyHandlers.ofString()).join(); + switch (response.statusCode()) { + case 200 -> { + JsonParser parser = new JsonParser(); + JsonElement element = parser.parse(response.body()); + JsonObject object = element.getAsJsonObject(); + String name = object.get("name").getAsString(); + return new MojangProfile(name, uuid); + } + case 204,404 -> { + logError("Mojang API Error [Status Code: " + response.statusCode() + "] [UUID: " + uuid + "]"); + throw new APIException("Mojang", "There is no Minecraft account with that UUID."); + } + case 429 -> { + logError("Mojang API Error [Status Code: " + response.statusCode() + "] [UUID: " + uuid + "]"); + throw new APIException("Mojang", "Rate limited by Mojang API, try again later."); + } + default -> { + logError("Unknown Mojang API Error [Status Code: " + response.statusCode() + "] [UUID: " + uuid + "]"); + throw new APIException("Mojang", "I've never seen this error before."); + } + } + } + private static final LoadingCache achievementsCache = CacheBuilder.newBuilder() .expireAfterWrite(1, TimeUnit.HOURS) .build( @@ -202,8 +274,45 @@ private static JsonElement fetchAchievementsResources() { } } + private static final LoadingCache playCommandCache = CacheBuilder.newBuilder() + .expireAfterWrite(1, TimeUnit.HOURS) + .build( + new CacheLoader<>() { + public JsonElement load(@NotNull String key) { + return fetchPlayCommands(); + } + } + ); + + public static JsonElement getPlayCommands() { + return playCommandCache.getUnchecked("achievements"); + } + + private static JsonElement fetchPlayCommands() { + try { + HttpRequest getRequest = HttpRequest.newBuilder() + .uri(URI.create(stuffyApiUrl + "apis/playcommands.json")) + .build(); + HttpClient client = HttpClient.newHttpClient(); + HttpResponse response = client.send(getRequest, HttpResponse.BodyHandlers.ofString()); + + if (response.statusCode() == 200) { + + JsonParser parser = new JsonParser(); + + return parser.parse(response.body()); + + } else { + throw new IllegalStateException("Unexpected response from Stuffy API: " + response.statusCode()); + } + } catch (Exception e) { + throw new RuntimeException("Failed to fetch play commands from Stuffy API", e); + } + } + public static JsonObject getTournamentData() { + // # TODO: Switch to https://raw.githubusercontent.com/stuffybot/PublicAPI/main/apis/tournaments.json try (InputStream inputStream = TournamentCommand.class.getResourceAsStream("/data/tournaments.json")) { assert inputStream != null; try (InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) { @@ -214,4 +323,265 @@ public static JsonObject getTournamentData() { return null; } } + + public static GitHub connectToGitHub() { + try { + GitHub github = new GitHubBuilder().withOAuthToken(System.getenv("GITHUB_OAUTH")).build(); + log(" Connected to GitHub as " + github.getMyself().getLogin() + " successfully."); + return github; + } catch (IOException e) { + logError(" Failed to connect to GitHub: " + e.getMessage()); + return null; + } + } + + public static void updateGitHubFile(String repo, String path, String content, String message) { + try { + Bot.getGitHub().getRepository(repo).createContent() + .path(path) + .content(content) + .message(message) + .sha(Bot.getGitHub().getRepository(repo).getFileContent(path).getSha()) + .commit(); + log(" Updated file " + path + " in " + repo + " successfully."); + } catch (IOException e) { + logError(" Failed to update file " + path + " in " + repo + ": " + e.getMessage()); + } + } + + public static void uploadGitHubFile(String repo, String path, String content, String message) { + try { + Bot.getGitHub().getRepository(repo).createContent() + .path(path) + .content(content) + .message(message) + .commit(); + log(" Uploaded file " + path + " in " + repo + " successfully."); + } catch (IOException e) { + logError(" Failed to upload file " + path + " in " + repo + ": " + e.getMessage()); + } + } + + public static GHContent getGitHubFile(String repo, String path) { + try { + return Bot.getGitHub().getRepository(repo).getFileContent(path); + } catch (IOException e) { + logError(" Failed to get file " + path + " in " + repo + ": " + e.getMessage()); + return null; + } + } + + public static String getPrivateApiRepo() { + return privateApiRepo; + } + + + public static void updateBotStats(int totalServers, int totalUsers, Map commandsRun) { + GHContent botStats = getGitHubFile(privateApiRepo, "apis/bot.json"); + if (botStats == null) throw new IllegalStateException("Failed to get bot.json from GitHub"); + + try { + // Read the JSON content + String botStatsContent = readFile(botStats); + JsonObject fullJson = JsonParser.parseString(botStatsContent).getAsJsonObject(); + + // Update the JSON content + fullJson.addProperty("lastUpdated", System.currentTimeMillis()); + JsonArray allBots = fullJson.get("bots").getAsJsonArray(); + String botId = Bot.getInstance().getJDA().getSelfUser().getId(); + + JsonObject bot = null; + for (JsonElement element : allBots) { + JsonObject currentBot = element.getAsJsonObject(); + if (currentBot.get("id").getAsString().equals(botId)) { + bot = currentBot; + break; + } + } + + if (bot == null) { + throw new IllegalStateException("Failed to find bot in bot.json"); + } + + bot.addProperty("servers", totalServers); + bot.addProperty("totalusers", totalUsers); + + JsonObject commands = bot.get("commandsRun").getAsJsonObject(); + + for (Map.Entry entry : commandsRun.entrySet()) { + String command = entry.getKey(); + int count = entry.getValue(); + int currentCount = 0; + if(commands.has(command)) currentCount = commands.get(command).getAsInt(); + commands.addProperty(command, currentCount + count); + } + + fullJson.add("bots", allBots); + + // Write the updated content back to the JSON file + updateGitHubFile(privateApiRepo, "apis/bot.json", fullJson.toString(), "Updated bot stats."); + log(" Updated bot stats for " + totalServers + " servers."); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static void updateUsersStats(Map uniqueUsers, Map userCommandsRun) { + GHContent linkedDB = getGitHubFile(privateApiRepo, "apis/linkeddb.csv"); + if (linkedDB == null) throw new IllegalStateException("Failed to get linkeddb.csv from GitHub"); + + try { + // Read the CSV content + String linkedDBContent = readFile(linkedDB); + List csvData; + try (CSVReader reader = new CSVReader(new StringReader(linkedDBContent))) { + csvData = reader.readAll(); + } + + int newUsers = 0; + int updatedUsers = 0; + int newCommandsRun = 0; + // Update the CSV content + for (String discordId : uniqueUsers.keySet()) { + String discordName = uniqueUsers.get(discordId); + int commandsRun = userCommandsRun.getOrDefault(discordId, 0); + newCommandsRun += commandsRun; + boolean updated = false; + for (String[] row : csvData) { + if (row[0].equals(discordId)) { + row[1] = discordName; + if (row[5].isEmpty()) row[5] = "0"; + row[5] = String.valueOf(commandsRun + Integer.parseInt(row[5])); + updated = true; + updatedUsers++; + break; + } + } + if (!updated) { + newUsers++; + csvData.add(new String[]{discordId, discordName, "", "", "", String.valueOf(commandsRun)}); + } + } + + // Write the updated content back to the CSV file + StringWriter stringWriter = new StringWriter(); + try (CSVWriter writer = new CSVWriter(stringWriter)) { + writer.writeAll(csvData); + } + String updatedContent = stringWriter.toString(); + updateGitHubFile(privateApiRepo, "apis/linkeddb.csv", updatedContent, + "Added `" + newCommandsRun + "` new commands run by `" + newUsers + "` new users and `" + + updatedUsers + "` existing users."); + Logger.log(" Updated stats for " + newUsers + " new users and " + updatedUsers + " existing users."); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static String readFile(GHContent content) { + try { + InputStream inputStream = content.read(); + InputStreamReader reader = new InputStreamReader(inputStream); + StringBuilder file = new StringBuilder(); + int character; + while ((character = reader.read()) != -1) { + file.append((char) character); + } + return file.toString(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static void setVerifiedStatus(String discordId, Boolean verified) { + GHContent linkedDB = getGitHubFile(privateApiRepo, "apis/linkeddb.csv"); + if (linkedDB == null) throw new IllegalStateException("Failed to get linkeddb.csv from GitHub"); + + try { + // Read the CSV content + String linkedDBContent = readFile(linkedDB); + List csvData; + try (CSVReader reader = new CSVReader(new StringReader(linkedDBContent))) { + csvData = reader.readAll(); + } + + // Update the CSV content + for (String[] row : csvData) { + if (row[0].equals(discordId)) { + row[4] = verified ? "TRUE" : "FALSE"; + break; + } + } + + // Write the updated content back to the CSV file + StringWriter stringWriter = new StringWriter(); + try (CSVWriter writer = new CSVWriter(stringWriter)) { + writer.writeAll(csvData); + } + String updatedContent = stringWriter.toString(); + updateGitHubFile(privateApiRepo, "apis/linkeddb.csv", updatedContent, "`@" + Bot.getGlobalData().getSessionUniqueUsers().getOrDefault(discordId, "NULL") + "` verified status set to `" + verified + "`"); + Bot.getGlobalData().setVerifiedAccount(discordId, verified); + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static void updateLinkedDB(String discordId, UUID uuid, String ign) { + String discordName = Bot.getGlobalData().getSessionUniqueUsers().getOrDefault(discordId, "NULL"); + + GHContent linkedDB = getGitHubFile(privateApiRepo, "apis/linkeddb.csv"); + if (linkedDB == null) throw new IllegalStateException("Failed to get linkeddb.csv from GitHub"); + + try { + // Read the CSV content + String linkedDBContent = readFile(linkedDB); + List csvData; + try (CSVReader reader = new CSVReader(new StringReader(linkedDBContent))) { + csvData = reader.readAll(); + } + + // Update the CSV content + boolean updated = false; + for (String[] row : csvData) { + if (row[0].equals(discordId)) { + row[1] = discordName; + row[2] = uuid.toString(); + row[3] = ign; + updated = true; + break; + } + } + if (!updated) { + csvData.add(new String[]{discordId, discordName, uuid.toString(), ign, "", ""}); + } + + // Write the updated content back to the CSV file + StringWriter stringWriter = new StringWriter(); + try (CSVWriter writer = new CSVWriter(stringWriter)) { + writer.writeAll(csvData); + } + String updatedContent = stringWriter.toString(); + updateGitHubFile(privateApiRepo, "apis/linkeddb.csv", updatedContent, "`@" + discordName + "` linked as `" + ign + "`"); + Bot.getGlobalData().addLinkedAccount(discordId, uuid); + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static void uploadLogs() { + try { + // Read the logs content + String logsContent = String.join("\n", Logger.getLogs()); + + String logName = Logger.getLogName(); + + // Write the updated content back to the logs file + uploadGitHubFile(privateApiRepo, "apis/logs/" + Bot.getInstance().getJDA().getSelfUser().getAsTag() + "/" + logName + ".txt", logsContent, "Uploaded logs for " + logName + "."); + Logger.log(" Uploaded logs to GitHub."); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } diff --git a/src/main/java/me/stuffy/stuffybot/utils/Config.java b/src/main/java/me/stuffy/stuffybot/utils/Config.java new file mode 100644 index 0000000..b68132e --- /dev/null +++ b/src/main/java/me/stuffy/stuffybot/utils/Config.java @@ -0,0 +1,45 @@ +package me.stuffy.stuffybot.utils; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +public class Config { + private static final Properties properties = new Properties(); + + static { + try (InputStream input = Config.class.getClassLoader().getResourceAsStream("config.properties")) { + if (input == null) { + Logger.logError("Unable to find config.properties"); + throw new RuntimeException("Unable to find config.properties"); + } + properties.load(input); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static String getEnvironment() { + return properties.getProperty("environment", "development"); + } + + public static String getHomeGuildId() { + return properties.getProperty("homeGuildId"); + } + + public static String getVerifiedRoleId() { + return properties.getProperty("verifiedRoleId"); + } + + public static String getNotVerifiedRoleId() { + return properties.getProperty("notVerifiedRoleId"); + } + + public static String getLinkCommandId() { + return properties.getProperty("linkCommandId"); + } + + public static String getCustomStatus() { + return properties.getProperty("customStatus"); + } +} diff --git a/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java b/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java index 7eda52a..916804b 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/DiscordUtils.java @@ -3,30 +3,20 @@ import me.stuffy.stuffybot.Bot; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.MessageEmbed; -import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; -import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; -import net.dv8tion.jda.api.interactions.components.ActionRow; -import net.dv8tion.jda.api.interactions.components.text.TextInput; -import net.dv8tion.jda.api.interactions.components.text.TextInputStyle; -import net.dv8tion.jda.api.interactions.modals.Modal; -import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; -import net.dv8tion.jda.api.utils.messages.MessageCreateData; - -import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; -import java.util.List; +import java.util.*; + +import static me.stuffy.stuffybot.utils.APIUtils.getMojangProfile; import static me.stuffy.stuffybot.utils.MiscUtils.toSkillIssue; public class DiscordUtils { - public static MessageEmbed makeEmbed(String embedTitle, String embedSubtitle, String embedContent, int embedColor) { + public static MessageEmbed makeEmbed(String embedTitle, String embedSubtitle, String embedContent, int embedColor, Integer maxLines) { EmbedBuilder embedBuilder = new EmbedBuilder(); embedBuilder.setTitle(embedTitle); String[] lines = embedContent.split("\n"); int lineCount = lines.length; - if (lineCount <= 15) { + if (lineCount <= maxLines) { if(embedSubtitle == null) embedBuilder.setDescription(embedContent); else @@ -53,16 +43,30 @@ public static MessageEmbed makeEmbed(String embedTitle, String embedSubtitle, St return embedBuilder.build(); } + public static MessageEmbed makeEmbed(String embedTitle, String embedSubtitle, String embedContent, int embedColor) { + return makeEmbed(embedTitle, embedSubtitle, embedContent, embedColor, 15); + } public static MessageEmbed makeErrorEmbed(String embedTitle, String embedContent) { if (Calendar.getInstance().get(Calendar.MONTH) == Calendar.APRIL && Calendar.getInstance().get(Calendar.DAY_OF_MONTH) == 1){ embedTitle = toSkillIssue(embedTitle); embedContent = toSkillIssue(embedContent); } - return makeEmbed(":no_entry: " + embedTitle, null, embedContent, 0xff0000); + return makeEmbed(":no_entry: " + embedTitle, null, embedContent, 0xC95353); + } + + public static MessageEmbed makeEmbedWithImage(String embedTitle, String embedSubtitle, String embedContent, String imageUrl, int embedColor) { + EmbedBuilder embedBuilder = new EmbedBuilder(); + embedBuilder.setTitle(embedTitle); + embedBuilder.setDescription("-# " + embedSubtitle + "\n" + embedContent); + embedBuilder.setColor(embedColor); + embedBuilder.setFooter("Stuffy Bot by @stuffy"); + embedBuilder.setTimestamp(new Date().toInstant()); + embedBuilder.setImage(imageUrl); + return embedBuilder.build(); } public static MessageEmbed makeUpdateEmbed(String embedTitle, String embedContent) { - return makeEmbed(":mega: " + embedTitle, null, embedContent, 0xffef14); + return makeEmbed(":mega: " + embedTitle, null, embedContent, 0xEBD773); } public static MessageEmbed makeStaffRankChangeEmbed(String ign, String oldRank, String newRank, String position) { @@ -82,11 +86,11 @@ public static MessageEmbed makeStaffRankChangeEmbed(String ign, String oldRank, } public static MessageEmbed makeStatsEmbed(String embedTitle, String embedContent) { - return makeEmbed(embedTitle, null, embedContent, 0xf7cb72); + return makeEmbed(embedTitle, null, embedContent, 0x6D8FCE); } public static MessageEmbed makeStatsEmbed(String embedTitle, String embedSubtitle, String embedContent) { - return makeEmbed(embedTitle, embedSubtitle , embedContent, 0xf7cb72); + return makeEmbed(embedTitle, embedSubtitle , embedContent, 0x6D8FCE); } public static String getDiscordUsername(String id){ @@ -129,53 +133,15 @@ public static String discordTimeUnix(long timestamp) { return discordTimeUnix(timestamp, "R"); } - public static void verifyButton(ButtonInteractionEvent event) { - // Look up the user in the database, and check if they have already verified/linked - // If they are verified, verify them - // If they are linked, attempt to verify them - // If they are not linked, or the verification fails, prompt them to verify - - String userId = event.getUser().getId(); - if(isVerified(userId)){ - MessageCreateData data = new MessageCreateBuilder() - .setEmbeds(makeErrorEmbed("Verification Error", "You have already verified your identity, silly goose.")).build(); - event.reply(data).setEphemeral(true).queue(); - return; - } - - - Modal modal = Modal.create("verify", "Verify your identity in Stuffy Discord") - .addComponents(ActionRow.of(TextInput.create("ign", "Minecraft Username", TextInputStyle.SHORT) - .setPlaceholder("Your Minecraft Username") - .setMaxLength(16) - .setMinLength(1) - .setRequired(true) - .build()), - ActionRow.of( - TextInput.create("captcha", "CAPTCHA", TextInputStyle.PARAGRAPH) - .setPlaceholder("Enter the word 'stuffy'.\n" + - "To prevent abuse, failing the CAPTCHA " + - "will result in a short timeout.") - .setRequired(false) - .build())) - .build(); - event.replyModal(modal).queue(); - } - - public static void updateRoles(User user, String ign, boolean announce) { - Bot bot = Bot.getInstance(); - // bot.getHomeGuild().getMember(user).modifyNickname(ign).queue(); - } - - public static boolean isVerified(String userId) { - return true; - } - - public static String getUsername(SlashCommandInteractionEvent event) { + public static String getUsername(SlashCommandInteractionEvent event) throws APIException { String username = event.getOption("ign") == null ? null : event.getOption("ign").getAsString(); if (username == null) { - // TODO: First, check the database - username = getDiscordUsername(event.getUser().getName()); + if (Bot.getGlobalData().getLinkedAccounts().containsKey(event.getUser().getId())) { + UUID uuid = Bot.getGlobalData().getLinkedAccounts().get(event.getUser().getId()); + username = getMojangProfile(uuid).getUsername(); + } else { + username = getDiscordUsername(event.getUser().getName()); + } } return username; } diff --git a/src/main/java/me/stuffy/stuffybot/utils/Logger.java b/src/main/java/me/stuffy/stuffybot/utils/Logger.java index 40274b5..0cd9d67 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/Logger.java +++ b/src/main/java/me/stuffy/stuffybot/utils/Logger.java @@ -1,13 +1,17 @@ package me.stuffy.stuffybot.utils; +import java.util.ArrayList; +import java.util.List; + public class Logger { -// public Guild logGuild; -// public Channel logChannel; + private static final List logs = new ArrayList<>(); + private static String logName; public static void log(String message) { String date = java.time.LocalDate.now().toString(); String time = java.time.LocalTime.now().truncatedTo(java.time.temporal.ChronoUnit.SECONDS).toString(); message = "[" + date + " " + time + "] " + message; + logs.add(message); System.out.println(message); } @@ -15,6 +19,19 @@ public static void logError(String message) { String date = java.time.LocalDate.now().toString(); String time = java.time.LocalTime.now().truncatedTo(java.time.temporal.ChronoUnit.SECONDS).toString(); message = "[ERROR] [" + date + " " + time + "] " + message; + logs.add(message); System.out.println(message); } + + public static List getLogs() { + return logs; + } + + public static String getLogName() { + return logName; + } + + public static void setLogName(String name) { + logName = name; + } } diff --git a/src/main/java/me/stuffy/stuffybot/utils/MiscUtils.java b/src/main/java/me/stuffy/stuffybot/utils/MiscUtils.java index d6ff5d5..4c4c661 100644 --- a/src/main/java/me/stuffy/stuffybot/utils/MiscUtils.java +++ b/src/main/java/me/stuffy/stuffybot/utils/MiscUtils.java @@ -10,6 +10,8 @@ import java.util.Map; import java.util.UUID; +import static me.stuffy.stuffybot.utils.APIUtils.getAchievementsResources; + public class MiscUtils { private static final Gson gson = new Gson(); public static UUID formatUUID(String uuid) { @@ -43,6 +45,14 @@ public static JsonElement getNestedJson(Integer defaultValue, Object object, Str } } + public static JsonElement getNestedJson(Double defaultValue, Object object, String... keys) { + try { + return getNestedJson((JsonObject) object, keys); + } catch (IllegalArgumentException e) { + return stringToJson(defaultValue.toString()); + } + } + public static JsonElement getNestedJson(Boolean defaultValue, Object object, String... keys) { try { return getNestedJson((JsonObject) object, keys); @@ -157,8 +167,8 @@ public static String genBase64(Integer length){ return sb.toString(); } - public static String toReadableName(String resourcesName) { - Map resourceNames = new HashMap<>(); + private static Map getResourceNames() { + Map resourceNames = new HashMap<>(); resourceNames.put("arcade", "Arcade"); resourceNames.put("arena", "Arena Brawl"); resourceNames.put("bedwars", "Bed Wars"); @@ -191,9 +201,23 @@ public static String toReadableName(String resourcesName) { resourceNames.put("warlords", "Warlords"); resourceNames.put("woolgames", "Wool Games"); + return resourceNames; + } + + public static String toReadableName(String resourcesName) { + Map resourceNames = getResourceNames(); return resourceNames.getOrDefault(resourcesName, resourcesName); } + public static String fromReadableName(String readableName) { + Map resourceNames = getResourceNames(); + Map reverseMap = new HashMap<>(); + for (Map.Entry entry : resourceNames.entrySet()) { + reverseMap.put(entry.getValue(), entry.getKey()); + } + return reverseMap.getOrDefault(readableName, readableName); + } + public static String minutesFormatted(int minutes) { int hours = minutes / 60; int remainingMinutes = minutes % 60; @@ -201,5 +225,50 @@ public static String minutesFormatted(int minutes) { return remainingMinutes + "m"; return hours + "h " + remainingMinutes + "m"; } + public static int getMaxAchievements() { + JsonObject achievementData = getAchievementsResources().getAsJsonObject(); + int total = 0; + for (String game : achievementData.keySet()) { + JsonObject gameData = achievementData.getAsJsonObject(game); + JsonObject oneTime = gameData.getAsJsonObject("one_time"); + JsonObject tiered = gameData.getAsJsonObject("tiered"); + + for (String key : oneTime.keySet()) { + boolean isLegacy = getNestedJson(false, oneTime, key, "legacy").getAsBoolean(); + if (!isLegacy) { + total++; + } + } + + for (String key : tiered.keySet()) { + boolean isLegacy = getNestedJson(false, tiered, key, "legacy").getAsBoolean(); + if (!isLegacy) { + total+= getNestedJson(tiered, key, "tiers").getAsJsonArray().size(); + } + } + } + return total; + } + + public static int getMaxAchievementPoints() { + JsonObject achievementData = getAchievementsResources().getAsJsonObject(); + int total = 0; + for (String game : achievementData.keySet()) { + JsonObject gameData = achievementData.getAsJsonObject(game); + + int totalPoints = getNestedJson(0, gameData, "total_points").getAsInt(); + total += totalPoints; + } + return total; + } + + public static Map autoCompleteAchGames() { + Map games = new HashMap<>(); + JsonObject achievementData = getAchievementsResources().getAsJsonObject(); + for (String game : achievementData.keySet()) { + games.put(game, toReadableName(game)); + } + return games; + } } diff --git a/src/main/java/me/stuffy/stuffybot/utils/Verification.java b/src/main/java/me/stuffy/stuffybot/utils/Verification.java new file mode 100644 index 0000000..fd110b4 --- /dev/null +++ b/src/main/java/me/stuffy/stuffybot/utils/Verification.java @@ -0,0 +1,174 @@ +package me.stuffy.stuffybot.utils; + +import me.stuffy.stuffybot.Bot; +import me.stuffy.stuffybot.profiles.HypixelProfile; +import net.dv8tion.jda.api.components.label.Label; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; +import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; +import net.dv8tion.jda.api.components.textinput.TextInput; +import net.dv8tion.jda.api.components.textinput.TextInputStyle; +import net.dv8tion.jda.api.modals.Modal; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import static me.stuffy.stuffybot.utils.APIUtils.*; +import static me.stuffy.stuffybot.utils.DiscordUtils.*; + +public class Verification { + private static final Map captchaTimeouts = new HashMap<>(); + + public static void verifyButton(ButtonInteractionEvent event) { + User user = event.getUser(); + String userId = event.getUser().getId(); + + if(captchaTimeouts.containsKey(userId)){ + if(captchaTimeouts.get(userId) > Instant.now().toEpochMilli()) { + MessageCreateData data = new MessageCreateBuilder() + .setEmbeds(makeErrorEmbed("Verification Error", "You have failed a CAPTCHA too recently. Please try again " + discordTimeUnix(captchaTimeouts.get(userId)) + ".")).build(); + event.reply(data).setEphemeral(true).queue(); + return; + } + else { + captchaTimeouts.remove(userId); + } + } + + // #TODO if I am verified in linked db, update me according to info there + if(isVerified(userId)){ + MessageCreateData data = new MessageCreateBuilder() + .setEmbeds(makeErrorEmbed("Verification Error", "You have already verified your identity, silly goose.")).build(); + event.reply(data).setEphemeral(true).queue(); + return; + } + + TextInput ign = TextInput.create("ign", TextInputStyle.SHORT) + .setPlaceholder("Your Minecraft Username") + .setMaxLength(16) + .setMinLength(1) + .setRequired(true) + .build(); + + TextInput captcha = TextInput.create("captcha", TextInputStyle.PARAGRAPH) + .setPlaceholder("Enter the word 'stuffy'.\n" + + "To prevent abuse, failing the CAPTCHA " + + "will result in a short timeout.") + .setRequired(false) + .build(); + + Modal modal = Modal.create("verify", "Verify your identity in Stuffy Discord") + .addComponents(Label.of("Minecraft Username", ign), Label.of("CAPTCHA", captcha)).build(); + event.replyModal(modal).queue(); + } + + public static boolean isVerified(String userID) { + ArrayList verifiedAccounts = Bot.getGlobalData().getVerifiedAccounts(); + if (verifiedAccounts.contains(userID)) { + Logger.log("user " + userID + " is already verified"); + return true; + } + Logger.log("user " + userID + " is not verified"); + return false; + } + + public static void verifyModal(ModalInteractionEvent event) { + String ign = Objects.requireNonNull(event.getValue("ign")).getAsString(); + String captcha = Objects.requireNonNull(event.getValue("captcha")).getAsString(); + + if (!ign.matches("[a-zA-Z0-9_]+")) { + MessageEmbed errorEmbed = makeErrorEmbed("Verification Error", "Your Minecraft username may only contain letters, numbers, and underscores."); + MessageCreateData data = new MessageCreateBuilder() + .addEmbeds(errorEmbed) + .build(); + event.reply(data).setEphemeral(true).queue(); + return; + } + + if (!captcha.equals("stuffy")) { + long timeout = Instant.now().plusSeconds(120).toEpochMilli(); + captchaTimeouts.put(event.getUser().getId(), timeout); + MessageEmbed errorEmbed = makeErrorEmbed("Verification Error", "You entered the CAPTCHA incorrectly.\n-# Try again " + discordTimeUnix(timeout) + "."); + MessageCreateData data = new MessageCreateBuilder() + .addEmbeds(errorEmbed) + .build(); + event.reply(data).setEphemeral(true).queue(); + return; + } + + HypixelProfile profile = null; + try { + profile = getHypixelProfile(ign); + } catch (APIException e) { + event.replyEmbeds(makeErrorEmbed("Hypixel API Error", e.errorMessage)).setEphemeral(true).queue(); + return; + } + if ( profile == null) { + event.replyEmbeds(makeErrorEmbed("Hypixel API Error", "The user " + ign + " does not seem to exist. SpoOo00oky")).setEphemeral(true).queue(); + return; + } + + ign = profile.getDisplayName(); + + String linkedDiscord = null; + try { + linkedDiscord = profile.getDiscord(); + } catch (Exception e) { + event.replyEmbeds(makeEmbedWithImage("Verification Error", "Check your linked discord in game","The user `" + ign + "` does not have a discord linked.\n\nFollow the steps below to correct this, and try again.", "https://i.imgur.com/HHs9nbZ.gif", 0xC95353)).setEphemeral(true).queue(); + return; + } + + String discordUsername = event.getUser().getName(); + if(!linkedDiscord.equals(discordUsername)){ + event.replyEmbeds(makeEmbedWithImage("Verification Error", "Check your linked discord in game", "The user `" + ign + "` has a different discord linked.\n In Game Linked Discord: `" + linkedDiscord + "`\n Your Discord Username: `" + discordUsername + "`\n\nFollow the steps below to correct this, and try again.", "https://i.imgur.com/HHs9nbZ.gif", 0xC95353)).setEphemeral(true).queue(); + return; + } + + + // User is verified, whoop-whoop + updateLinkedDB(event.getUser().getId(), profile.getUuid(), ign); + setVerifiedStatus(event.getUser().getId(), true); + + Bot bot = Bot.getInstance(); + bot.getJDA().getGuildById(Config.getHomeGuildId()).addRoleToMember(event.getUser(), bot.getVerifiedRole()).queue(); + bot.getJDA().getGuildById(Config.getHomeGuildId()).removeRoleFromMember(event.getUser(), bot.getNotVerifiedRole()).queue(); + + event.replyEmbeds(DiscordUtils.makeEmbed("Verification Successful", "You have been verified.", "You may now enjoy all of the perks that come with that. You may unverify at any time.", 0x3d84a2)).setEphemeral(true).queue(); + } + + public static void unverifyButton(ButtonInteractionEvent event) { + User user = event.getUser(); + String userId = event.getUser().getId(); + if(!isVerified(userId)){ + MessageCreateData data = new MessageCreateBuilder() + .setEmbeds(makeErrorEmbed("Unverification Error", "You have not yet verified, there is nothing to undo!")).build(); + event.reply(data).setEphemeral(true).queue(); + return; + } + + setVerifiedStatus(userId, false); + + Bot bot = Bot.getInstance(); + bot.getJDA().getGuildById(Config.getHomeGuildId()).removeRoleFromMember(event.getUser(), bot.getVerifiedRole()).queue(); + bot.getJDA().getGuildById(Config.getHomeGuildId()).addRoleToMember(event.getUser(), bot.getNotVerifiedRole()).queue(); + + // TODO: Remove all roles that the player has already earned + + MessageEmbed embed = DiscordUtils.makeEmbed("Unverify", "You have been unverified.", "You may verify again at any time.", 0x3d84a2); + event.replyEmbeds(embed).setEphemeral(true).queue(); + } + + public static void verifyUser(User user) { + // #TODO check if anyone already has the account verified, unverify them first + // #TODO add this username,uuid to verified in linked db + // #TODO remove unverified role, add verified role + // #TODO add all roles that the player has already earned + } + +} diff --git a/src/main/resources/config.properties b/src/main/resources/config.properties new file mode 100644 index 0000000..cc069b6 --- /dev/null +++ b/src/main/resources/config.properties @@ -0,0 +1,6 @@ +environment=production +homeGuildId=818238263110008863 +verifiedRoleId=818283784763473952 +notVerifiedRoleId=1269849137982083215 +linkCommandId=1281037425032040491 +customStatus=Use anywhere! diff --git a/src/main/resources/data/mwclasses.json b/src/main/resources/data/mwclasses.json index fe3e09e..f64e364 100644 --- a/src/main/resources/data/mwclasses.json +++ b/src/main/resources/data/mwclasses.json @@ -1,9 +1,9 @@ { "lastUpdated": 0, - "classes" : [ + "classes": [ { - "id" : "cow", - "name" : "Cow", + "id": "cow", + "name": "Cow", "skins": [ { "name": "Moo Brawler", @@ -625,8 +625,8 @@ ] }, { - "id" : "skeleton", - "name" : "Skeleton", + "id": "skeleton", + "name": "Skeleton", "skins": [ { "name": "Marksman", diff --git a/src/main/resources/data/schemas/tournaments-schema.json b/src/main/resources/data/schemas/tournaments-schema.json new file mode 100644 index 0000000..c35dabc --- /dev/null +++ b/src/main/resources/data/schemas/tournaments-schema.json @@ -0,0 +1,133 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Tournaments Schema", + "description": "Schema for tournament data, including the name, iteration, duration, and data for navigating the api to find specific stats", + "type": "object", + "properties": { + "lastUpdated": { + "type": "integer", + "description": "Unix timestamp of the last time the data was updated" + }, + "tournaments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "The id of the tournament, denoted by the index in which it happened" + }, + "name": { + "type": "string", + "description": "The name of the tournament" + }, + "icon": { + "type": "string", + "description": "The path to the icon of the tournament, located at https://hypixel.net/styles/hypixel-v2/images/game-icons/{icon}" + }, + "iteration": { + "type": "integer", + "description": "The iteration of the tournament" + }, + "duration": { + "type": "object", + "properties": { + "start": { + "type": "integer", + "description": "Unix timestamp of the start of the tournament" + }, + "end": { + "type": "integer", + "description": "Unix timestamp of the end of the tournament" + } + }, + "required": [ + "start", + "end" + ] + }, + "data": { + "type": "object", + "properties": { + "timeLimit": { + "type": "integer", + "description": "The maximum playtime for participants in minutes" + }, + "gameLimit": { + "type": "integer", + "description": "The maximum amount of games a participant can play" + }, + "tourneyField": { + "type": "string", + "description": "The field in the player's data that contains the tournament data" + }, + "wins": { + "type": "string", + "description": "The field in the player's stats that contains the amount of wins" + }, + "losses": { + "type": "string", + "description": "The field in the player's stats that contains the amount of losses" + }, + "winstreak": { + "type": "string", + "description": "The field in the player's stats that contains the amount of wins in a row" + }, + "kills": { + "type": "string", + "description": "The field in the player's stats that contains the amount of kills" + }, + "deaths": { + "type": "string", + "description": "The field in the player's stats that contains the amount of deaths" + }, + "killstreak": { + "type": "string", + "description": "The field in the player's stats that contains the amount of kills in a row" + }, + "assists": { + "type": "string", + "description": "The field in the player's stats that contains the amount of assists" + }, + "finalKills": { + "type": "string", + "description": "The field in the player's stats that contains the amount of final kills" + }, + "finalDeaths": { + "type": "string", + "description": "The field in the player's stats that contains the amount of final deaths" + } + }, + "oneOf": [ + { + "required": [ + "timeLimit" + ] + }, + { + "required": [ + "gameLimit" + ] + } + ], + "required": [ + "tourneyField", + "wins" + ], + "additionalProperties": { + "type": "string" + } + } + }, + "required": [ + "id", + "name", + "icon", + "iteration", + "duration", + "data" + ] + } + } + } +} \ No newline at end of file diff --git a/src/main/resources/data/tournaments.json b/src/main/resources/data/tournaments.json index cf9be7e..80724e3 100644 --- a/src/main/resources/data/tournaments.json +++ b/src/main/resources/data/tournaments.json @@ -1,4 +1,5 @@ { + "lastUpdated": 1725138732, "tournaments": [ { "id": 0, @@ -10,16 +11,16 @@ "end": 1543186800 }, "data": { - "timeLimit" : 1200, - "tourneyField" : "bedwars4s_0", - "wins" : "Bedwars.tourney_bedwars4s_0_wins_bedwars", - "losses" : "Bedwars.tourney_bedwars4s_0_losses_bedwars", - "finalKills" : "Bedwars.tourney_bedwars4s_0_final_kills_bedwars", - "finalDeaths" : "Bedwars.tourney_bedwars4s_0_final_deaths_bedwars", - "winstreak" : "Bedwars.tourney_bedwars4s_0_winstreak2", - "killstreak" : "Bedwars.tourney_bedwars4s_0_killstreak_bedwars", - "kills" : "Bedwars.tourney_bedwars4s_0_kills_bedwars", - "deaths" : "Bedwars.tourney_bedwars4s_0_deaths_bedwars" + "timeLimit": 1200, + "tourneyField": "bedwars4s_0", + "wins": "Bedwars.tourney_bedwars4s_0_wins_bedwars", + "losses": "Bedwars.tourney_bedwars4s_0_losses_bedwars", + "finalKills": "Bedwars.tourney_bedwars4s_0_final_kills_bedwars", + "finalDeaths": "Bedwars.tourney_bedwars4s_0_final_deaths_bedwars", + "winstreak": "Bedwars.tourney_bedwars4s_0_winstreak2", + "killstreak": "Bedwars.tourney_bedwars4s_0_killstreak_bedwars", + "kills": "Bedwars.tourney_bedwars4s_0_kills_bedwars", + "deaths": "Bedwars.tourney_bedwars4s_0_deaths_bedwars" } }, { @@ -32,11 +33,11 @@ "end": 1546815600 }, "data": { - "timeLimit" : 720, - "tourneyField" : "blitz_duo_0", - "wins" : "HungerGames.tourney_blitz_duo_0_wins_teams", - "kills" : "HungerGames.tourney_blitz_duo_0_kills", - "deaths" : "HungerGames.tourney_blitz_duo_0_deaths" + "timeLimit": 720, + "tourneyField": "blitz_duo_0", + "wins": "HungerGames.tourney_blitz_duo_0_wins_teams", + "kills": "HungerGames.tourney_blitz_duo_0_kills", + "deaths": "HungerGames.tourney_blitz_duo_0_deaths" } }, { @@ -49,14 +50,14 @@ "end": 1552860000 }, "data": { - "timeLimit" : 480, - "tourneyField" : "sw_crazy_solo_0", - "wins" : "SkyWars.tourney_sw_crazy_solo_0_wins", - "losses" : "SkyWars.tourney_sw_crazy_solo_0_losses", - "winstreak" : "SkyWars.tourney_sw_crazy_solo_0_win_streak", - "kills" : "SkyWars.tourney_sw_crazy_solo_0_kills", - "deaths" : "SkyWars.tourney_sw_crazy_solo_0_deaths", - "killstreak" : "SkyWars.tourney_sw_crazy_solo_0_killstreak" + "timeLimit": 480, + "tourneyField": "sw_crazy_solo_0", + "wins": "SkyWars.tourney_sw_crazy_solo_0_wins", + "losses": "SkyWars.tourney_sw_crazy_solo_0_losses", + "winstreak": "SkyWars.tourney_sw_crazy_solo_0_win_streak", + "kills": "SkyWars.tourney_sw_crazy_solo_0_kills", + "deaths": "SkyWars.tourney_sw_crazy_solo_0_deaths", + "killstreak": "SkyWars.tourney_sw_crazy_solo_0_killstreak" } }, { @@ -69,11 +70,11 @@ "end": 1572213600 }, "data": { - "timeLimit" : 480, - "tourneyField" : "mcgo_defusal_0", - "wins" : "MCGO.game_wins_tourney_mcgo_defusal_0", - "kills" : "MCGO.kills_tourney_mcgo_defusal_0", - "deaths" : "MCGO.deaths_tourney_mcgo_defusal_0" + "timeLimit": 480, + "tourneyField": "mcgo_defusal_0", + "wins": "MCGO.game_wins_tourney_mcgo_defusal_0", + "kills": "MCGO.kills_tourney_mcgo_defusal_0", + "deaths": "MCGO.deaths_tourney_mcgo_defusal_0" } }, { @@ -86,16 +87,16 @@ "end": 1576450800 }, "data": { - "gameLimit" : 75, - "tourneyField" : "bedwars_two_four_0", - "wins" : "Bedwars.tourney_bedwars_two_four_0_wins_bedwars", - "losses" : "Bedwars.tourney_bedwars_two_four_0_losses_bedwars", - "finalKills" : "Bedwars.tourney_bedwars_two_four_0_final_kills_bedwars", - "finalDeaths" : "Bedwars.tourney_bedwars_two_four_0_final_deaths_bedwars", - "winstreak" : "Bedwars.tourney_bedwars_two_four_0_winstreak2", - "killstreak" : "Bedwars.tourney_bedwars_two_four_0_killstreak_bedwars", - "kills" : "Bedwars.tourney_bedwars_two_four_0_kills_bedwars", - "deaths" : "Bedwars.tourney_bedwars_two_four_0_deaths_bedwars" + "gameLimit": 75, + "tourneyField": "bedwars_two_four_0", + "wins": "Bedwars.tourney_bedwars_two_four_0_wins_bedwars", + "losses": "Bedwars.tourney_bedwars_two_four_0_losses_bedwars", + "finalKills": "Bedwars.tourney_bedwars_two_four_0_final_kills_bedwars", + "finalDeaths": "Bedwars.tourney_bedwars_two_four_0_final_deaths_bedwars", + "winstreak": "Bedwars.tourney_bedwars_two_four_0_winstreak2", + "killstreak": "Bedwars.tourney_bedwars_two_four_0_killstreak_bedwars", + "kills": "Bedwars.tourney_bedwars_two_four_0_kills_bedwars", + "deaths": "Bedwars.tourney_bedwars_two_four_0_deaths_bedwars" } }, { @@ -108,11 +109,11 @@ "end": 1580684400 }, "data": { - "gameLimit" : 250, - "tourneyField" : "quake_solo2_0", - "wins" : "Quake.wins_tourney_quake_solo2_1", - "kills" : "Quake.kills_tourney_quake_solo2_1", - "deaths" : "Quake.deaths_tourney_quake_solo2_1" + "gameLimit": 250, + "tourneyField": "quake_solo2_0", + "wins": "Quake.wins_tourney_quake_solo2_1", + "kills": "Quake.kills_tourney_quake_solo2_1", + "deaths": "Quake.deaths_tourney_quake_solo2_1" } }, { @@ -125,14 +126,14 @@ "end": 1586124000 }, "data": { - "gameLimit" : 120, - "tourneyField" : "sw_insane_doubles_0", - "wins" : "SkyWars.tourney_sw_insane_doubles_0_wins", - "losses" : "SkyWars.tourney_sw_insane_doubles_0_losses", - "winstreak" : "SkyWars.tourney_sw_insane_doubles_0_win_streak", - "kills" : "SkyWars.tourney_sw_insane_doubles_0_kills", - "deaths" : "SkyWars.tourney_sw_insane_doubles_0_deaths", - "killstreak" : "SkyWars.tourney_sw_insane_doubles_0_killstreak" + "gameLimit": 120, + "tourneyField": "sw_insane_doubles_0", + "wins": "SkyWars.tourney_sw_insane_doubles_0_wins", + "losses": "SkyWars.tourney_sw_insane_doubles_0_losses", + "winstreak": "SkyWars.tourney_sw_insane_doubles_0_win_streak", + "kills": "SkyWars.tourney_sw_insane_doubles_0_kills", + "deaths": "SkyWars.tourney_sw_insane_doubles_0_deaths", + "killstreak": "SkyWars.tourney_sw_insane_doubles_0_killstreak" } }, { @@ -145,16 +146,16 @@ "end": 1592172000 }, "data": { - "gameLimit" : 80, - "tourneyField" : "bedwars4s_1", - "wins" : "Bedwars.tourney_bedwars4s_1_wins_bedwars", - "losses" : "Bedwars.tourney_bedwars4s_1_losses_bedwars", - "finalKills" : "Bedwars.tourney_bedwars4s_1_final_kills_bedwars", - "finalDeaths" : "Bedwars.tourney_bedwars4s_1_final_deaths_bedwars", - "winstreak" : "Bedwars.tourney_bedwars4s_1_winstreak2", - "killstreak" : "Bedwars.tourney_bedwars4s_1_killstreak_bedwars", - "kills" : "Bedwars.tourney_bedwars4s_1_kills_bedwars", - "deaths" : "Bedwars.tourney_bedwars4s_1_deaths_bedwars" + "gameLimit": 80, + "tourneyField": "bedwars4s_1", + "wins": "Bedwars.tourney_bedwars4s_1_wins_bedwars", + "losses": "Bedwars.tourney_bedwars4s_1_losses_bedwars", + "finalKills": "Bedwars.tourney_bedwars4s_1_final_kills_bedwars", + "finalDeaths": "Bedwars.tourney_bedwars4s_1_final_deaths_bedwars", + "winstreak": "Bedwars.tourney_bedwars4s_1_winstreak2", + "killstreak": "Bedwars.tourney_bedwars4s_1_killstreak_bedwars", + "kills": "Bedwars.tourney_bedwars4s_1_kills_bedwars", + "deaths": "Bedwars.tourney_bedwars4s_1_deaths_bedwars" } }, { @@ -167,9 +168,9 @@ "end": 1597010400 }, "data": { - "gameLimit" : 72, - "tourneyField" : "gingerbread_solo_0", - "wins" : "GingerBread.tourney_gingerbread_solo_0_gold_trophy" + "gameLimit": 72, + "tourneyField": "gingerbread_solo_0", + "wins": "GingerBread.tourney_gingerbread_solo_0_gold_trophy" } }, { @@ -182,11 +183,11 @@ "end": 1603058400 }, "data": { - "gameLimit" : 70, - "tourneyField" : "blitz_duo_1", - "wins" : "HungerGames.tourney_blitz_duo_1_wins_teams", - "kills" : "HungerGames.tourney_blitz_duo_1_kills", - "deaths" : "HungerGames.tourney_blitz_duo_1_deaths" + "gameLimit": 70, + "tourneyField": "blitz_duo_1", + "wins": "HungerGames.tourney_blitz_duo_1_wins_teams", + "kills": "HungerGames.tourney_blitz_duo_1_kills", + "deaths": "HungerGames.tourney_blitz_duo_1_deaths" } }, { @@ -199,11 +200,11 @@ "end": 1608505200 }, "data": { - "gameLimit" : 60, - "tourneyField" : "grinch_simulator_0", - "wins" : "Arcade.wins_grinch_simulator_v2_tourney", - "losses" : "Arcade.losses_grinch_simulator_v2_tourney", - "grinch_gifts" : "Arcade.gifts_grinch_simulator_v2_tourney" + "gameLimit": 60, + "tourneyField": "grinch_simulator_0", + "wins": "Arcade.wins_grinch_simulator_v2_tourney", + "losses": "Arcade.losses_grinch_simulator_v2_tourney", + "grinch_gifts": "Arcade.gifts_grinch_simulator_v2_tourney" } }, { @@ -216,11 +217,11 @@ "end": 1615158000 }, "data": { - "gameLimit" : 40, - "tourneyField" : "mcgo_defusal_1", - "wins" : "MCGO.game_wins_tourney_mcgo_defusal_0", - "kills" : "MCGO.kills_tourney_mcgo_defusal_0", - "deaths" : "MCGO.deaths_tourney_mcgo_defusal_0" + "gameLimit": 40, + "tourneyField": "mcgo_defusal_1", + "wins": "MCGO.game_wins_tourney_mcgo_defusal_0", + "kills": "MCGO.kills_tourney_mcgo_defusal_0", + "deaths": "MCGO.deaths_tourney_mcgo_defusal_0" } }, { @@ -233,10 +234,10 @@ "end": 1629064800 }, "data": { - "gameLimit" : 120, - "tourneyField" : "tnt_run_0", - "wins" : "TNTGames.wins_tourney_tnt_run_0", - "losses" : "TNTGames.deaths_tourney_tnt_run_0" + "gameLimit": 120, + "tourneyField": "tnt_run_0", + "wins": "TNTGames.wins_tourney_tnt_run_0", + "losses": "TNTGames.deaths_tourney_tnt_run_0" } }, { @@ -249,14 +250,14 @@ "end": 1634508000 }, "data": { - "gameLimit" : 120, - "tourneyField" : "sw_insane_doubles_1", - "wins" : "SkyWars.tourney_sw_insane_doubles_1_wins", - "losses" : "SkyWars.tourney_sw_insane_doubles_1_losses", - "winstreak" : "SkyWars.tourney_sw_insane_doubles_1_win_streak", - "kills" : "SkyWars.tourney_sw_insane_doubles_1_kills", - "deaths" : "SkyWars.tourney_sw_insane_doubles_1_deaths", - "killstreak" : "SkyWars.tourney_sw_insane_doubles_1_killstreak" + "gameLimit": 120, + "tourneyField": "sw_insane_doubles_1", + "wins": "SkyWars.tourney_sw_insane_doubles_1_wins", + "losses": "SkyWars.tourney_sw_insane_doubles_1_losses", + "winstreak": "SkyWars.tourney_sw_insane_doubles_1_win_streak", + "kills": "SkyWars.tourney_sw_insane_doubles_1_kills", + "deaths": "SkyWars.tourney_sw_insane_doubles_1_deaths", + "killstreak": "SkyWars.tourney_sw_insane_doubles_1_killstreak" } }, { @@ -269,11 +270,11 @@ "end": 1639954800 }, "data": { - "gameLimit" : 200, - "tourneyField" : "quake_solo2_1", - "wins" : "Quake.wins_tourney_quake_solo2_1", - "kills" : "Quake.kills_tourney_quake_solo2_1", - "deaths" : "Quake.deaths_tourney_quake_solo2_1" + "gameLimit": 200, + "tourneyField": "quake_solo2_1", + "wins": "Quake.wins_tourney_quake_solo2_1", + "kills": "Quake.kills_tourney_quake_solo2_1", + "deaths": "Quake.deaths_tourney_quake_solo2_1" } }, { @@ -286,16 +287,16 @@ "end": 1647813600 }, "data": { - "gameLimit" : 40, - "tourneyField" : "bedwars_two_four_0", - "wins" : "Bedwars.tourney_bedwars_two_four_0_wins_bedwars", - "losses" : "Bedwars.tourney_bedwars_two_four_0_losses_bedwars", - "finalKills" : "Bedwars.tourney_bedwars_two_four_0_final_kills_bedwars", - "finalDeaths" : "Bedwars.tourney_bedwars_two_four_0_final_deaths_bedwars", - "winstreak" : "Bedwars.tourney_bedwars_two_four_0_winstreak2", - "killstreak" : "Bedwars.tourney_bedwars_two_four_0_killstreak_bedwars", - "kills" : "Bedwars.tourney_bedwars_two_four_0_kills_bedwars", - "deaths" : "Bedwars.tourney_bedwars_two_four_0_deaths_bedwars" + "gameLimit": 40, + "tourneyField": "bedwars_two_four_0", + "wins": "Bedwars.tourney_bedwars_two_four_0_wins_bedwars", + "losses": "Bedwars.tourney_bedwars_two_four_0_losses_bedwars", + "finalKills": "Bedwars.tourney_bedwars_two_four_0_final_kills_bedwars", + "finalDeaths": "Bedwars.tourney_bedwars_two_four_0_final_deaths_bedwars", + "winstreak": "Bedwars.tourney_bedwars_two_four_0_winstreak2", + "killstreak": "Bedwars.tourney_bedwars_two_four_0_killstreak_bedwars", + "kills": "Bedwars.tourney_bedwars_two_four_0_kills_bedwars", + "deaths": "Bedwars.tourney_bedwars_two_four_0_deaths_bedwars" } }, { @@ -308,12 +309,12 @@ "end": 1660514400 }, "data": { - "gameLimit" : 50, - "tourneyField" : "mini_walls_0", - "wins" : "Arcade.wins_tourney_mini_walls_0", - "deaths" : "Arcade.deaths_tourney_mini_walls_0", - "kills" : "Arcade.kills_tourney_mini_walls_0", - "finalKills" : "Arcade.final_kills_tourney_mini_walls_0" + "gameLimit": 50, + "tourneyField": "mini_walls_0", + "wins": "Arcade.wins_tourney_mini_walls_0", + "deaths": "Arcade.deaths_tourney_mini_walls_0", + "kills": "Arcade.kills_tourney_mini_walls_0", + "finalKills": "Arcade.final_kills_tourney_mini_walls_0" } }, { @@ -326,9 +327,9 @@ "end": 1667167200 }, "data": { - "gameLimit" : 72, - "tourneyField" : "gingerbread_solo_1", - "wins" : "GingerBread.tourney_gingerbread_solo_1_gold_trophy" + "gameLimit": 72, + "tourneyField": "gingerbread_solo_1", + "wins": "GingerBread.tourney_gingerbread_solo_1_gold_trophy" } }, { @@ -341,11 +342,11 @@ "end": 1679868000 }, "data": { - "gameLimit" : 70, - "tourneyField" : "blitz_duo_2", - "wins" : "HungerGames.tourney_blitz_duo_2_wins_teams", - "kills" : "HungerGames.tourney_blitz_duo_2_kills", - "deaths" : "HungerGames.tourney_blitz_duo_2_deaths" + "gameLimit": 70, + "tourneyField": "blitz_duo_2", + "wins": "HungerGames.tourney_blitz_duo_2_wins_teams", + "kills": "HungerGames.tourney_blitz_duo_2_kills", + "deaths": "HungerGames.tourney_blitz_duo_2_deaths" } }, { @@ -358,11 +359,11 @@ "end": 1693163760 }, "data": { - "gameLimit" : 100, - "tourneyField" : "wool_wars_0", - "wins" : "WoolGames.wool_wars.stats.tourney.wool_wars_0.wins", - "kills" : "WoolGames.wool_wars.stats.tourney.wool_wars_0.kills", - "deaths" : "WoolGames.wool_wars.stats.tourney.wool_wars_0.deaths" + "gameLimit": 100, + "tourneyField": "wool_wars_0", + "wins": "WoolGames.wool_wars.stats.tourney.wool_wars_0.wins", + "kills": "WoolGames.wool_wars.stats.tourney.wool_wars_0.kills", + "deaths": "WoolGames.wool_wars.stats.tourney.wool_wars_0.deaths" } }, { @@ -375,11 +376,11 @@ "end": 1694383200 }, "data": { - "gameLimit" : 40, - "tourneyField" : "wool_wars_1", - "wins" : "WoolGames.wool_wars.stats.tourney.wool_wars_1.wins", - "kills" : "WoolGames.wool_wars.stats.tourney.wool_wars_1.kills", - "deaths" : "WoolGames.wool_wars.stats.tourney.wool_wars_1.deaths" + "gameLimit": 40, + "tourneyField": "wool_wars_1", + "wins": "WoolGames.wool_wars.stats.tourney.wool_wars_1.wins", + "kills": "WoolGames.wool_wars.stats.tourney.wool_wars_1.kills", + "deaths": "WoolGames.wool_wars.stats.tourney.wool_wars_1.deaths" } }, { @@ -392,16 +393,16 @@ "end": 1698012000 }, "data": { - "gameLimit" : 40, - "tourneyField" : "bedwars_two_four_1", - "wins" : "Bedwars.tourney_bedwars_two_four_1_wins_bedwars", - "losses" : "Bedwars.tourney_bedwars_two_four_1_losses_bedwars", - "finalKills" : "Bedwars.tourney_bedwars_two_four_1_final_kills_bedwars", - "finalDeaths" : "Bedwars.tourney_bedwars_two_four_1_final_deaths_bedwars", - "winstreak" : "Bedwars.tourney_bedwars_two_four_1_winstreak2", - "killstreak" : "Bedwars.tourney_bedwars_two_four_1_killstreak_bedwars", - "kills" : "Bedwars.tourney_bedwars_two_four_1_kills_bedwars", - "deaths" : "Bedwars.tourney_bedwars_two_four_1_deaths_bedwars" + "gameLimit": 40, + "tourneyField": "bedwars_two_four_1", + "wins": "Bedwars.tourney_bedwars_two_four_1_wins_bedwars", + "losses": "Bedwars.tourney_bedwars_two_four_1_losses_bedwars", + "finalKills": "Bedwars.tourney_bedwars_two_four_1_final_kills_bedwars", + "finalDeaths": "Bedwars.tourney_bedwars_two_four_1_final_deaths_bedwars", + "winstreak": "Bedwars.tourney_bedwars_two_four_1_winstreak2", + "killstreak": "Bedwars.tourney_bedwars_two_four_1_killstreak_bedwars", + "kills": "Bedwars.tourney_bedwars_two_four_1_kills_bedwars", + "deaths": "Bedwars.tourney_bedwars_two_four_1_deaths_bedwars" } }, { @@ -414,11 +415,11 @@ "end": 1702854000 }, "data": { - "gameLimit" : 60, - "tourneyField" : "grinch_simulator_1", - "wins" : "Arcade.wins_grinch_simulator_v2_tourney_grinch_simulator_1", - "losses" : "Arcade.losses_grinch_simulator_v2_tourney_grinch_simulator_1", - "grinch_gifts" : "Arcade.gifts_grinch_simulator_v2_tourney_grinch_simulator_1" + "gameLimit": 60, + "tourneyField": "grinch_simulator_1", + "wins": "Arcade.wins_grinch_simulator_v2_tourney_grinch_simulator_1", + "losses": "Arcade.losses_grinch_simulator_v2_tourney_grinch_simulator_1", + "grinch_gifts": "Arcade.gifts_grinch_simulator_v2_tourney_grinch_simulator_1" } }, { @@ -431,10 +432,10 @@ "end": 1710712800 }, "data": { - "gameLimit" : 80, - "tourneyField" : "tnt_run_1", - "wins" : "TNTGames.wins_tourney_tnt_run_1", - "losses" : "TNTGames.deaths_tourney_tnt_run_1" + "gameLimit": 80, + "tourneyField": "tnt_run_1", + "wins": "TNTGames.wins_tourney_tnt_run_1", + "losses": "TNTGames.deaths_tourney_tnt_run_1" } }, { @@ -447,15 +448,15 @@ "end": 1719180000 }, "data": { - "gameLimit" : 120, - "tourneyField" : "sw_normal_doubles_0", - "wins" : "SkyWars.tourney_sw_normal_doubles_0_wins", - "losses" : "SkyWars.tourney_sw_normal_doubles_0_losses", - "kills" : "SkyWars.tourney_sw_normal_doubles_0_kills", - "deaths" : "SkyWars.tourney_sw_normal_doubles_0_deaths", - "assists" : "SkyWars.tourney_sw_normal_doubles_0_assists", - "winstreak" : "SkyWars.tourney_sw_normal_doubles_0_win_streak", - "killstreak" : "SkyWars.tourney_sw_normal_doubles_0_killstreak" + "gameLimit": 120, + "tourneyField": "sw_normal_doubles_0", + "wins": "SkyWars.tourney_sw_normal_doubles_0_wins", + "losses": "SkyWars.tourney_sw_normal_doubles_0_losses", + "kills": "SkyWars.tourney_sw_normal_doubles_0_kills", + "deaths": "SkyWars.tourney_sw_normal_doubles_0_deaths", + "assists": "SkyWars.tourney_sw_normal_doubles_0_assists", + "winstreak": "SkyWars.tourney_sw_normal_doubles_0_win_streak", + "killstreak": "SkyWars.tourney_sw_normal_doubles_0_killstreak" } } ] diff --git a/target/classes/data/mwclasses.json b/target/classes/data/mwclasses.json index fe3e09e..f64e364 100644 --- a/target/classes/data/mwclasses.json +++ b/target/classes/data/mwclasses.json @@ -1,9 +1,9 @@ { "lastUpdated": 0, - "classes" : [ + "classes": [ { - "id" : "cow", - "name" : "Cow", + "id": "cow", + "name": "Cow", "skins": [ { "name": "Moo Brawler", @@ -625,8 +625,8 @@ ] }, { - "id" : "skeleton", - "name" : "Skeleton", + "id": "skeleton", + "name": "Skeleton", "skins": [ { "name": "Marksman", diff --git a/target/classes/data/schemas/tournaments-schema.json b/target/classes/data/schemas/tournaments-schema.json new file mode 100644 index 0000000..c35dabc --- /dev/null +++ b/target/classes/data/schemas/tournaments-schema.json @@ -0,0 +1,133 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Tournaments Schema", + "description": "Schema for tournament data, including the name, iteration, duration, and data for navigating the api to find specific stats", + "type": "object", + "properties": { + "lastUpdated": { + "type": "integer", + "description": "Unix timestamp of the last time the data was updated" + }, + "tournaments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "The id of the tournament, denoted by the index in which it happened" + }, + "name": { + "type": "string", + "description": "The name of the tournament" + }, + "icon": { + "type": "string", + "description": "The path to the icon of the tournament, located at https://hypixel.net/styles/hypixel-v2/images/game-icons/{icon}" + }, + "iteration": { + "type": "integer", + "description": "The iteration of the tournament" + }, + "duration": { + "type": "object", + "properties": { + "start": { + "type": "integer", + "description": "Unix timestamp of the start of the tournament" + }, + "end": { + "type": "integer", + "description": "Unix timestamp of the end of the tournament" + } + }, + "required": [ + "start", + "end" + ] + }, + "data": { + "type": "object", + "properties": { + "timeLimit": { + "type": "integer", + "description": "The maximum playtime for participants in minutes" + }, + "gameLimit": { + "type": "integer", + "description": "The maximum amount of games a participant can play" + }, + "tourneyField": { + "type": "string", + "description": "The field in the player's data that contains the tournament data" + }, + "wins": { + "type": "string", + "description": "The field in the player's stats that contains the amount of wins" + }, + "losses": { + "type": "string", + "description": "The field in the player's stats that contains the amount of losses" + }, + "winstreak": { + "type": "string", + "description": "The field in the player's stats that contains the amount of wins in a row" + }, + "kills": { + "type": "string", + "description": "The field in the player's stats that contains the amount of kills" + }, + "deaths": { + "type": "string", + "description": "The field in the player's stats that contains the amount of deaths" + }, + "killstreak": { + "type": "string", + "description": "The field in the player's stats that contains the amount of kills in a row" + }, + "assists": { + "type": "string", + "description": "The field in the player's stats that contains the amount of assists" + }, + "finalKills": { + "type": "string", + "description": "The field in the player's stats that contains the amount of final kills" + }, + "finalDeaths": { + "type": "string", + "description": "The field in the player's stats that contains the amount of final deaths" + } + }, + "oneOf": [ + { + "required": [ + "timeLimit" + ] + }, + { + "required": [ + "gameLimit" + ] + } + ], + "required": [ + "tourneyField", + "wins" + ], + "additionalProperties": { + "type": "string" + } + } + }, + "required": [ + "id", + "name", + "icon", + "iteration", + "duration", + "data" + ] + } + } + } +} \ No newline at end of file diff --git a/target/classes/data/tournaments.json b/target/classes/data/tournaments.json index cf9be7e..80724e3 100644 --- a/target/classes/data/tournaments.json +++ b/target/classes/data/tournaments.json @@ -1,4 +1,5 @@ { + "lastUpdated": 1725138732, "tournaments": [ { "id": 0, @@ -10,16 +11,16 @@ "end": 1543186800 }, "data": { - "timeLimit" : 1200, - "tourneyField" : "bedwars4s_0", - "wins" : "Bedwars.tourney_bedwars4s_0_wins_bedwars", - "losses" : "Bedwars.tourney_bedwars4s_0_losses_bedwars", - "finalKills" : "Bedwars.tourney_bedwars4s_0_final_kills_bedwars", - "finalDeaths" : "Bedwars.tourney_bedwars4s_0_final_deaths_bedwars", - "winstreak" : "Bedwars.tourney_bedwars4s_0_winstreak2", - "killstreak" : "Bedwars.tourney_bedwars4s_0_killstreak_bedwars", - "kills" : "Bedwars.tourney_bedwars4s_0_kills_bedwars", - "deaths" : "Bedwars.tourney_bedwars4s_0_deaths_bedwars" + "timeLimit": 1200, + "tourneyField": "bedwars4s_0", + "wins": "Bedwars.tourney_bedwars4s_0_wins_bedwars", + "losses": "Bedwars.tourney_bedwars4s_0_losses_bedwars", + "finalKills": "Bedwars.tourney_bedwars4s_0_final_kills_bedwars", + "finalDeaths": "Bedwars.tourney_bedwars4s_0_final_deaths_bedwars", + "winstreak": "Bedwars.tourney_bedwars4s_0_winstreak2", + "killstreak": "Bedwars.tourney_bedwars4s_0_killstreak_bedwars", + "kills": "Bedwars.tourney_bedwars4s_0_kills_bedwars", + "deaths": "Bedwars.tourney_bedwars4s_0_deaths_bedwars" } }, { @@ -32,11 +33,11 @@ "end": 1546815600 }, "data": { - "timeLimit" : 720, - "tourneyField" : "blitz_duo_0", - "wins" : "HungerGames.tourney_blitz_duo_0_wins_teams", - "kills" : "HungerGames.tourney_blitz_duo_0_kills", - "deaths" : "HungerGames.tourney_blitz_duo_0_deaths" + "timeLimit": 720, + "tourneyField": "blitz_duo_0", + "wins": "HungerGames.tourney_blitz_duo_0_wins_teams", + "kills": "HungerGames.tourney_blitz_duo_0_kills", + "deaths": "HungerGames.tourney_blitz_duo_0_deaths" } }, { @@ -49,14 +50,14 @@ "end": 1552860000 }, "data": { - "timeLimit" : 480, - "tourneyField" : "sw_crazy_solo_0", - "wins" : "SkyWars.tourney_sw_crazy_solo_0_wins", - "losses" : "SkyWars.tourney_sw_crazy_solo_0_losses", - "winstreak" : "SkyWars.tourney_sw_crazy_solo_0_win_streak", - "kills" : "SkyWars.tourney_sw_crazy_solo_0_kills", - "deaths" : "SkyWars.tourney_sw_crazy_solo_0_deaths", - "killstreak" : "SkyWars.tourney_sw_crazy_solo_0_killstreak" + "timeLimit": 480, + "tourneyField": "sw_crazy_solo_0", + "wins": "SkyWars.tourney_sw_crazy_solo_0_wins", + "losses": "SkyWars.tourney_sw_crazy_solo_0_losses", + "winstreak": "SkyWars.tourney_sw_crazy_solo_0_win_streak", + "kills": "SkyWars.tourney_sw_crazy_solo_0_kills", + "deaths": "SkyWars.tourney_sw_crazy_solo_0_deaths", + "killstreak": "SkyWars.tourney_sw_crazy_solo_0_killstreak" } }, { @@ -69,11 +70,11 @@ "end": 1572213600 }, "data": { - "timeLimit" : 480, - "tourneyField" : "mcgo_defusal_0", - "wins" : "MCGO.game_wins_tourney_mcgo_defusal_0", - "kills" : "MCGO.kills_tourney_mcgo_defusal_0", - "deaths" : "MCGO.deaths_tourney_mcgo_defusal_0" + "timeLimit": 480, + "tourneyField": "mcgo_defusal_0", + "wins": "MCGO.game_wins_tourney_mcgo_defusal_0", + "kills": "MCGO.kills_tourney_mcgo_defusal_0", + "deaths": "MCGO.deaths_tourney_mcgo_defusal_0" } }, { @@ -86,16 +87,16 @@ "end": 1576450800 }, "data": { - "gameLimit" : 75, - "tourneyField" : "bedwars_two_four_0", - "wins" : "Bedwars.tourney_bedwars_two_four_0_wins_bedwars", - "losses" : "Bedwars.tourney_bedwars_two_four_0_losses_bedwars", - "finalKills" : "Bedwars.tourney_bedwars_two_four_0_final_kills_bedwars", - "finalDeaths" : "Bedwars.tourney_bedwars_two_four_0_final_deaths_bedwars", - "winstreak" : "Bedwars.tourney_bedwars_two_four_0_winstreak2", - "killstreak" : "Bedwars.tourney_bedwars_two_four_0_killstreak_bedwars", - "kills" : "Bedwars.tourney_bedwars_two_four_0_kills_bedwars", - "deaths" : "Bedwars.tourney_bedwars_two_four_0_deaths_bedwars" + "gameLimit": 75, + "tourneyField": "bedwars_two_four_0", + "wins": "Bedwars.tourney_bedwars_two_four_0_wins_bedwars", + "losses": "Bedwars.tourney_bedwars_two_four_0_losses_bedwars", + "finalKills": "Bedwars.tourney_bedwars_two_four_0_final_kills_bedwars", + "finalDeaths": "Bedwars.tourney_bedwars_two_four_0_final_deaths_bedwars", + "winstreak": "Bedwars.tourney_bedwars_two_four_0_winstreak2", + "killstreak": "Bedwars.tourney_bedwars_two_four_0_killstreak_bedwars", + "kills": "Bedwars.tourney_bedwars_two_four_0_kills_bedwars", + "deaths": "Bedwars.tourney_bedwars_two_four_0_deaths_bedwars" } }, { @@ -108,11 +109,11 @@ "end": 1580684400 }, "data": { - "gameLimit" : 250, - "tourneyField" : "quake_solo2_0", - "wins" : "Quake.wins_tourney_quake_solo2_1", - "kills" : "Quake.kills_tourney_quake_solo2_1", - "deaths" : "Quake.deaths_tourney_quake_solo2_1" + "gameLimit": 250, + "tourneyField": "quake_solo2_0", + "wins": "Quake.wins_tourney_quake_solo2_1", + "kills": "Quake.kills_tourney_quake_solo2_1", + "deaths": "Quake.deaths_tourney_quake_solo2_1" } }, { @@ -125,14 +126,14 @@ "end": 1586124000 }, "data": { - "gameLimit" : 120, - "tourneyField" : "sw_insane_doubles_0", - "wins" : "SkyWars.tourney_sw_insane_doubles_0_wins", - "losses" : "SkyWars.tourney_sw_insane_doubles_0_losses", - "winstreak" : "SkyWars.tourney_sw_insane_doubles_0_win_streak", - "kills" : "SkyWars.tourney_sw_insane_doubles_0_kills", - "deaths" : "SkyWars.tourney_sw_insane_doubles_0_deaths", - "killstreak" : "SkyWars.tourney_sw_insane_doubles_0_killstreak" + "gameLimit": 120, + "tourneyField": "sw_insane_doubles_0", + "wins": "SkyWars.tourney_sw_insane_doubles_0_wins", + "losses": "SkyWars.tourney_sw_insane_doubles_0_losses", + "winstreak": "SkyWars.tourney_sw_insane_doubles_0_win_streak", + "kills": "SkyWars.tourney_sw_insane_doubles_0_kills", + "deaths": "SkyWars.tourney_sw_insane_doubles_0_deaths", + "killstreak": "SkyWars.tourney_sw_insane_doubles_0_killstreak" } }, { @@ -145,16 +146,16 @@ "end": 1592172000 }, "data": { - "gameLimit" : 80, - "tourneyField" : "bedwars4s_1", - "wins" : "Bedwars.tourney_bedwars4s_1_wins_bedwars", - "losses" : "Bedwars.tourney_bedwars4s_1_losses_bedwars", - "finalKills" : "Bedwars.tourney_bedwars4s_1_final_kills_bedwars", - "finalDeaths" : "Bedwars.tourney_bedwars4s_1_final_deaths_bedwars", - "winstreak" : "Bedwars.tourney_bedwars4s_1_winstreak2", - "killstreak" : "Bedwars.tourney_bedwars4s_1_killstreak_bedwars", - "kills" : "Bedwars.tourney_bedwars4s_1_kills_bedwars", - "deaths" : "Bedwars.tourney_bedwars4s_1_deaths_bedwars" + "gameLimit": 80, + "tourneyField": "bedwars4s_1", + "wins": "Bedwars.tourney_bedwars4s_1_wins_bedwars", + "losses": "Bedwars.tourney_bedwars4s_1_losses_bedwars", + "finalKills": "Bedwars.tourney_bedwars4s_1_final_kills_bedwars", + "finalDeaths": "Bedwars.tourney_bedwars4s_1_final_deaths_bedwars", + "winstreak": "Bedwars.tourney_bedwars4s_1_winstreak2", + "killstreak": "Bedwars.tourney_bedwars4s_1_killstreak_bedwars", + "kills": "Bedwars.tourney_bedwars4s_1_kills_bedwars", + "deaths": "Bedwars.tourney_bedwars4s_1_deaths_bedwars" } }, { @@ -167,9 +168,9 @@ "end": 1597010400 }, "data": { - "gameLimit" : 72, - "tourneyField" : "gingerbread_solo_0", - "wins" : "GingerBread.tourney_gingerbread_solo_0_gold_trophy" + "gameLimit": 72, + "tourneyField": "gingerbread_solo_0", + "wins": "GingerBread.tourney_gingerbread_solo_0_gold_trophy" } }, { @@ -182,11 +183,11 @@ "end": 1603058400 }, "data": { - "gameLimit" : 70, - "tourneyField" : "blitz_duo_1", - "wins" : "HungerGames.tourney_blitz_duo_1_wins_teams", - "kills" : "HungerGames.tourney_blitz_duo_1_kills", - "deaths" : "HungerGames.tourney_blitz_duo_1_deaths" + "gameLimit": 70, + "tourneyField": "blitz_duo_1", + "wins": "HungerGames.tourney_blitz_duo_1_wins_teams", + "kills": "HungerGames.tourney_blitz_duo_1_kills", + "deaths": "HungerGames.tourney_blitz_duo_1_deaths" } }, { @@ -199,11 +200,11 @@ "end": 1608505200 }, "data": { - "gameLimit" : 60, - "tourneyField" : "grinch_simulator_0", - "wins" : "Arcade.wins_grinch_simulator_v2_tourney", - "losses" : "Arcade.losses_grinch_simulator_v2_tourney", - "grinch_gifts" : "Arcade.gifts_grinch_simulator_v2_tourney" + "gameLimit": 60, + "tourneyField": "grinch_simulator_0", + "wins": "Arcade.wins_grinch_simulator_v2_tourney", + "losses": "Arcade.losses_grinch_simulator_v2_tourney", + "grinch_gifts": "Arcade.gifts_grinch_simulator_v2_tourney" } }, { @@ -216,11 +217,11 @@ "end": 1615158000 }, "data": { - "gameLimit" : 40, - "tourneyField" : "mcgo_defusal_1", - "wins" : "MCGO.game_wins_tourney_mcgo_defusal_0", - "kills" : "MCGO.kills_tourney_mcgo_defusal_0", - "deaths" : "MCGO.deaths_tourney_mcgo_defusal_0" + "gameLimit": 40, + "tourneyField": "mcgo_defusal_1", + "wins": "MCGO.game_wins_tourney_mcgo_defusal_0", + "kills": "MCGO.kills_tourney_mcgo_defusal_0", + "deaths": "MCGO.deaths_tourney_mcgo_defusal_0" } }, { @@ -233,10 +234,10 @@ "end": 1629064800 }, "data": { - "gameLimit" : 120, - "tourneyField" : "tnt_run_0", - "wins" : "TNTGames.wins_tourney_tnt_run_0", - "losses" : "TNTGames.deaths_tourney_tnt_run_0" + "gameLimit": 120, + "tourneyField": "tnt_run_0", + "wins": "TNTGames.wins_tourney_tnt_run_0", + "losses": "TNTGames.deaths_tourney_tnt_run_0" } }, { @@ -249,14 +250,14 @@ "end": 1634508000 }, "data": { - "gameLimit" : 120, - "tourneyField" : "sw_insane_doubles_1", - "wins" : "SkyWars.tourney_sw_insane_doubles_1_wins", - "losses" : "SkyWars.tourney_sw_insane_doubles_1_losses", - "winstreak" : "SkyWars.tourney_sw_insane_doubles_1_win_streak", - "kills" : "SkyWars.tourney_sw_insane_doubles_1_kills", - "deaths" : "SkyWars.tourney_sw_insane_doubles_1_deaths", - "killstreak" : "SkyWars.tourney_sw_insane_doubles_1_killstreak" + "gameLimit": 120, + "tourneyField": "sw_insane_doubles_1", + "wins": "SkyWars.tourney_sw_insane_doubles_1_wins", + "losses": "SkyWars.tourney_sw_insane_doubles_1_losses", + "winstreak": "SkyWars.tourney_sw_insane_doubles_1_win_streak", + "kills": "SkyWars.tourney_sw_insane_doubles_1_kills", + "deaths": "SkyWars.tourney_sw_insane_doubles_1_deaths", + "killstreak": "SkyWars.tourney_sw_insane_doubles_1_killstreak" } }, { @@ -269,11 +270,11 @@ "end": 1639954800 }, "data": { - "gameLimit" : 200, - "tourneyField" : "quake_solo2_1", - "wins" : "Quake.wins_tourney_quake_solo2_1", - "kills" : "Quake.kills_tourney_quake_solo2_1", - "deaths" : "Quake.deaths_tourney_quake_solo2_1" + "gameLimit": 200, + "tourneyField": "quake_solo2_1", + "wins": "Quake.wins_tourney_quake_solo2_1", + "kills": "Quake.kills_tourney_quake_solo2_1", + "deaths": "Quake.deaths_tourney_quake_solo2_1" } }, { @@ -286,16 +287,16 @@ "end": 1647813600 }, "data": { - "gameLimit" : 40, - "tourneyField" : "bedwars_two_four_0", - "wins" : "Bedwars.tourney_bedwars_two_four_0_wins_bedwars", - "losses" : "Bedwars.tourney_bedwars_two_four_0_losses_bedwars", - "finalKills" : "Bedwars.tourney_bedwars_two_four_0_final_kills_bedwars", - "finalDeaths" : "Bedwars.tourney_bedwars_two_four_0_final_deaths_bedwars", - "winstreak" : "Bedwars.tourney_bedwars_two_four_0_winstreak2", - "killstreak" : "Bedwars.tourney_bedwars_two_four_0_killstreak_bedwars", - "kills" : "Bedwars.tourney_bedwars_two_four_0_kills_bedwars", - "deaths" : "Bedwars.tourney_bedwars_two_four_0_deaths_bedwars" + "gameLimit": 40, + "tourneyField": "bedwars_two_four_0", + "wins": "Bedwars.tourney_bedwars_two_four_0_wins_bedwars", + "losses": "Bedwars.tourney_bedwars_two_four_0_losses_bedwars", + "finalKills": "Bedwars.tourney_bedwars_two_four_0_final_kills_bedwars", + "finalDeaths": "Bedwars.tourney_bedwars_two_four_0_final_deaths_bedwars", + "winstreak": "Bedwars.tourney_bedwars_two_four_0_winstreak2", + "killstreak": "Bedwars.tourney_bedwars_two_four_0_killstreak_bedwars", + "kills": "Bedwars.tourney_bedwars_two_four_0_kills_bedwars", + "deaths": "Bedwars.tourney_bedwars_two_four_0_deaths_bedwars" } }, { @@ -308,12 +309,12 @@ "end": 1660514400 }, "data": { - "gameLimit" : 50, - "tourneyField" : "mini_walls_0", - "wins" : "Arcade.wins_tourney_mini_walls_0", - "deaths" : "Arcade.deaths_tourney_mini_walls_0", - "kills" : "Arcade.kills_tourney_mini_walls_0", - "finalKills" : "Arcade.final_kills_tourney_mini_walls_0" + "gameLimit": 50, + "tourneyField": "mini_walls_0", + "wins": "Arcade.wins_tourney_mini_walls_0", + "deaths": "Arcade.deaths_tourney_mini_walls_0", + "kills": "Arcade.kills_tourney_mini_walls_0", + "finalKills": "Arcade.final_kills_tourney_mini_walls_0" } }, { @@ -326,9 +327,9 @@ "end": 1667167200 }, "data": { - "gameLimit" : 72, - "tourneyField" : "gingerbread_solo_1", - "wins" : "GingerBread.tourney_gingerbread_solo_1_gold_trophy" + "gameLimit": 72, + "tourneyField": "gingerbread_solo_1", + "wins": "GingerBread.tourney_gingerbread_solo_1_gold_trophy" } }, { @@ -341,11 +342,11 @@ "end": 1679868000 }, "data": { - "gameLimit" : 70, - "tourneyField" : "blitz_duo_2", - "wins" : "HungerGames.tourney_blitz_duo_2_wins_teams", - "kills" : "HungerGames.tourney_blitz_duo_2_kills", - "deaths" : "HungerGames.tourney_blitz_duo_2_deaths" + "gameLimit": 70, + "tourneyField": "blitz_duo_2", + "wins": "HungerGames.tourney_blitz_duo_2_wins_teams", + "kills": "HungerGames.tourney_blitz_duo_2_kills", + "deaths": "HungerGames.tourney_blitz_duo_2_deaths" } }, { @@ -358,11 +359,11 @@ "end": 1693163760 }, "data": { - "gameLimit" : 100, - "tourneyField" : "wool_wars_0", - "wins" : "WoolGames.wool_wars.stats.tourney.wool_wars_0.wins", - "kills" : "WoolGames.wool_wars.stats.tourney.wool_wars_0.kills", - "deaths" : "WoolGames.wool_wars.stats.tourney.wool_wars_0.deaths" + "gameLimit": 100, + "tourneyField": "wool_wars_0", + "wins": "WoolGames.wool_wars.stats.tourney.wool_wars_0.wins", + "kills": "WoolGames.wool_wars.stats.tourney.wool_wars_0.kills", + "deaths": "WoolGames.wool_wars.stats.tourney.wool_wars_0.deaths" } }, { @@ -375,11 +376,11 @@ "end": 1694383200 }, "data": { - "gameLimit" : 40, - "tourneyField" : "wool_wars_1", - "wins" : "WoolGames.wool_wars.stats.tourney.wool_wars_1.wins", - "kills" : "WoolGames.wool_wars.stats.tourney.wool_wars_1.kills", - "deaths" : "WoolGames.wool_wars.stats.tourney.wool_wars_1.deaths" + "gameLimit": 40, + "tourneyField": "wool_wars_1", + "wins": "WoolGames.wool_wars.stats.tourney.wool_wars_1.wins", + "kills": "WoolGames.wool_wars.stats.tourney.wool_wars_1.kills", + "deaths": "WoolGames.wool_wars.stats.tourney.wool_wars_1.deaths" } }, { @@ -392,16 +393,16 @@ "end": 1698012000 }, "data": { - "gameLimit" : 40, - "tourneyField" : "bedwars_two_four_1", - "wins" : "Bedwars.tourney_bedwars_two_four_1_wins_bedwars", - "losses" : "Bedwars.tourney_bedwars_two_four_1_losses_bedwars", - "finalKills" : "Bedwars.tourney_bedwars_two_four_1_final_kills_bedwars", - "finalDeaths" : "Bedwars.tourney_bedwars_two_four_1_final_deaths_bedwars", - "winstreak" : "Bedwars.tourney_bedwars_two_four_1_winstreak2", - "killstreak" : "Bedwars.tourney_bedwars_two_four_1_killstreak_bedwars", - "kills" : "Bedwars.tourney_bedwars_two_four_1_kills_bedwars", - "deaths" : "Bedwars.tourney_bedwars_two_four_1_deaths_bedwars" + "gameLimit": 40, + "tourneyField": "bedwars_two_four_1", + "wins": "Bedwars.tourney_bedwars_two_four_1_wins_bedwars", + "losses": "Bedwars.tourney_bedwars_two_four_1_losses_bedwars", + "finalKills": "Bedwars.tourney_bedwars_two_four_1_final_kills_bedwars", + "finalDeaths": "Bedwars.tourney_bedwars_two_four_1_final_deaths_bedwars", + "winstreak": "Bedwars.tourney_bedwars_two_four_1_winstreak2", + "killstreak": "Bedwars.tourney_bedwars_two_four_1_killstreak_bedwars", + "kills": "Bedwars.tourney_bedwars_two_four_1_kills_bedwars", + "deaths": "Bedwars.tourney_bedwars_two_four_1_deaths_bedwars" } }, { @@ -414,11 +415,11 @@ "end": 1702854000 }, "data": { - "gameLimit" : 60, - "tourneyField" : "grinch_simulator_1", - "wins" : "Arcade.wins_grinch_simulator_v2_tourney_grinch_simulator_1", - "losses" : "Arcade.losses_grinch_simulator_v2_tourney_grinch_simulator_1", - "grinch_gifts" : "Arcade.gifts_grinch_simulator_v2_tourney_grinch_simulator_1" + "gameLimit": 60, + "tourneyField": "grinch_simulator_1", + "wins": "Arcade.wins_grinch_simulator_v2_tourney_grinch_simulator_1", + "losses": "Arcade.losses_grinch_simulator_v2_tourney_grinch_simulator_1", + "grinch_gifts": "Arcade.gifts_grinch_simulator_v2_tourney_grinch_simulator_1" } }, { @@ -431,10 +432,10 @@ "end": 1710712800 }, "data": { - "gameLimit" : 80, - "tourneyField" : "tnt_run_1", - "wins" : "TNTGames.wins_tourney_tnt_run_1", - "losses" : "TNTGames.deaths_tourney_tnt_run_1" + "gameLimit": 80, + "tourneyField": "tnt_run_1", + "wins": "TNTGames.wins_tourney_tnt_run_1", + "losses": "TNTGames.deaths_tourney_tnt_run_1" } }, { @@ -447,15 +448,15 @@ "end": 1719180000 }, "data": { - "gameLimit" : 120, - "tourneyField" : "sw_normal_doubles_0", - "wins" : "SkyWars.tourney_sw_normal_doubles_0_wins", - "losses" : "SkyWars.tourney_sw_normal_doubles_0_losses", - "kills" : "SkyWars.tourney_sw_normal_doubles_0_kills", - "deaths" : "SkyWars.tourney_sw_normal_doubles_0_deaths", - "assists" : "SkyWars.tourney_sw_normal_doubles_0_assists", - "winstreak" : "SkyWars.tourney_sw_normal_doubles_0_win_streak", - "killstreak" : "SkyWars.tourney_sw_normal_doubles_0_killstreak" + "gameLimit": 120, + "tourneyField": "sw_normal_doubles_0", + "wins": "SkyWars.tourney_sw_normal_doubles_0_wins", + "losses": "SkyWars.tourney_sw_normal_doubles_0_losses", + "kills": "SkyWars.tourney_sw_normal_doubles_0_kills", + "deaths": "SkyWars.tourney_sw_normal_doubles_0_deaths", + "assists": "SkyWars.tourney_sw_normal_doubles_0_assists", + "winstreak": "SkyWars.tourney_sw_normal_doubles_0_win_streak", + "killstreak": "SkyWars.tourney_sw_normal_doubles_0_killstreak" } } ] diff --git a/target/maven-archiver/pom.properties b/target/maven-archiver/pom.properties index 2cceb68..3f162e5 100644 --- a/target/maven-archiver/pom.properties +++ b/target/maven-archiver/pom.properties @@ -1,5 +1,3 @@ -#Generated by Maven -#Wed Jun 28 17:50:57 EDT 2023 -groupId=me.stuffy artifactId=stuffybot-java +groupId=me.stuffy version=1.0-SNAPSHOT diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst index f310943..47d8e20 100644 --- a/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -1 +1,42 @@ +me\stuffy\stuffybot\profiles\Rank.class +me\stuffy\stuffybot\utils\MiscUtils.class +me\stuffy\stuffybot\commands\MaxesCommand.class +me\stuffy\stuffybot\events\UpdateBotStatsEvent.class +me\stuffy\stuffybot\commands\MegaWallsCommand.class +me\stuffy\stuffybot\commands\SetupCommand.class +me\stuffy\stuffybot\profiles\GlobalData.class +me\stuffy\stuffybot\utils\DiscordUtils.class +me\stuffy\stuffybot\profiles\MojangProfile.class +me\stuffy\stuffybot\events\BaseEvent.class +me\stuffy\stuffybot\profiles\Achievement.class +me\stuffy\stuffybot\utils\APIUtils$3.class +me\stuffy\stuffybot\utils\Config.class +me\stuffy\stuffybot\profiles\HypixelProfile.class +me\stuffy\stuffybot\interactions\InteractionHandler.class +me\stuffy\stuffybot\events\ActiveEvents.class +me\stuffy\stuffybot\commands\BlitzCommand.class +me\stuffy\stuffybot\commands\PlayCommandCommand.class +me\stuffy\stuffybot\utils\InvalidOptionException.class +me\stuffy\stuffybot\utils\APIUtils$2.class +me\stuffy\stuffybot\interactions\InteractionManager.class +me\stuffy\stuffybot\utils\Verification.class +me\stuffy\stuffybot\profiles\DiscordUser.class +me\stuffy\stuffybot\utils\APIUtils$1.class +me\stuffy\stuffybot\commands\PitCommand.class +me\stuffy\stuffybot\commands\TkrCommand.class +me\stuffy\stuffybot\utils\InteractionException.class +me\stuffy\stuffybot\commands\StatsCommand.class +me\stuffy\stuffybot\utils\APIUtils$5.class me\stuffy\stuffybot\Bot.class +me\stuffy\stuffybot\utils\APIException.class +me\stuffy\stuffybot\commands\HelpCommand.class +me\stuffy\stuffybot\commands\LinkCommand.class +me\stuffy\stuffybot\commands\SearchCommand.class +me\stuffy\stuffybot\commands\AchievementsCommand.class +me\stuffy\stuffybot\utils\APIUtils.class +me\stuffy\stuffybot\interactions\InteractionId.class +me\stuffy\stuffybot\utils\Logger.class +me\stuffy\stuffybot\profiles\Achievement$Type.class +me\stuffy\stuffybot\commands\TournamentCommand.class +me\stuffy\stuffybot\utils\StatisticsManager.class +me\stuffy\stuffybot\utils\APIUtils$4.class diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst index ff9febf..4e74a4c 100644 --- a/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -1,14 +1,36 @@ -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\events\AdminForumPost.java -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\events\GameResources.java -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\HypixelApiUtils.java -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\PingCommand.java -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\StuffyCommand.java -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\events\UpdateStatistics.java -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\events\HypixelStatusUpdate.java -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\events\BaseEvent.java -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\MojangApiUtils.java -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\VerifyCommand.java C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\Bot.java -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\events\StaffRankChanges.java -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\BaseCommand.java -C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\TimeUtils.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\AchievementsCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\BlitzCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\HelpCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\LinkCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\MaxesCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\MegaWallsCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\PitCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\PlayCommandCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\SearchCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\SetupCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\StatsCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\TkrCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\commands\TournamentCommand.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\events\ActiveEvents.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\events\BaseEvent.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\events\UpdateBotStatsEvent.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\interactions\InteractionHandler.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\interactions\InteractionId.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\interactions\InteractionManager.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\profiles\Achievement.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\profiles\DiscordUser.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\profiles\GlobalData.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\profiles\HypixelProfile.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\profiles\MojangProfile.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\profiles\Rank.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\APIException.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\APIUtils.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\Config.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\DiscordUtils.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\InteractionException.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\InvalidOptionException.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\Logger.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\MiscUtils.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\StatisticsManager.java +C:\Users\mattd\IdeaProjects\stuffybot-java\src\main\java\me\stuffy\stuffybot\utils\Verification.java