diff --git a/src/pt/argosscan/build.gradle b/src/pt/argosscan/build.gradle index c7c2213ae..f798556a3 100644 --- a/src/pt/argosscan/build.gradle +++ b/src/pt/argosscan/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'Argos Scan' pkgNameSuffix = 'pt.argosscan' extClass = '.ArgosScan' - extVersionCode = 16 + extVersionCode = 17 } dependencies { diff --git a/src/pt/argosscan/src/eu/kanade/tachiyomi/extension/pt/argosscan/ArgosScan.kt b/src/pt/argosscan/src/eu/kanade/tachiyomi/extension/pt/argosscan/ArgosScan.kt index 9970294e7..948cd0a1a 100644 --- a/src/pt/argosscan/src/eu/kanade/tachiyomi/extension/pt/argosscan/ArgosScan.kt +++ b/src/pt/argosscan/src/eu/kanade/tachiyomi/extension/pt/argosscan/ArgosScan.kt @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.extension.pt.argosscan import android.app.Application import android.content.SharedPreferences +import android.text.InputType import androidx.preference.EditTextPreference import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor @@ -20,6 +21,7 @@ import kotlinx.serialization.json.add import kotlinx.serialization.json.decodeFromJsonElement import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive import okhttp3.Interceptor import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.OkHttpClient @@ -49,7 +51,7 @@ class ArgosScan : HttpSource(), ConfigurableSource { override val supportsLatest = true override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .addInterceptor(::tokenIntercept) + .addInterceptor(::loginIntercept) .addInterceptor(RateLimitInterceptor(1, 2, TimeUnit.SECONDS)) .build() @@ -59,6 +61,8 @@ class ArgosScan : HttpSource(), ConfigurableSource { Injekt.get().getSharedPreferences("source_$id", 0x0000) } + private var token: String? = null + private fun genericMangaFromObject(project: ArgosProjectDto): SManga = SManga.create().apply { title = project.name!! url = "/obras/${project.id}" @@ -234,40 +238,104 @@ class ArgosScan : HttpSource(), ConfigurableSource { } override fun setupPreferenceScreen(screen: PreferenceScreen) { - val tokenPref = EditTextPreference(screen.context).apply { - key = TOKEN_PREF_KEY - title = TOKEN_PREF_TITLE - summary = TOKEN_PREF_SUMMARY + val emailPref = EditTextPreference(screen.context).apply { + key = EMAIL_PREF_KEY + title = EMAIL_PREF_TITLE + summary = EMAIL_PREF_SUMMARY setDefaultValue("") - dialogTitle = TOKEN_PREF_TITLE - dialogMessage = TOKEN_PREF_SUMMARY + dialogTitle = EMAIL_PREF_TITLE + dialogMessage = EMAIL_PREF_DIALOG + + setOnBindEditTextListener { + it.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS + } setOnPreferenceChangeListener { _, newValue -> + token = null + preferences.edit() - .putString(TOKEN_PREF_KEY, newValue as String) + .putString(EMAIL_PREF_KEY, newValue as String) .commit() } } - screen.addPreference(tokenPref) + val passwordPref = EditTextPreference(screen.context).apply { + key = PASSWORD_PREF_KEY + title = PASSWORD_PREF_TITLE + summary = PASSWORD_PREF_SUMMARY + setDefaultValue("") + dialogTitle = PASSWORD_PREF_TITLE + dialogMessage = PASSWORD_PREF_DIALOG + + setOnBindEditTextListener { + it.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD + } + + setOnPreferenceChangeListener { _, newValue -> + token = null + + preferences.edit() + .putString(PASSWORD_PREF_KEY, newValue as String) + .commit() + } + } + + screen.addPreference(emailPref) + screen.addPreference(passwordPref) } - private fun tokenIntercept(chain: Interceptor.Chain): Response { + private fun loginIntercept(chain: Interceptor.Chain): Response { if (chain.request().url.toString().contains("graphql").not()) { return chain.proceed(chain.request()) } - val token = preferences.getString(TOKEN_PREF_KEY, "") + val email = preferences.getString(EMAIL_PREF_KEY, "") + val password = preferences.getString(PASSWORD_PREF_KEY, "") - if (token.isNullOrEmpty()) { - throw IOException(TOKEN_NOT_FOUND) + if (!email.isNullOrEmpty() && !password.isNullOrEmpty() && token.isNullOrEmpty()) { + val loginResponse = chain.proceed(loginRequest(email, password)) + val loginResult = json.parseToJsonElement(loginResponse.body!!.string()).jsonObject + + if (loginResult["errors"] != null) { + loginResponse.close() + + val errorMessage = runCatching { + loginResult["errors"]!!.jsonArray[0].jsonObject["message"]?.jsonPrimitive?.content + } + + throw IOException(errorMessage.getOrNull() ?: REQUEST_ERROR) + } + + token = loginResult["data"]!! + .jsonObject["login"]!! + .jsonObject["token"]!! + .jsonPrimitive.content + + loginResponse.close() } - val newRequest = chain.request().newBuilder() - .addHeader("Token", token) + if (!token.isNullOrEmpty()) { + val authorizedRequest = chain.request().newBuilder() + .addHeader("Authorization", "Bearer $token") + .build() + + return chain.proceed(authorizedRequest) + } + + return chain.proceed(chain.request()) + } + + private fun loginRequest(email: String, password: String): Request { + val payload = buildLoginMutationQueryPayload(email, password) + + val body = payload.toString().toRequestBody(JSON_MEDIA_TYPE) + + val newHeaders = headersBuilder() + .add("Content-Length", body.contentLength().toString()) + .add("Content-Type", body.contentType().toString()) .build() - return chain.proceed(newRequest) + return POST(GRAPHQL_URL, newHeaders, body) } private fun String.toDate(): Long { @@ -278,10 +346,16 @@ class ArgosScan : HttpSource(), ConfigurableSource { companion object { private const val GRAPHQL_URL = "https://argosscan.com/graphql" - private const val TOKEN_PREF_KEY = "token" - private const val TOKEN_PREF_TITLE = "Token" - private const val TOKEN_PREF_SUMMARY = "Defina o token de acesso ao conteúdo." - private const val TOKEN_NOT_FOUND = "Token não informado. Defina-o nas configurações da extensão." + private const val EMAIL_PREF_KEY = "email" + private const val EMAIL_PREF_TITLE = "E-mail" + private const val EMAIL_PREF_SUMMARY = "Defina o e-mail de sua conta no site." + private const val EMAIL_PREF_DIALOG = "Deixe em branco caso o site torne o login opcional." + + private const val PASSWORD_PREF_KEY = "password" + private const val PASSWORD_PREF_TITLE = "Senha" + private const val PASSWORD_PREF_SUMMARY = "Defina a senha de sua conta no site." + private const val PASSWORD_PREF_DIALOG = EMAIL_PREF_DIALOG + private const val REQUEST_ERROR = "Erro na requisição. Tente novamente mais tarde." private val JSON_MEDIA_TYPE = "application/json; charset=utf-8".toMediaTypeOrNull() diff --git a/src/pt/argosscan/src/eu/kanade/tachiyomi/extension/pt/argosscan/ArgosScanQueries.kt b/src/pt/argosscan/src/eu/kanade/tachiyomi/extension/pt/argosscan/ArgosScanQueries.kt index bc5fe86e9..bd28f62c8 100644 --- a/src/pt/argosscan/src/eu/kanade/tachiyomi/extension/pt/argosscan/ArgosScanQueries.kt +++ b/src/pt/argosscan/src/eu/kanade/tachiyomi/extension/pt/argosscan/ArgosScanQueries.kt @@ -9,6 +9,25 @@ import kotlinx.serialization.json.putJsonObject private fun buildQuery(queryAction: () -> String) = queryAction().replace("%", "$") +val LOGIN_MUTATION_QUERY = buildQuery { + """ + | mutation login(%email: String!, %password: String!) { + | login(loginInput: { email: %email, password: %password }) { + | token + | } + | } + """.trimMargin() +} + +fun buildLoginMutationQueryPayload(email: String, password: String) = buildJsonObject { + put("operationName", "login") + put("query", LOGIN_MUTATION_QUERY) + putJsonObject("variables") { + put("email", email) + put("password", password) + } +} + val POPULAR_QUERY = buildQuery { """ | query getProjects(