diff options
10 files changed, 73 insertions, 130 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c8a717..702e681 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [1.8.9-0.16.0] - unreleased +### Note on API keys ❗ +In case you haven't heard yet: player-based API keys are currently being phased out (see also forums post about [Public API Changes](https://hypixel.net/threads/public-api-changes-february-2023.5266129/)). + +Therefore, API-related features _that require an API key_ will probably stop working in the foreseeable future. I _don't_ currently plan to provide my own API backend system, so sooner or later the few features in Cowlection that require an API key will probably stop working. + +The affected features are the following: +1. `/moo stalk`: check online status & current game of a user ➡️I plan to move this to my Discord bot Moonitor (for that join the [Cowshed Discord server](https://discord.gg/fU2tFPf)) +2. `/moo stalkskyblock`: check SkyBlock stats of a player ➡️there are various bots/mods/websites that allow SkyBlock player lookups +3. `/moo dungeon party` (= `/moo dp`): lookup armor and dungeons stats of each Dungeons party member ➡️same as (2) + +All other features *do not* require an API key and thus should be unaffected by these changes. + ### Removed - Removed Cowlection 'Best friends' list: - Migrate your current Cowlection best friends with `/moo bestfriends` @@ -19,6 +31,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed - Dungeons overlay: now disabled by default (old config entries aren't modified) - SkyBlock player lookup: removed 'last played/last profile save' as it's no longer part of the API +- some Hypixel API related changes regarding API key validation ### Fixed - Pet exp in tooltips: fixed rare crash caused by unexpected NBT data typing diff --git a/src/main/java/de/cowtipper/cowlection/command/MooCommand.java b/src/main/java/de/cowtipper/cowlection/command/MooCommand.java index c1baff9..04c1eb1 100644 --- a/src/main/java/de/cowtipper/cowlection/command/MooCommand.java +++ b/src/main/java/de/cowtipper/cowlection/command/MooCommand.java @@ -202,7 +202,7 @@ public class MooCommand extends CommandBase { //region sub commands: Best friends, friends & other players private void handleStalking(String[] args) throws CommandException { if (!CredentialStorage.isMooValid) { - throw new MooCommandException("You haven't set your Hypixel API key yet or the API key is invalid. Use " + EnumChatFormatting.DARK_RED + "/" + this.getCommandName() + " apikey <key>" + EnumChatFormatting.RED + " to manually set your existing API key."); + throw new MooCommandException("[Cowlection] You haven't set your Hypixel API key yet or the API key is invalid. Use " + EnumChatFormatting.DARK_RED + "/" + this.getCommandName() + " apikey <key>" + EnumChatFormatting.RED + " to manually set your existing API key."); } if (args.length != 2) { throw new WrongUsageException("/" + getCommandName() + " stalk <playerName>"); @@ -296,7 +296,7 @@ public class MooCommand extends CommandBase { //region sub commands: SkyBlock private void handleStalkingSkyBlock(String[] args) throws CommandException { if (!CredentialStorage.isMooValid) { - throw new MooCommandException("You haven't set your Hypixel API key yet or the API key is invalid. Use " + EnumChatFormatting.DARK_RED + "/" + this.getCommandName() + " apikey <key>" + EnumChatFormatting.RED + " to manually set your existing API key."); + throw new MooCommandException("[Cowlection] You haven't set your Hypixel API key yet or the API key is invalid. Use " + EnumChatFormatting.DARK_RED + "/" + this.getCommandName() + " apikey <key>" + EnumChatFormatting.RED + " to manually set your existing API key."); } if (args.length != 2) { throw new WrongUsageException("/" + getCommandName() + " skyblockstalk <playerName>"); @@ -827,7 +827,7 @@ public class MooCommand extends CommandBase { } else if ((args.length == 2 && (args[1].equalsIgnoreCase("party") || args[1].equalsIgnoreCase("p"))) || args.length == 1 && args[0].equalsIgnoreCase("dp")) { if (!CredentialStorage.isMooValid) { - throw new MooCommandException("You haven't set your Hypixel API key yet or the API key is invalid. Use " + EnumChatFormatting.DARK_RED + "/" + this.getCommandName() + " apikey <key>" + EnumChatFormatting.RED + " to manually set your existing API key."); + throw new MooCommandException("[Cowlection] You haven't set your Hypixel API key yet or the API key is invalid. Use " + EnumChatFormatting.DARK_RED + "/" + this.getCommandName() + " apikey <key>" + EnumChatFormatting.RED + " to manually set your existing API key."); } else if (dungeonsPartyListener != null) { throw new MooCommandException("Please wait a few seconds before using this command again."); } @@ -876,37 +876,22 @@ public class MooCommand extends CommandBase { EnumChatFormatting color; EnumChatFormatting colorSecondary; if (CredentialStorage.isMooValid && StringUtils.isNotEmpty(CredentialStorage.moo)) { - firstSentence = EnumChatFormatting.GREEN + "[" + Cowlection.MODNAME + "] You already set your Hypixel API key. Requesting API usage statistics..."; + firstSentence = EnumChatFormatting.GREEN + "[Cowlection] You already set your Hypixel API key."; color = EnumChatFormatting.GRAY; colorSecondary = EnumChatFormatting.YELLOW; - - ApiUtils.fetchApiKeyInfo(CredentialStorage.moo, hyApiKey -> { - if (hyApiKey != null && hyApiKey.isSuccess()) { - HyApiKey.Record apiKeyRecord = hyApiKey.getRecord(); - if (apiKeyRecord != null) { - main.getChatHelper().sendMessage(EnumChatFormatting.GREEN, "[" + Cowlection.MODNAME + "] Your Hypixel API key was used to execute a total of " + EnumChatFormatting.DARK_GREEN + Utils.formatNumber(apiKeyRecord.getTotalQueries()) - + EnumChatFormatting.GREEN + " API requests. In the last minute, " + EnumChatFormatting.DARK_GREEN + apiKeyRecord.getQueriesInPastMin() + EnumChatFormatting.GREEN + " out of maximum " + EnumChatFormatting.DARK_GREEN + apiKeyRecord.getLimit() + EnumChatFormatting.GREEN + " allowed requests were made."); - } else { - main.getChatHelper().sendMessage(EnumChatFormatting.GREEN, "[" + Cowlection.MODNAME + "] Your Hypixel API key seems to be valid, but processing usage statistics failed."); - } - } else { - String cause = hyApiKey != null ? hyApiKey.getCause() : null; - Cowlection.getInstance().getChatHelper().sendMessage(EnumChatFormatting.RED, "[" + Cowlection.MODNAME + "] Failed to check API key usage statistics: " + (cause != null ? cause : "unknown cause :c")); - } - }); } else { - firstSentence = "[" + Cowlection.MODNAME + "] You haven't set your Hypixel API key yet or the API key is invalid."; + firstSentence = "[Cowlection] You haven't set your Hypixel API key yet or the API key is invalid."; color = EnumChatFormatting.RED; colorSecondary = EnumChatFormatting.DARK_RED; } - main.getChatHelper().sendMessage(color, firstSentence + color + " Use " + colorSecondary + "/" + this.getCommandName() + " apikey <key>" + color + " to manually set your existing API key."); + main.getChatHelper().sendMessage(color, firstSentence + color + " Use " + colorSecondary + "/" + this.getCommandName() + " apikey <key>" + color + " to manually set your API key."); } else { String key = args[1]; if (Utils.isValidUuid(key)) { - main.getChatHelper().sendMessage(EnumChatFormatting.YELLOW, "[" + Cowlection.MODNAME + "] Validating API key..."); - main.getMoo().setMooIfValid(key, true); + main.getChatHelper().sendMessage(EnumChatFormatting.GREEN, "[Cowlection] Saved new API key."); + main.getMoo().setMoo(key); } else { - throw new SyntaxErrorException("[" + Cowlection.MODNAME + "] That doesn't look like a valid API key..."); + throw new SyntaxErrorException("[Cowlection] That doesn't look like a valid API key..."); } } } diff --git a/src/main/java/de/cowtipper/cowlection/config/CredentialStorage.java b/src/main/java/de/cowtipper/cowlection/config/CredentialStorage.java index 5b1b0b1..309e76e 100644 --- a/src/main/java/de/cowtipper/cowlection/config/CredentialStorage.java +++ b/src/main/java/de/cowtipper/cowlection/config/CredentialStorage.java @@ -1,9 +1,7 @@ package de.cowtipper.cowlection.config; import de.cowtipper.cowlection.Cowlection; -import de.cowtipper.cowlection.util.ApiUtils; import de.cowtipper.cowlection.util.Utils; -import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.MathHelper; import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Property; @@ -131,23 +129,7 @@ public class CredentialStorage { return false; } - public void setMooIfValid(String moo, boolean commandTriggered) { - ApiUtils.fetchApiKeyInfo(moo, hyApiKey -> { - if (hyApiKey != null && hyApiKey.isSuccess()) { - // api key is valid! - Cowlection.getInstance().getMoo().setMoo(moo); - if (commandTriggered) { - Cowlection.getInstance().getChatHelper().sendMessage(EnumChatFormatting.GREEN, "[" + Cowlection.MODNAME + "] Successfully verified API key ✔"); - } - } else if (commandTriggered) { - // api key is invalid - String cause = hyApiKey != null ? hyApiKey.getCause() : null; - Cowlection.getInstance().getChatHelper().sendMessage(EnumChatFormatting.RED, "[" + Cowlection.MODNAME + "] Failed to verify API key: " + (cause != null ? cause : "unknown cause :c")); - } - }); - } - - private void setMoo(String moo) { + public void setMoo(String moo) { CredentialStorage.moo = moo; propMoo.set(moo); setMooValidity(true); diff --git a/src/main/java/de/cowtipper/cowlection/config/MooConfig.java b/src/main/java/de/cowtipper/cowlection/config/MooConfig.java index 9a9ba4b..38b7396 100644 --- a/src/main/java/de/cowtipper/cowlection/config/MooConfig.java +++ b/src/main/java/de/cowtipper/cowlection/config/MooConfig.java @@ -162,7 +162,7 @@ public class MooConfig { String oldMoo = cfg.getString("moo", Configuration.CATEGORY_CLIENT, "00000000-0000-0000-0000-000000000000", "Temporary config entry, should be deleted automatically.", Utils.VALID_UUID_PATTERN); if (StringUtils.isNotEmpty(oldMoo) && Utils.isValidUuid(oldMoo)) { // save into new cfg: - main.getMoo().setMooIfValid(oldMoo, false); + main.getMoo().setMoo(oldMoo); } } @@ -333,7 +333,7 @@ public class MooConfig { // Sub-Category: API settings subCat = configCat.addSubCategory("API settings"); subCat.addExplanations("Some features use the official Hypixel API and therefore require your API key.", - "Use " + EnumChatFormatting.YELLOW + "/moo apikey " + EnumChatFormatting.RESET + "to see how to request a new API key from Hypixel", + "Use " + EnumChatFormatting.YELLOW + "/moo apikey " + EnumChatFormatting.RESET + "to set your Hypixel API key", "The API key is stored " + EnumChatFormatting.ITALIC + "locally " + EnumChatFormatting.ITALIC + "on your computer."); subCat.addConfigEntry(main.getMoo().getPropIsMooValid()); diff --git a/src/main/java/de/cowtipper/cowlection/data/HyApiKey.java b/src/main/java/de/cowtipper/cowlection/data/HyApiKey.java deleted file mode 100644 index 9552948..0000000 --- a/src/main/java/de/cowtipper/cowlection/data/HyApiKey.java +++ /dev/null @@ -1,45 +0,0 @@ -package de.cowtipper.cowlection.data; - -@SuppressWarnings("unused") -public class HyApiKey { - private boolean success; - private String cause; - - private Record record; - - /** - * No-args constructor for GSON - */ - private HyApiKey() { - } - - public boolean isSuccess() { - return success; - } - - public String getCause() { - return cause; - } - - public Record getRecord() { - return record; - } - - public class Record { - private int queriesInPastMin; - private int limit; - private long totalQueries; - - public int getQueriesInPastMin() { - return queriesInPastMin; - } - - public int getLimit() { - return limit; - } - - public long getTotalQueries() { - return totalQueries; - } - } -} diff --git a/src/main/java/de/cowtipper/cowlection/data/HyStalkingData.java b/src/main/java/de/cowtipper/cowlection/data/HyStalkingData.java index 5233917..bdac08f 100644 --- a/src/main/java/de/cowtipper/cowlection/data/HyStalkingData.java +++ b/src/main/java/de/cowtipper/cowlection/data/HyStalkingData.java @@ -48,7 +48,7 @@ public class HyStalkingData { } public String getMode() { - // modes partially taken from https://api.hypixel.net/gameCounts?key=MOO + // modes partially taken from https://api.hypixel.net/gameCounts if (mode == null) { return null; } diff --git a/src/main/java/de/cowtipper/cowlection/error/ApiHttpErrorEvent.java b/src/main/java/de/cowtipper/cowlection/error/ApiHttpErrorEvent.java index 4b66bb6..757a2bf 100644 --- a/src/main/java/de/cowtipper/cowlection/error/ApiHttpErrorEvent.java +++ b/src/main/java/de/cowtipper/cowlection/error/ApiHttpErrorEvent.java @@ -5,10 +5,12 @@ import net.minecraftforge.fml.common.eventhandler.Event; public class ApiHttpErrorEvent extends Event { private final String message; private final String url; + private final boolean wasUsingApiKey; - public ApiHttpErrorEvent(String message, String url) { + public ApiHttpErrorEvent(String message, String url, boolean wasUsingApiKey) { this.message = message; this.url = url; + this.wasUsingApiKey = wasUsingApiKey; } public String getMessage() { @@ -24,7 +26,7 @@ public class ApiHttpErrorEvent extends Event { return queryParamStart > 0 ? url.substring(0, queryParamStart) : url; } - public boolean hasUrlKey() { - return url.contains("key="); + public boolean wasUsingApiKey() { + return wasUsingApiKey; } } diff --git a/src/main/java/de/cowtipper/cowlection/error/ApiHttpErrorException.java b/src/main/java/de/cowtipper/cowlection/error/ApiHttpErrorException.java index ac0a6a4..b222827 100644 --- a/src/main/java/de/cowtipper/cowlection/error/ApiHttpErrorException.java +++ b/src/main/java/de/cowtipper/cowlection/error/ApiHttpErrorException.java @@ -4,13 +4,23 @@ import java.io.IOException; public class ApiHttpErrorException extends IOException { private final String url; + private final boolean hasApiKey; public ApiHttpErrorException(String message, String url) { + this(message, url, false); + } + + public ApiHttpErrorException(String message, String url, boolean hasApiKey) { super(message); this.url = url; + this.hasApiKey = hasApiKey; } public String getUrl() { return url; } + + public boolean wasUsingApiKey() { + return hasApiKey; + } } diff --git a/src/main/java/de/cowtipper/cowlection/listener/PlayerListener.java b/src/main/java/de/cowtipper/cowlection/listener/PlayerListener.java index ad47288..4478f1b 100644 --- a/src/main/java/de/cowtipper/cowlection/listener/PlayerListener.java +++ b/src/main/java/de/cowtipper/cowlection/listener/PlayerListener.java @@ -125,10 +125,9 @@ public class PlayerListener { if (nextApiErrorMessage < System.currentTimeMillis() && Minecraft.getMinecraft().thePlayer != null) { this.nextApiErrorMessage = System.currentTimeMillis() + 3000; MooChatComponent hoverComponent = new MooChatComponent.KeyValueTooltipComponent("Click to visit", e.getBaseUrl()); - if (e.hasUrlKey()) { + if (e.wasUsingApiKey()) { String eyeCatcher = "" + EnumChatFormatting.LIGHT_PURPLE + EnumChatFormatting.OBFUSCATED + "#" + EnumChatFormatting.RESET + EnumChatFormatting.RED; - hoverComponent.appendFreshSibling(new MooChatComponent(eyeCatcher + " Warning! " + eyeCatcher + " If you're streaming or sharing your screen:").red() - .appendFreshSibling(new MooChatComponent("Clicking this will reveal your Hypixel " + EnumChatFormatting.DARK_RED + "API key" + EnumChatFormatting.RED + "!").red())); + hoverComponent.appendFreshSibling(new MooChatComponent(eyeCatcher + " Request was using your API-Key.").red()); } main.getChatHelper().sendMessage(new MooChatComponent(e.getMessage()).red() .setUrl(e.getUrl(), hoverComponent)); diff --git a/src/main/java/de/cowtipper/cowlection/util/ApiUtils.java b/src/main/java/de/cowtipper/cowlection/util/ApiUtils.java index d820c83..b8fa4a5 100644 --- a/src/main/java/de/cowtipper/cowlection/util/ApiUtils.java +++ b/src/main/java/de/cowtipper/cowlection/util/ApiUtils.java @@ -8,7 +8,10 @@ import de.cowtipper.cowlection.chesttracker.data.HyItemsData; import de.cowtipper.cowlection.chesttracker.data.LowestBinsCache; import de.cowtipper.cowlection.command.exception.ThrowingConsumer; import de.cowtipper.cowlection.config.CredentialStorage; -import de.cowtipper.cowlection.data.*; +import de.cowtipper.cowlection.data.Friend; +import de.cowtipper.cowlection.data.HyPlayerData; +import de.cowtipper.cowlection.data.HySkyBlockStats; +import de.cowtipper.cowlection.data.HyStalkingData; import de.cowtipper.cowlection.error.ApiHttpErrorEvent; import de.cowtipper.cowlection.error.ApiHttpErrorException; import net.minecraft.util.EnumChatFormatting; @@ -30,13 +33,12 @@ import java.util.concurrent.Executors; public class ApiUtils { private static final String NAME_TO_UUID_URL = "https://api.mojang.com/users/profiles/minecraft/"; - private static final String ONLINE_STATUS_URL = "https://api.hypixel.net/status?key=%s&uuid=%s"; - private static final String SKYBLOCK_STATS_URL = "https://api.hypixel.net/skyblock/profiles?key=%s&uuid=%s"; + private static final String ONLINE_STATUS_URL = "https://api.hypixel.net/status?uuid=%s"; + private static final String SKYBLOCK_STATS_URL = "https://api.hypixel.net/skyblock/profiles?uuid=%s"; private static final String BAZAAR_URL = "https://api.hypixel.net/skyblock/bazaar"; public static final String LOWEST_BINS = "https://moulberry.codes/lowestbin.json"; private static final String ITEMS_URL = "https://api.hypixel.net/resources/skyblock/items"; - private static final String PLAYER_URL = "https://api.hypixel.net/player?key=%s&uuid=%s"; - private static final String API_KEY_URL = "https://api.hypixel.net/key?key=%s"; + private static final String PLAYER_URL = "https://api.hypixel.net/player?uuid=%s"; private static final ExecutorService pool = Executors.newCachedThreadPool(); private ApiUtils() { @@ -47,7 +49,7 @@ public class ApiUtils { } private static Friend getFriend(String name) { - try (BufferedReader reader = makeApiCall(NAME_TO_UUID_URL + name)) { + try (BufferedReader reader = makeApiCall(NAME_TO_UUID_URL + name, false)) { if (reader == null) { return Friend.FRIEND_NOT_FOUND; } else { @@ -64,7 +66,7 @@ public class ApiUtils { } private static HyStalkingData stalkPlayer(Friend friend) { - try (BufferedReader reader = makeApiCall(String.format(ONLINE_STATUS_URL, CredentialStorage.moo, UUIDTypeAdapter.fromUUID(friend.getUuid())))) { + try (BufferedReader reader = makeApiCall(String.format(ONLINE_STATUS_URL, UUIDTypeAdapter.fromUUID(friend.getUuid())), true)) { if (reader != null) { return GsonUtils.fromJson(reader, HyStalkingData.class); } @@ -79,7 +81,7 @@ public class ApiUtils { } private static HySkyBlockStats stalkSkyBlockStats(Friend friend) { - try (BufferedReader reader = makeApiCall(String.format(SKYBLOCK_STATS_URL, CredentialStorage.moo, UUIDTypeAdapter.fromUUID(friend.getUuid())))) { + try (BufferedReader reader = makeApiCall(String.format(SKYBLOCK_STATS_URL, UUIDTypeAdapter.fromUUID(friend.getUuid())), true)) { if (reader != null) { return GsonUtils.fromJson(reader, HySkyBlockStats.class); } @@ -94,7 +96,7 @@ public class ApiUtils { } private static HyBazaarData getBazaarData() { - try (BufferedReader reader = makeApiCall(BAZAAR_URL)) { + try (BufferedReader reader = makeApiCall(BAZAAR_URL, false)) { if (reader != null) { return GsonUtils.fromJson(reader, HyBazaarData.class); } @@ -109,7 +111,7 @@ public class ApiUtils { } private static LowestBinsCache getLowestBins() { - try (BufferedReader reader = makeApiCall(LOWEST_BINS)) { + try (BufferedReader reader = makeApiCall(LOWEST_BINS, false)) { if (reader != null) { return GsonUtils.fromJson(reader, LowestBinsCache.class); } @@ -124,7 +126,7 @@ public class ApiUtils { } private static HyItemsData getItemsData() { - try (BufferedReader reader = makeApiCall(ITEMS_URL)) { + try (BufferedReader reader = makeApiCall(ITEMS_URL, false)) { if (reader != null) { return GsonUtils.fromJson(reader, HyItemsData.class); } @@ -139,7 +141,7 @@ public class ApiUtils { } private static HyPlayerData stalkHyPlayer(Friend stalkedPlayer) { - try (BufferedReader reader = makeApiCall(String.format(PLAYER_URL, CredentialStorage.moo, UUIDTypeAdapter.fromUUID(stalkedPlayer.getUuid())))) { + try (BufferedReader reader = makeApiCall(String.format(PLAYER_URL, UUIDTypeAdapter.fromUUID(stalkedPlayer.getUuid())), true)) { if (reader != null) { return GsonUtils.fromJson(reader, HyPlayerData.class); } @@ -149,34 +151,29 @@ public class ApiUtils { return null; } - public static void fetchApiKeyInfo(String moo, ThrowingConsumer<HyApiKey> action) { - pool.execute(() -> action.accept(getApiKeyInfo(moo))); - } - - private static HyApiKey getApiKeyInfo(String moo) { - try (BufferedReader reader = makeApiCall(String.format(API_KEY_URL, moo))) { - if (reader != null) { - return GsonUtils.fromJson(reader, HyApiKey.class); - } - } catch (IOException | JsonSyntaxException e) { - handleApiException(e); - } - return null; - } - private static void handleApiException(Exception e) { e.printStackTrace(); if (e instanceof ApiHttpErrorException) { - MinecraftForge.EVENT_BUS.post(new ApiHttpErrorEvent(e.getMessage(), ((ApiHttpErrorException) e).getUrl())); + MinecraftForge.EVENT_BUS.post(new ApiHttpErrorEvent(e.getMessage(), ((ApiHttpErrorException) e).getUrl(), ((ApiHttpErrorException) e).wasUsingApiKey())); } } - private static BufferedReader makeApiCall(String url) throws IOException { + private static BufferedReader makeApiCall(String url, boolean sendApiKey) throws IOException { + return makeApiCall(url, sendApiKey, CredentialStorage.moo); + } + + private static BufferedReader makeApiCall(String url, boolean sendApiKey, String key) throws IOException { + if (sendApiKey && !CredentialStorage.isMooValid) { + throw new ApiHttpErrorException("Your current Hypixel API key is invalid. Use /moo apikey to manually set your existing API key.", url); + } try { HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); if (CredentialStorage.sslContext != null && connection instanceof HttpsURLConnection) { ((HttpsURLConnection) connection).setSSLSocketFactory(CredentialStorage.sslContext.getSocketFactory()); } + if (sendApiKey) { + connection.addRequestProperty("API-Key", key); + } connection.setConnectTimeout(5000); connection.setReadTimeout(8000); connection.addRequestProperty("User-Agent", "Forge Mod " + Cowlection.MODNAME + "/" + Cowlection.VERSION + " (" + Cowlection.GITURL + ")"); @@ -186,10 +183,11 @@ public class ApiUtils { return null; } else if (connection.getResponseCode() == HttpStatus.SC_BAD_GATEWAY && url.startsWith("https://api.hypixel.net/")) { // http status 502 (cloudflare) throw new ApiHttpErrorException("Couldn't contact Hypixel API (502 Bad Gateway). API might be down, check https://status.hypixel.net for info.", "https://status.hypixel.net"); + } else if (connection.getResponseCode() == HttpStatus.SC_FORBIDDEN && sendApiKey && url.startsWith("https://api.hypixel.net/")) { // http status 403 Forbidden + Cowlection.getInstance().getMoo().setMooValidity(false); + throw new ApiHttpErrorException("Your current Hypixel API key seems to be invalid. Use /moo apikey to manually set your existing API key.", url); } else if (connection.getResponseCode() == HttpStatus.SC_SERVICE_UNAVAILABLE) { // http status 503 Service Unavailable - int queryParamStart = url.indexOf('?', 10); - String baseUrl = queryParamStart > 0 ? url.substring(0, queryParamStart) : url; - throw new ApiHttpErrorException("Couldn't contact the API (503 Service unavailable). API might be down, or you might be blocked by Cloudflare, check if you can reach: " + baseUrl, url); + throw new ApiHttpErrorException("Couldn't contact the API (503 Service unavailable). API might be down, or you might be blocked by Cloudflare, check if you can reach: " + url, url, sendApiKey); } else if (connection.getResponseCode() == HttpStatus.SC_BAD_GATEWAY && url.startsWith("https://moulberry.codes/")) { // http status 502 (cloudflare) throw new ApiHttpErrorException("Couldn't contact Moulberry's API (502 Bad Gateway). API might be down, check if " + LOWEST_BINS + " is reachable.", LOWEST_BINS); } else { @@ -213,5 +211,4 @@ public class ApiUtils { } } } - } |