diff --git a/app/src/main/java/ani/dantotsu/App.kt b/app/src/main/java/ani/dantotsu/App.kt index 7861711b..5456fc9c 100644 --- a/app/src/main/java/ani/dantotsu/App.kt +++ b/app/src/main/java/ani/dantotsu/App.kt @@ -111,6 +111,10 @@ class App : MultiDexApplication() { logger("Novel Extensions: ${novelExtensionManager.installedExtensionsFlow.first()}") NovelSources.init(novelExtensionManager.installedExtensionsFlow) } + val commentsScope = CoroutineScope(Dispatchers.Default) + commentsScope.launch { + CommentsAPI.fetchAuthToken() + } } diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt index 546049df..f9a5850e 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt @@ -37,7 +37,6 @@ suspend fun getUserId(context: Context, block: () -> Unit) { if (MAL.token != null && !MAL.query.getUserData()) snackString(context.getString(R.string.error_loading_mal_user_data)) } - CommentsAPI.fetchAuthToken() true } else { snackString(context.getString(R.string.error_loading_anilist_user_data)) diff --git a/app/src/main/java/ani/dantotsu/connections/comments/CommentsAPI.kt b/app/src/main/java/ani/dantotsu/connections/comments/CommentsAPI.kt index 8b13f50a..6f51a52b 100644 --- a/app/src/main/java/ani/dantotsu/connections/comments/CommentsAPI.kt +++ b/app/src/main/java/ani/dantotsu/connections/comments/CommentsAPI.kt @@ -4,6 +4,7 @@ import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.snackString +import com.lagradost.nicehttp.NiceResponse import com.lagradost.nicehttp.Requests import eu.kanade.tachiyomi.network.NetworkHelper import kotlinx.serialization.KSerializer @@ -206,31 +207,63 @@ object CommentsAPI { suspend fun fetchAuthToken() { if (authToken != null) return + val MAX_RETRIES = 5 + val tokenLifetime: Long = 1000 * 60 * 60 * 24 * 6 // 6 days + val tokenExpiry = PrefManager.getVal(PrefName.CommentTokenExpiry) + if (tokenExpiry < System.currentTimeMillis() + tokenLifetime) { + val commentResponse = PrefManager.getNullableVal(PrefName.CommentAuthResponse, null) + if (commentResponse != null) { + authToken = commentResponse.authToken + userId = commentResponse.user.id + isBanned = commentResponse.user.isBanned ?: false + isAdmin = commentResponse.user.isAdmin ?: false + isMod = commentResponse.user.isMod ?: false + totalVotes = commentResponse.user.totalVotes + return + } + } val url = "$address/authenticate" val token = PrefManager.getVal(PrefName.AnilistToken, null as String?) ?: return + repeat(MAX_RETRIES) { // Define MAX_RETRIES as a constant + try { + val json = authRequest(token, url) + if (json.code == 200) { + if (!json.text.startsWith("{")) throw IOException("Invalid response") + val parsed = try { + Json.decodeFromString(json.text) + } catch (e: Exception) { + snackString("Failed to login to comments API: ${e.printStackTrace()}") + return + } + PrefManager.setVal(PrefName.CommentAuthResponse, parsed) + PrefManager.setVal(PrefName.CommentTokenExpiry, System.currentTimeMillis() + tokenLifetime) + authToken = parsed.authToken + userId = parsed.user.id + isBanned = parsed.user.isBanned ?: false + isAdmin = parsed.user.isAdmin ?: false + isMod = parsed.user.isMod ?: false + totalVotes = parsed.user.totalVotes + return + } else if (json.code != 429) { + errorReason(json.code, json.text) + return + } + } catch (e: IOException) { + snackString("Failed to login to comments API") + return + } + // Wait for 1 minute before retrying + kotlinx.coroutines.delay(60000) + } + snackString("Failed to login after multiple attempts") + } + + private suspend fun authRequest(token: String, url: String): NiceResponse { val body: FormBody = FormBody.Builder() .add("token", token) .build() val request = requestBuilder() - val json = try { - request.post(url, requestBody = body) - } catch (e: IOException) { - snackString("Failed to login to comments API") - return - } - if (!json.text.startsWith("{")) return - val parsed = try { - Json.decodeFromString(json.text) - } catch (e: Exception) { - snackString("Failed to login to comments API: ${e.printStackTrace()}") - return - } - authToken = parsed.authToken - userId = parsed.user.id - isBanned = parsed.user.isBanned ?: false - isAdmin = parsed.user.isAdmin ?: false - isMod = parsed.user.isMod ?: false - totalVotes = parsed.user.totalVotes + return request.post(url, requestBody = body) } private fun headerBuilder(): Map { @@ -278,7 +311,11 @@ data class AuthResponse( val authToken: String, @SerialName("user") val user: User -) +) : java.io.Serializable { + companion object { + private const val serialVersionUID: Long = 1 + } +} @Serializable data class User( @@ -299,7 +336,11 @@ data class User( val isMod: Boolean? = null, @SerialName("total_votes") val totalVotes: Int, -) +) : java.io.Serializable { + companion object { + private const val serialVersionUID: Long = 1 + } +} @Serializable data class CommentResponse( diff --git a/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt b/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt index 72847482..bbe0dcf9 100644 --- a/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt +++ b/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt @@ -1,6 +1,7 @@ package ani.dantotsu.settings.saving import android.graphics.Color +import ani.dantotsu.connections.comments.AuthResponse import ani.dantotsu.connections.mal.MAL import ani.dantotsu.settings.saving.internal.Location import ani.dantotsu.settings.saving.internal.Pref @@ -154,6 +155,8 @@ enum class PrefName(val data: Pref) { //TODO: Split this into multiple files TagsListNonAdult(Pref(Location.Irrelevant, Set::class, setOf())), MakeDefault(Pref(Location.Irrelevant, Boolean::class, true)), FirstComment(Pref(Location.Irrelevant, Boolean::class, true)), + CommentAuthResponse(Pref(Location.Irrelevant, AuthResponse::class, "")), + CommentTokenExpiry(Pref(Location.Irrelevant, Long::class, 0L)), //Protected DiscordToken(Pref(Location.Protected, String::class, "")),