diff --git a/src/main/kotlin/com/lambda/client/command/commands/PlaytimeCommand.kt b/src/main/kotlin/com/lambda/client/command/commands/PlaytimeCommand.kt new file mode 100644 index 000000000..a1f3c7f0a --- /dev/null +++ b/src/main/kotlin/com/lambda/client/command/commands/PlaytimeCommand.kt @@ -0,0 +1,44 @@ +package com.lambda.client.command.commands + +import com.google.gson.JsonParser +import com.lambda.client.command.ClientCommand +import com.lambda.client.commons.utils.ConnectionUtils +import com.lambda.client.commons.utils.grammar +import com.lambda.client.manager.managers.UUIDManager +import com.lambda.client.util.text.MessageSendHelper +import kotlin.time.DurationUnit +import kotlin.time.toDuration + +object PlaytimeCommand: ClientCommand( + name = "playtime", + alias = arrayOf("pt"), + description = "Check a player's playtime on 2b2t" +) { + private val parser = JsonParser() + + init { + string("playerName") { playerName -> + executeAsync("Check a player's playtime on 2b2t") { + UUIDManager.getByName(playerName.value)?.let outer@ { profile -> + ConnectionUtils.requestRawJsonFrom("https://api.2b2t.vc/playtime?uuid=${profile.uuid}") { + MessageSendHelper.sendChatMessage("Failed querying playtime data for player: ${it.message}") + }?.let { + if (it.isEmpty()) { + MessageSendHelper.sendChatMessage("No data found for player: ${profile.name}") + return@outer + } + val jsonElement = parser.parse(it) + val playtimeSeconds = jsonElement.asJsonObject["playtimeSeconds"].asDouble + MessageSendHelper.sendChatMessage("${profile.name} has played for ${ + playtimeSeconds.toDuration(DurationUnit.SECONDS) + }") + } + + return@executeAsync + } + + MessageSendHelper.sendChatMessage("Failed to find player with name ${playerName.value}") + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/client/command/commands/SeenCommand.kt b/src/main/kotlin/com/lambda/client/command/commands/SeenCommand.kt new file mode 100644 index 000000000..81e1e3c91 --- /dev/null +++ b/src/main/kotlin/com/lambda/client/command/commands/SeenCommand.kt @@ -0,0 +1,45 @@ +package com.lambda.client.command.commands + +import com.google.gson.JsonParser +import com.lambda.client.command.ClientCommand +import com.lambda.client.commons.utils.ConnectionUtils +import com.lambda.client.manager.managers.UUIDManager +import com.lambda.client.util.text.MessageSendHelper +import java.time.ZoneId +import java.time.ZonedDateTime +import java.time.format.DateTimeFormatter + +object SeenCommand : ClientCommand( + name = "seen", + alias = arrayOf("lastseen"), + description = "Check when a player was last seen" +) { + + private val parser = JsonParser() + + init { + string("playerName") { playerName -> + executeAsync("Check when a player was last seen") { + UUIDManager.getByName(playerName.value)?.let outer@ { profile -> + ConnectionUtils.requestRawJsonFrom("https://api.2b2t.vc/seen?uuid=${profile.uuid}") { + MessageSendHelper.sendChatMessage("Failed querying seen data for player: ${it.message}") + }?.let { + if (it.isEmpty()) { + MessageSendHelper.sendChatMessage("No data found for player: ${profile.name}") + return@outer + } + val jsonElement = parser.parse(it) + val dateRaw = jsonElement.asJsonObject["time"].asString + val parsedDate = ZonedDateTime.parse(dateRaw).withZoneSameInstant(ZoneId.systemDefault()) + val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(java.time.format.FormatStyle.LONG) + MessageSendHelper.sendChatMessage("${profile.name} was last seen on ${parsedDate.format(dateFormatter)}") + } + + return@executeAsync + } + + MessageSendHelper.sendChatMessage("Failed to find player with name ${playerName.value}") + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/client/commons/utils/ConnectionUtils.kt b/src/main/kotlin/com/lambda/client/commons/utils/ConnectionUtils.kt index b72478aa8..0280abaee 100644 --- a/src/main/kotlin/com/lambda/client/commons/utils/ConnectionUtils.kt +++ b/src/main/kotlin/com/lambda/client/commons/utils/ConnectionUtils.kt @@ -1,13 +1,15 @@ package com.lambda.client.commons.utils import com.lambda.client.module.modules.client.Plugins +import java.net.HttpURLConnection import java.net.URL -import javax.net.ssl.HttpsURLConnection object ConnectionUtils { fun requestRawJsonFrom(url: String, catch: (Exception) -> Unit = { it.printStackTrace() }): String? { return runConnection(url, { connection -> + connection.setRequestProperty("User-Agent", "LambdaClient") + connection.setRequestProperty("Connection", "close") connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8") if (Plugins.token.isNotBlank()) connection.setRequestProperty("Authorization", "token ${Plugins.token}") connection.requestMethod = "GET" @@ -15,8 +17,8 @@ object ConnectionUtils { }, catch) } - fun <T> runConnection(url: String, block: (HttpsURLConnection) -> T?, catch: (Exception) -> Unit = { it.printStackTrace() }): T? { - (URL(url).openConnection() as HttpsURLConnection).run { + fun <T> runConnection(url: String, block: (HttpURLConnection) -> T?, catch: (Exception) -> Unit = { it.printStackTrace() }): T? { + (URL(url).openConnection() as HttpURLConnection).run { return try { doOutput = true doInput = true @@ -29,5 +31,4 @@ object ConnectionUtils { } } } - } \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/client/gui/hudgui/elements/misc/Queue2B2T.kt b/src/main/kotlin/com/lambda/client/gui/hudgui/elements/misc/Queue2B2T.kt index e981b0117..a7696736c 100644 --- a/src/main/kotlin/com/lambda/client/gui/hudgui/elements/misc/Queue2B2T.kt +++ b/src/main/kotlin/com/lambda/client/gui/hudgui/elements/misc/Queue2B2T.kt @@ -1,7 +1,8 @@ package com.lambda.client.gui.hudgui.elements.misc import com.google.gson.Gson -import com.google.gson.annotations.SerializedName +import com.lambda.client.LambdaMod +import com.lambda.client.commons.utils.ConnectionUtils import com.lambda.client.commons.utils.grammar import com.lambda.client.event.SafeClientEvent import com.lambda.client.gui.hudgui.LabelHud @@ -9,11 +10,12 @@ import com.lambda.client.manager.managers.NetworkManager import com.lambda.client.util.CachedValue import com.lambda.client.util.TickTimer import com.lambda.client.util.TimeUnit -import com.lambda.client.util.WebUtils import com.lambda.client.util.text.MessageSendHelper import com.lambda.client.util.threads.defaultScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import java.time.Instant +import java.time.ZonedDateTime internal object Queue2B2T : LabelHud( name = "2B2T Queue", @@ -22,22 +24,26 @@ internal object Queue2B2T : LabelHud( ) { private val hasShownWarning = setting("Has Shown Warning", false, { false }) private val show by setting("Show", Show.BOTH) + private val showUpdatedTime by setting("Show Updated Time", true) private enum class Show { BOTH, PRIORITY, REGULAR } - private const val apiUrl = "https://2bqueue.info/queue" + private const val apiUrl = "https://api.2b2t.vc/queue" private val gson = Gson() private val dataUpdateTimer = TickTimer(TimeUnit.SECONDS) + private var hasInitialized = false - private var queueData = QueueData(0, 0, 0, 0) + private var queueData = QueueData(0, 0, ZonedDateTime.now().toString()) private val lastUpdate by CachedValue(1L, TimeUnit.SECONDS) { - val difference = System.currentTimeMillis() - queueData.lastUpdated + val dateRaw = queueData.time + val parsedDate = ZonedDateTime.parse(dateRaw) + val difference = Instant.now().epochSecond - parsedDate.toEpochSecond() - val minuteAmt = (difference / 60000L % 60L).toInt() - val secondAmt = (difference / 1000L % 60L).toInt() + val minuteAmt = (difference / 60L % 60L).toInt() + val secondAmt = (difference % 60L).toInt() val minutes = grammar(minuteAmt, "minute", "minutes") val seconds = grammar(secondAmt, "second", "seconds") @@ -53,24 +59,28 @@ internal object Queue2B2T : LabelHud( sendWarning() } - if (dataUpdateTimer.tick(15L)) { + if (dataUpdateTimer.tick(300L) // API caches queue data for 5 minutes + || !hasInitialized) { + hasInitialized = true updateQueueData() } if (NetworkManager.isOffline) { - displayText.addLine("Cannot connect to 2bqueue.info", primaryColor) + displayText.addLine("Cannot connect to api.2b2t.vc", primaryColor) displayText.add("Make sure your internet is working!", primaryColor) - } else { - if (showPriority) { - displayText.add("Priority: ", primaryColor) - displayText.add("${queueData.priority}", secondaryColor) - } + return + } - if (showRegular) { - displayText.add("Regular: ", primaryColor) - displayText.add("${queueData.regular}", secondaryColor) - } + if (showPriority) { + displayText.add("Priority: ", primaryColor) + displayText.add("${queueData.prio}", secondaryColor) + } + if (showRegular) { + displayText.add("Regular: ", primaryColor) + displayText.add("${queueData.regular}", secondaryColor) + } + if (showUpdatedTime) { displayText.addLine("", primaryColor) displayText.add("Last updated $lastUpdate ago", primaryColor) } @@ -78,8 +88,8 @@ internal object Queue2B2T : LabelHud( private fun sendWarning() { MessageSendHelper.sendWarningMessage( - "This module uses an external API, 2bqueue.info, which is operated by tycrek#0001." + - "If you do not trust this external API / have not verified the safety yourself, disable this HUD component." + "This module uses an external API, api.2b2t.vc, which is operated by rfresh#2222." + + " If you do not trust this external API / have not verified the safety yourself, disable this HUD component." ) hasShownWarning.value = true } @@ -87,10 +97,16 @@ internal object Queue2B2T : LabelHud( private fun updateQueueData() { defaultScope.launch(Dispatchers.IO) { runCatching { - val json = WebUtils.getUrlContents(apiUrl) - gson.fromJson(json, QueueData::class.java) - }.getOrNull()?.let { - queueData = it + ConnectionUtils.requestRawJsonFrom(apiUrl) { + LambdaMod.LOG.error("Failed querying queue data", it) + }?.let { + gson.fromJson(it, QueueData::class.java)?.let { data -> + queueData = data + return@runCatching + } + + LambdaMod.LOG.error("No queue data received. Is 2b2t down?") + } } } } @@ -99,11 +115,8 @@ internal object Queue2B2T : LabelHud( private val showRegular get() = show == Show.BOTH || show == Show.REGULAR private class QueueData( - @SerializedName("prio") - val priority: Int, + val prio: Int, val regular: Int, - val total: Int, - @SerializedName("timems") - val lastUpdated: Long + val time: String ) }