From 7c9f889efd9c5d889394f8bf00288ef1c38f8ae4 Mon Sep 17 00:00:00 2001 From: CalMWolfs <94038482+CalMWolfs@users.noreply.github.com> Date: Tue, 24 Sep 2024 00:11:18 +1000 Subject: Backend: Add Http Request Patching (#2578) --- .../hannibal2/skyhanni/config/commands/Commands.kt | 4 +- .../java/at/hannibal2/skyhanni/data/MayorAPI.kt | 4 +- .../skyhanni/data/bazaar/HypixelBazaarFetcher.kt | 4 +- .../features/chat/translation/Translator.kt | 4 +- .../features/garden/GardenNextJacobContest.kt | 6 +- .../garden/farming/FarmingWeightDisplay.kt | 8 +- .../features/inventory/bazaar/BazaarDataHolder.kt | 4 +- .../mining/eventtracker/MiningEventTracker.kt | 6 +- .../skyhanni/features/misc/update/UpdateManager.kt | 4 +- .../java/at/hannibal2/skyhanni/utils/APIUtil.kt | 193 ------------------- .../java/at/hannibal2/skyhanni/utils/APIUtils.kt | 213 +++++++++++++++++++++ 11 files changed, 235 insertions(+), 215 deletions(-) delete mode 100644 src/main/java/at/hannibal2/skyhanni/utils/APIUtil.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/APIUtils.kt (limited to 'src/main/java/at/hannibal2/skyhanni') diff --git a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt index cf4679e39..a52852a81 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt @@ -94,7 +94,7 @@ import at.hannibal2.skyhanni.test.command.TestChatCommand import at.hannibal2.skyhanni.test.command.TrackParticlesCommand import at.hannibal2.skyhanni.test.command.TrackSoundsCommand import at.hannibal2.skyhanni.test.graph.GraphEditor -import at.hannibal2.skyhanni.utils.APIUtil +import at.hannibal2.skyhanni.utils.APIUtils import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.ExtendedChatColor import at.hannibal2.skyhanni.utils.ItemPriceUtils @@ -382,7 +382,7 @@ object Commands { registerCommand( "shtogglehypixelapierrors", "Show/hide hypixel api error messages in chat", - ) { APIUtil.toggleApiErrorMessages() } + ) { APIUtils.toggleApiErrorMessages() } registerCommand( "shclearcropspeed", "Reset garden crop speed data and best crop time data", diff --git a/src/main/java/at/hannibal2/skyhanni/data/MayorAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/MayorAPI.kt index b4fb7eaa2..469c48489 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/MayorAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/MayorAPI.kt @@ -16,7 +16,7 @@ import at.hannibal2.skyhanni.events.LorenzChatEvent import at.hannibal2.skyhanni.events.SecondPassedEvent import at.hannibal2.skyhanni.features.fame.ReminderUtils import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule -import at.hannibal2.skyhanni.utils.APIUtil +import at.hannibal2.skyhanni.utils.APIUtils import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.CollectionUtils.nextAfter import at.hannibal2.skyhanni.utils.CollectionUtils.put @@ -216,7 +216,7 @@ object MayorAPI { SkyHanniMod.coroutineScope.launch { val url = "https://api.hypixel.net/v2/resources/skyblock/election" - val jsonObject = withContext(dispatcher) { APIUtil.getJSONResponse(url) } + val jsonObject = withContext(dispatcher) { APIUtils.getJSONResponse(url) } rawMayorData = ConfigManager.gson.fromJson(jsonObject) val data = rawMayorData ?: return@launch val map = mutableMapOf() diff --git a/src/main/java/at/hannibal2/skyhanni/data/bazaar/HypixelBazaarFetcher.kt b/src/main/java/at/hannibal2/skyhanni/data/bazaar/HypixelBazaarFetcher.kt index 29a16944c..13044b7c3 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/bazaar/HypixelBazaarFetcher.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/bazaar/HypixelBazaarFetcher.kt @@ -6,7 +6,7 @@ import at.hannibal2.skyhanni.events.LorenzTickEvent import at.hannibal2.skyhanni.features.inventory.bazaar.BazaarData import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.test.command.ErrorManager -import at.hannibal2.skyhanni.utils.APIUtil +import at.hannibal2.skyhanni.utils.APIUtils import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.ItemUtils.itemName import at.hannibal2.skyhanni.utils.LorenzUtils @@ -46,7 +46,7 @@ object HypixelBazaarFetcher { val fetchType = if (nextFetchIsManual) "manual" else "automatic" nextFetchIsManual = false try { - val jsonResponse = withContext(Dispatchers.IO) { APIUtil.getJSONResponse(URL) }.asJsonObject + val jsonResponse = withContext(Dispatchers.IO) { APIUtils.getJSONResponse(URL) }.asJsonObject val response = ConfigManager.gson.fromJson(jsonResponse) if (response.success) { latestProductInformation = process(response.products) diff --git a/src/main/java/at/hannibal2/skyhanni/features/chat/translation/Translator.kt b/src/main/java/at/hannibal2/skyhanni/features/chat/translation/Translator.kt index 389b57fb0..24809faac 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/chat/translation/Translator.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/chat/translation/Translator.kt @@ -6,7 +6,7 @@ import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator import at.hannibal2.skyhanni.events.ConfigLoadEvent import at.hannibal2.skyhanni.events.LorenzChatEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule -import at.hannibal2.skyhanni.utils.APIUtil +import at.hannibal2.skyhanni.utils.APIUtils import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.ConditionalUtils.onToggle import at.hannibal2.skyhanni.utils.ConditionalUtils.transformIf @@ -112,7 +112,7 @@ object Translator { */ private fun getJSONResponse(urlString: String) = - APIUtil.getJSONResponseAsElement(urlString, false, "Google Translate API") + APIUtils.getJSONResponseAsElement(urlString, false, "Google Translate API") private fun getTranslationToEnglish(message: String): String { val url = diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenNextJacobContest.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenNextJacobContest.kt index ab8820b40..4285a24af 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenNextJacobContest.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenNextJacobContest.kt @@ -15,7 +15,7 @@ import at.hannibal2.skyhanni.events.TabListUpdateEvent import at.hannibal2.skyhanni.features.garden.GardenAPI.addCropIcon import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.test.command.ErrorManager -import at.hannibal2.skyhanni.utils.APIUtil +import at.hannibal2.skyhanni.utils.APIUtils import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.ConfigUtils import at.hannibal2.skyhanni.utils.HypixelCommands @@ -543,7 +543,7 @@ object GardenNextJacobContest { suspend fun fetchUpcomingContests() { try { val url = "https://api.elitebot.dev/contests/at/now" - val result = withContext(dispatcher) { APIUtil.getJSONResponse(url) }.asJsonObject + val result = withContext(dispatcher) { APIUtils.getJSONResponse(url) }.asJsonObject val newContests = mutableMapOf() @@ -613,7 +613,7 @@ object GardenNextJacobContest { val url = "https://api.elitebot.dev/contests/at/now" val body = Gson().toJson(formatted) - val result = withContext(dispatcher) { APIUtil.postJSONIsSuccessful(url, body) } + val result = withContext(dispatcher) { APIUtils.postJSONIsSuccessful(url, body) } if (result) { ChatUtils.chat("Successfully submitted this years upcoming contests, thank you for helping everyone out!") diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/FarmingWeightDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/FarmingWeightDisplay.kt index 222e75f4f..1fa4b57e6 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/FarmingWeightDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/FarmingWeightDisplay.kt @@ -20,7 +20,7 @@ import at.hannibal2.skyhanni.features.garden.farming.GardenCropSpeed.getSpeed import at.hannibal2.skyhanni.features.garden.pests.PestType import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.test.command.ErrorManager -import at.hannibal2.skyhanni.utils.APIUtil +import at.hannibal2.skyhanni.utils.APIUtils import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzUtils.round @@ -447,7 +447,7 @@ object FarmingWeightDisplay { val atRank = if (isEtaEnabled() && goalRank != 10001) "&atRank=$goalRank" else "" val url = "https://api.elitebot.dev/leaderboard/rank/farmingweight/$uuid/$profileId$includeUpcoming$atRank" - val apiResponse = APIUtil.getJSONResponse(url) + val apiResponse = APIUtils.getJSONResponse(url) try { val apiData = toEliteLeaderboardJson(apiResponse).data @@ -477,7 +477,7 @@ object FarmingWeightDisplay { private fun loadWeight(localProfile: String) { val uuid = LorenzUtils.getPlayerUuid() val url = "https://api.elitebot.dev/weight/$uuid" - val apiResponse = APIUtil.getJSONResponse(url) + val apiResponse = APIUtils.getJSONResponse(url) var error: Throwable? = null @@ -573,7 +573,7 @@ object FarmingWeightDisplay { if (attemptingCropWeightFetch || hasFetchedCropWeights) return attemptingCropWeightFetch = true val url = "https://api.elitebot.dev/weights/all" - val apiResponse = APIUtil.getJSONResponse(url) + val apiResponse = APIUtils.getJSONResponse(url) try { val apiData = eliteWeightApiGson.fromJson(apiResponse) diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarDataHolder.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarDataHolder.kt index 3d7867ad9..cf9cb6d2e 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarDataHolder.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarDataHolder.kt @@ -5,7 +5,7 @@ import at.hannibal2.skyhanni.config.ConfigManager import at.hannibal2.skyhanni.data.jsonobjects.other.SkyblockItemsDataJson import at.hannibal2.skyhanni.features.rift.RiftAPI import at.hannibal2.skyhanni.test.command.ErrorManager -import at.hannibal2.skyhanni.utils.APIUtil +import at.hannibal2.skyhanni.utils.APIUtils import at.hannibal2.skyhanni.utils.NEUInternalName import at.hannibal2.skyhanni.utils.NEUItems import at.hannibal2.skyhanni.utils.json.fromJson @@ -22,7 +22,7 @@ class BazaarDataHolder { private fun loadNpcPrices(): MutableMap { val list = mutableMapOf() - val apiResponse = APIUtil.getJSONResponse("https://api.hypixel.net/v2/resources/skyblock/items") + val apiResponse = APIUtils.getJSONResponse("https://api.hypixel.net/v2/resources/skyblock/items") try { val itemsData = ConfigManager.gson.fromJson(apiResponse) diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventTracker.kt index 97806b6fb..a076ba68f 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventTracker.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventTracker.kt @@ -13,7 +13,7 @@ import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent import at.hannibal2.skyhanni.events.SecondPassedEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.test.command.ErrorManager -import at.hannibal2.skyhanni.utils.APIUtil +import at.hannibal2.skyhanni.utils.APIUtils import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland @@ -160,7 +160,7 @@ object MiningEventTracker { private fun sendData(json: String) { val response = try { - APIUtil.postJSON("https://api.soopy.dev/skyblock/chevents/set", json) + APIUtils.postJSON("https://api.soopy.dev/skyblock/chevents/set", json) } catch (e: IOException) { if (LorenzUtils.debug) { ErrorManager.logErrorWithData( @@ -194,7 +194,7 @@ object MiningEventTracker { canRequestAt = SimpleTimeMark.now() + defaultCooldown SkyHanniMod.coroutineScope.launch { val data = try { - APIUtil.getJSONResponse("https://api.soopy.dev/skyblock/chevents/get") + APIUtils.getJSONResponse("https://api.soopy.dev/skyblock/chevents/get") } catch (e: Exception) { apiErrorCount++ canRequestAt = SimpleTimeMark.now() + 20.minutes diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/update/UpdateManager.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/update/UpdateManager.kt index 040b6741a..9948d5eeb 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/update/UpdateManager.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/update/UpdateManager.kt @@ -5,12 +5,12 @@ import at.hannibal2.skyhanni.config.features.About.UpdateStream import at.hannibal2.skyhanni.events.ConfigLoadEvent import at.hannibal2.skyhanni.events.LorenzTickEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.APIUtils import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.ConditionalUtils.onToggle import at.hannibal2.skyhanni.utils.DelayedRun import at.hannibal2.skyhanni.utils.LorenzLogger import com.google.gson.JsonElement -import io.github.moulberry.notenoughupdates.util.ApiUtil import io.github.notenoughupdates.moulconfig.observer.Property import io.github.notenoughupdates.moulconfig.processor.MoulConfigProcessor import moe.nea.libautoupdate.CurrentVersion @@ -161,7 +161,7 @@ object UpdateManager { context.cleanup() UpdateUtils.patchConnection { if (it is HttpsURLConnection) { - ApiUtil.patchHttpsRequest(it) + APIUtils.patchHttpsRequest(it) } } } diff --git a/src/main/java/at/hannibal2/skyhanni/utils/APIUtil.kt b/src/main/java/at/hannibal2/skyhanni/utils/APIUtil.kt deleted file mode 100644 index 483bb6d04..000000000 --- a/src/main/java/at/hannibal2/skyhanni/utils/APIUtil.kt +++ /dev/null @@ -1,193 +0,0 @@ -package at.hannibal2.skyhanni.utils - -import at.hannibal2.skyhanni.SkyHanniMod -import at.hannibal2.skyhanni.test.command.ErrorManager -import com.google.gson.JsonElement -import com.google.gson.JsonObject -import com.google.gson.JsonParser -import com.google.gson.JsonSyntaxException -import org.apache.http.HttpEntity -import org.apache.http.client.config.RequestConfig -import org.apache.http.client.methods.HttpGet -import org.apache.http.client.methods.HttpPost -import org.apache.http.entity.ContentType -import org.apache.http.entity.StringEntity -import org.apache.http.impl.client.HttpClientBuilder -import org.apache.http.impl.client.HttpClients -import org.apache.http.message.BasicHeader -import org.apache.http.util.EntityUtils -import java.io.BufferedReader -import java.io.File -import java.io.FileInputStream -import java.io.InputStreamReader -import java.nio.charset.StandardCharsets - -object APIUtil { - - private val parser = JsonParser() - private var showApiErrors = false - - data class ApiResponse(val success: Boolean, val message: String?, val data: JsonObject) - - private val builder: HttpClientBuilder = - HttpClients.custom().setUserAgent("SkyHanni/${SkyHanniMod.version}") - .setDefaultHeaders( - mutableListOf( - BasicHeader("Pragma", "no-cache"), - BasicHeader("Cache-Control", "no-cache") - ) - ) - .setDefaultRequestConfig( - RequestConfig.custom() - .build() - ) - .useSystemProperties() - - /** - * TODO - * make suspend - * use withContext(Dispatchers.IO) { APIUtil.getJSONResponse(url) }.asJsonObject - */ - fun getJSONResponse(urlString: String, silentError: Boolean = false) = - getJSONResponseAsElement(urlString, silentError) as JsonObject - - fun getJSONResponseAsElement( - urlString: String, - silentError: Boolean = false, - apiName: String = "Hypixel API", - ): JsonElement { - val client = builder.build() - try { - client.execute(HttpGet(urlString)).use { response -> - val entity = response.entity - if (entity != null) { - val retSrc = EntityUtils.toString(entity) - try { - return parser.parse(retSrc) - } catch (e: JsonSyntaxException) { - val name = e.javaClass.name - val message = "$name: ${e.message}" - if (e.message?.contains("Use JsonReader.setLenient(true)") == true) { - println("MalformedJsonException: Use JsonReader.setLenient(true)") - println(" - getJSONResponse: '$urlString'") - ChatUtils.debug("MalformedJsonException: Use JsonReader.setLenient(true)") - } else if (retSrc.contains("

502 Bad Gateway

")) { - if (showApiErrors && apiName == "Hypixel API") { - ChatUtils.clickableChat( - "Problems with detecting the Hypixel API. §eClick here to hide this message for now.", - onClick = { toggleApiErrorMessages() }, - "§eClick to run /shtogglehypixelapierrors!" - ) - } - ErrorManager.skyHanniError( - "SkyHanni Connection Error", - "error message" to "$message(502 Bad Gateway)", - "apiName" to apiName, - "urlString" to urlString, - "returnedData" to retSrc - ) - } else { - ErrorManager.skyHanniError( - "SkyHanni Connection Error", - "error message" to message, - "apiName" to apiName, - "urlString" to urlString, - "returnedData" to retSrc - ) - } - } - } - } - } catch (e: Throwable) { - if (silentError) { - throw e - } - val name = e.javaClass.name - val message = "$name: ${e.message}" - ErrorManager.skyHanniError( - "SkyHanni Connection Error", - "error message" to message, - "urlString" to urlString, - ) - } finally { - client.close() - } - return JsonObject() - } - - fun postJSON(urlString: String, body: String, silentError: Boolean = false): ApiResponse { - val client = builder.build() - - try { - val method = HttpPost(urlString) - method.entity = StringEntity(body, ContentType.APPLICATION_JSON) - - client.execute(method).use { response -> - val status = response.statusLine - val entity = response.entity - - if (status.statusCode in 200..299) { - val data = readResponse(entity) - return ApiResponse(true, "Request successful", data) - } - - val message = "POST request to '$urlString' returned status ${status.statusCode}" - ErrorManager.logErrorStateWithData( - "Error communicating with API", "APIUtil POST request returned an error code", - "statusCode" to status.statusCode, - "urlString" to urlString, - "body" to body, - ) - return ApiResponse(false, message, JsonObject()) - } - } catch (throwable: Throwable) { - if (silentError) { - throw throwable - } - ErrorManager.logErrorWithData( - throwable, "SkyHanni ran into an ${throwable::class.simpleName ?: "error"} whilst sending a resource", - "urlString" to urlString, - "body" to body, - ) - return ApiResponse(false, throwable.message, JsonObject()) - } finally { - client.close() - } - } - - private fun readResponse(entity: HttpEntity): JsonObject { - val retSrc = EntityUtils.toString(entity) ?: return JsonObject() - val parsed = parser.parse(retSrc) - if (parsed.isJsonNull) return JsonObject() - return parsed as JsonObject - } - - fun postJSONIsSuccessful(url: String, body: String, silentError: Boolean = false): Boolean { - val response = postJSON(url, body, silentError) - - if (response.success) { - return true - } - - ErrorManager.logErrorStateWithData( - "An error occurred during the API request", - "unsuccessful API response", - "url" to url, - "body" to body, - "message" to response.message, - "response" to response, - ) - - return false - } - - fun readFile(file: File): BufferedReader { - return BufferedReader(InputStreamReader(FileInputStream(file), StandardCharsets.UTF_8)) - } - - // TODO remove command, use clickable chat message instead - fun toggleApiErrorMessages() { - showApiErrors = !showApiErrors - ChatUtils.chat("Hypixel API error messages " + if (showApiErrors) "§chidden" else "§ashown") - } -} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/APIUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/APIUtils.kt new file mode 100644 index 000000000..d28263af6 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/APIUtils.kt @@ -0,0 +1,213 @@ +package at.hannibal2.skyhanni.utils + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.test.command.ErrorManager +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import com.google.gson.JsonSyntaxException +import org.apache.http.HttpEntity +import org.apache.http.client.config.RequestConfig +import org.apache.http.client.methods.HttpGet +import org.apache.http.client.methods.HttpPost +import org.apache.http.entity.ContentType +import org.apache.http.entity.StringEntity +import org.apache.http.impl.client.HttpClientBuilder +import org.apache.http.impl.client.HttpClients +import org.apache.http.message.BasicHeader +import org.apache.http.util.EntityUtils +import java.security.KeyStore +import javax.net.ssl.HttpsURLConnection +import javax.net.ssl.KeyManagerFactory +import javax.net.ssl.SSLContext +import javax.net.ssl.TrustManagerFactory + +object APIUtils { + + private val parser = JsonParser() + private var showApiErrors = false + + private val ctx: SSLContext? = run { + try { + val myKeyStore = KeyStore.getInstance("JKS") + myKeyStore.load(APIUtils.javaClass.getResourceAsStream("/keystore.jks"), "changeit".toCharArray()) + val kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()) + val tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) + kmf.init(myKeyStore, null) + tmf.init(myKeyStore) + SSLContext.getInstance("TLS").apply { + init(kmf.keyManagers, tmf.trustManagers, null) + } + } catch (e: Exception) { + println("Failed to load keystore. A lot of API requests won't work") + e.printStackTrace() + null + } + } + + fun patchHttpsRequest(connection: HttpsURLConnection) { + ctx?.let { + connection.sslSocketFactory = it.socketFactory + } + } + + data class ApiResponse(val success: Boolean, val message: String?, val data: JsonObject) + + private val builder: HttpClientBuilder = + HttpClients.custom().setUserAgent("SkyHanni/${SkyHanniMod.version}") + .setDefaultHeaders( + mutableListOf( + BasicHeader("Pragma", "no-cache"), + BasicHeader("Cache-Control", "no-cache"), + ), + ) + .setDefaultRequestConfig( + RequestConfig.custom() + .build(), + ) + .useSystemProperties() + + /** + * TODO + * make suspend + * use withContext(Dispatchers.IO) { APIUtils.getJSONResponse(url) }.asJsonObject + */ + fun getJSONResponse(urlString: String, silentError: Boolean = false) = + getJSONResponseAsElement(urlString, silentError) as JsonObject + + fun getJSONResponseAsElement( + urlString: String, + silentError: Boolean = false, + apiName: String = "Hypixel API", + ): JsonElement { + val client = builder.build() + try { + client.execute(HttpGet(urlString)).use { response -> + val entity = response.entity + if (entity != null) { + val retSrc = EntityUtils.toString(entity) + try { + return parser.parse(retSrc) + } catch (e: JsonSyntaxException) { + val name = e.javaClass.name + val message = "$name: ${e.message}" + if (e.message?.contains("Use JsonReader.setLenient(true)") == true) { + println("MalformedJsonException: Use JsonReader.setLenient(true)") + println(" - getJSONResponse: '$urlString'") + ChatUtils.debug("MalformedJsonException: Use JsonReader.setLenient(true)") + } else if (retSrc.contains("

502 Bad Gateway

")) { + if (showApiErrors && apiName == "Hypixel API") { + ChatUtils.clickableChat( + "Problems with detecting the Hypixel API. §eClick here to hide this message for now.", + onClick = { toggleApiErrorMessages() }, + "§eClick to run /shtogglehypixelapierrors!", + ) + } + ErrorManager.skyHanniError( + "SkyHanni Connection Error", + "error message" to "$message(502 Bad Gateway)", + "apiName" to apiName, + "urlString" to urlString, + "returnedData" to retSrc, + ) + } else { + ErrorManager.skyHanniError( + "SkyHanni Connection Error", + "error message" to message, + "apiName" to apiName, + "urlString" to urlString, + "returnedData" to retSrc, + ) + } + } + } + } + } catch (e: Throwable) { + if (silentError) { + throw e + } + val name = e.javaClass.name + val message = "$name: ${e.message}" + ErrorManager.skyHanniError( + "SkyHanni Connection Error", + "error message" to message, + "urlString" to urlString, + ) + } finally { + client.close() + } + return JsonObject() + } + + fun postJSON(urlString: String, body: String, silentError: Boolean = false): ApiResponse { + val client = builder.build() + + try { + val method = HttpPost(urlString) + method.entity = StringEntity(body, ContentType.APPLICATION_JSON) + + client.execute(method).use { response -> + val status = response.statusLine + val entity = response.entity + + if (status.statusCode in 200..299) { + val data = readResponse(entity) + return ApiResponse(true, "Request successful", data) + } + + val message = "POST request to '$urlString' returned status ${status.statusCode}" + ErrorManager.logErrorStateWithData( + "Error communicating with API", "APIUtil POST request returned an error code", + "statusCode" to status.statusCode, + "urlString" to urlString, + "body" to body, + ) + return ApiResponse(false, message, JsonObject()) + } + } catch (throwable: Throwable) { + if (silentError) { + throw throwable + } + ErrorManager.logErrorWithData( + throwable, "SkyHanni ran into an ${throwable::class.simpleName ?: "error"} whilst sending a resource", + "urlString" to urlString, + "body" to body, + ) + return ApiResponse(false, throwable.message, JsonObject()) + } finally { + client.close() + } + } + + private fun readResponse(entity: HttpEntity): JsonObject { + val retSrc = EntityUtils.toString(entity) ?: return JsonObject() + val parsed = parser.parse(retSrc) + if (parsed.isJsonNull) return JsonObject() + return parsed as JsonObject + } + + fun postJSONIsSuccessful(url: String, body: String, silentError: Boolean = false): Boolean { + val response = postJSON(url, body, silentError) + + if (response.success) { + return true + } + + ErrorManager.logErrorStateWithData( + "An error occurred during the API request", + "unsuccessful API response", + "url" to url, + "body" to body, + "message" to response.message, + "response" to response, + ) + + return false + } + + // TODO remove command, use clickable chat message instead + fun toggleApiErrorMessages() { + showApiErrors = !showApiErrors + ChatUtils.chat("Hypixel API error messages " + if (showApiErrors) "§chidden" else "§ashown") + } +} -- cgit