11package com.geode.launcher.updater
22
33import com.geode.launcher.utils.DownloadUtils.executeCoroutine
4+ import kotlinx.datetime.Clock
5+ import kotlinx.datetime.DateTimeUnit
6+ import kotlinx.datetime.Instant
7+ import kotlinx.datetime.until
48import kotlinx.serialization.ExperimentalSerializationApi
59import kotlinx.serialization.json.Json
610import kotlinx.serialization.json.JsonNamingStrategy
@@ -15,6 +19,9 @@ class ReleaseRepository(private val httpClient: OkHttpClient) {
1519 private const val GITHUB_API_BASE = " https://api.github.com"
1620 private const val GITHUB_API_HEADER = " X-GitHub-Api-Version"
1721 private const val GITHUB_API_VERSION = " 2022-11-28"
22+
23+ private const val GITHUB_RATELIMIT_REMAINING = " x-ratelimit-remaining"
24+ private const val GITHUB_RATELIMIT_RESET = " x-ratelimit-reset"
1825 }
1926
2027 suspend fun getLatestLauncherRelease (): Release ? {
@@ -35,6 +42,15 @@ class ReleaseRepository(private val httpClient: OkHttpClient) {
3542 return getReleaseByUrl(url)
3643 }
3744
45+ private fun generateRateLimitMessage (resetTime : Instant ): String {
46+ val currentTime = Clock .System .now()
47+ val resetDelay = currentTime.until(resetTime, DateTimeUnit .SECOND )
48+
49+ val formattedWait = " ${resetDelay / 60 } m"
50+
51+ return " api ratelimit reached, try again in ${formattedWait} "
52+ }
53+
3854 @OptIn(ExperimentalSerializationApi ::class )
3955 private suspend fun getReleaseByUrl (url : URL ): Release ? {
4056 val request = Request .Builder ()
@@ -59,6 +75,24 @@ class ReleaseRepository(private val httpClient: OkHttpClient) {
5975
6076 release
6177 }
78+ 403 -> {
79+ // determine if the error code is a ratelimit
80+ // (github docs say it sends 429 too, but haven't seen that)
81+
82+ val limitRemaining = response.headers.get(GITHUB_RATELIMIT_REMAINING )?.toInt()
83+ val limitReset = response.headers.get(GITHUB_RATELIMIT_RESET )?.toLong()
84+
85+ if (limitRemaining == 0 && limitReset != null ) {
86+ // handle ratelimit with a custom error
87+ // there's also a retry-after header but again, haven't seen
88+ val resetTime = Instant .fromEpochSeconds(limitReset, 0L )
89+ val msg = generateRateLimitMessage(resetTime)
90+
91+ throw IOException (msg)
92+ }
93+
94+ throw IOException (" response 403: ${response.body!! .string()} " )
95+ }
6296 404 -> {
6397 null
6498 }
0 commit comments