mirror of
https://github.com/mihonapp/mihon.git
synced 2025-06-27 11:37:51 +02:00
Improvements to accuracy of universal captcha detection
This commit is contained in:
@ -3,10 +3,6 @@ package eu.kanade.tachiyomi.network
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import exh.log.maybeInjectEHLogger
|
import exh.log.maybeInjectEHLogger
|
||||||
import exh.patch.MANGADEX_DOMAIN
|
|
||||||
import exh.patch.MANGADEX_SOURCE_ID
|
|
||||||
import exh.patch.attachMangaDexLogin
|
|
||||||
import exh.patch.detectCaptchas
|
|
||||||
import okhttp3.*
|
import okhttp3.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@ -35,8 +31,7 @@ open class NetworkHelper(context: Context) {
|
|||||||
|
|
||||||
open val cloudflareClient = client.newBuilder()
|
open val cloudflareClient = client.newBuilder()
|
||||||
.addInterceptor(CloudflareInterceptor(context))
|
.addInterceptor(CloudflareInterceptor(context))
|
||||||
.attachMangaDexLogin()
|
.maybeInjectEHLogger()
|
||||||
.detectCaptchas(context, MANGADEX_SOURCE_ID, listOf(MANGADEX_DOMAIN))
|
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
private fun OkHttpClient.Builder.enableTLS12(): OkHttpClient.Builder {
|
private fun OkHttpClient.Builder.enableTLS12(): OkHttpClient.Builder {
|
||||||
|
@ -6,13 +6,12 @@ import eu.kanade.tachiyomi.data.preference.getOrDefault
|
|||||||
import eu.kanade.tachiyomi.network.*
|
import eu.kanade.tachiyomi.network.*
|
||||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||||
import eu.kanade.tachiyomi.source.model.*
|
import eu.kanade.tachiyomi.source.model.*
|
||||||
import exh.patch.detectCaptchas
|
import exh.patch.injectPatches
|
||||||
import exh.source.DelegatedHttpSource
|
import exh.source.DelegatedHttpSource
|
||||||
import okhttp3.*
|
import okhttp3.*
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import uy.kohesive.injekt.injectLazy
|
|
||||||
import java.lang.Exception
|
import java.lang.Exception
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.net.URISyntaxException
|
import java.net.URISyntaxException
|
||||||
@ -31,9 +30,15 @@ abstract class HttpSource : CatalogueSource {
|
|||||||
object : NetworkHelper(Injekt.get<Application>()) {
|
object : NetworkHelper(Injekt.get<Application>()) {
|
||||||
override val client: OkHttpClient?
|
override val client: OkHttpClient?
|
||||||
get() = delegate?.networkHttpClient ?: original.client
|
get() = delegate?.networkHttpClient ?: original.client
|
||||||
|
.newBuilder()
|
||||||
|
.injectPatches { id }
|
||||||
|
.build()
|
||||||
|
|
||||||
override val cloudflareClient: OkHttpClient?
|
override val cloudflareClient: OkHttpClient?
|
||||||
get() = delegate?.networkCloudflareClient ?: original.cloudflareClient
|
get() = delegate?.networkCloudflareClient ?: original.cloudflareClient
|
||||||
|
.newBuilder()
|
||||||
|
.injectPatches { id }
|
||||||
|
.build()
|
||||||
|
|
||||||
override val cookieManager: AndroidCookieJar
|
override val cookieManager: AndroidCookieJar
|
||||||
get() = original.cookieManager
|
get() = original.cookieManager
|
||||||
@ -79,9 +84,6 @@ abstract class HttpSource : CatalogueSource {
|
|||||||
*/
|
*/
|
||||||
open val client: OkHttpClient
|
open val client: OkHttpClient
|
||||||
get() = delegate?.baseHttpClient ?: network.client
|
get() = delegate?.baseHttpClient ?: network.client
|
||||||
.newBuilder()
|
|
||||||
.detectCaptchas(Injekt.get<Application>(), id, null)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Headers builder for requests. Implementations can override this method for custom headers.
|
* Headers builder for requests. Implementations can override this method for custom headers.
|
||||||
|
@ -24,10 +24,8 @@ private fun verifyComplete(url: String): Boolean {
|
|||||||
} ?: false
|
} ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun OkHttpClient.Builder.attachMangaDexLogin() =
|
val MANGADEX_LOGIN_PATCH: EHInterceptor = { request, response, sourceId ->
|
||||||
addInterceptor { chain ->
|
if(request.url().host() == MANGADEX_DOMAIN) {
|
||||||
val response = chain.proceed(chain.request())
|
|
||||||
if(response.request().url().host() == MANGADEX_DOMAIN) {
|
|
||||||
response.interceptAsHtml { doc ->
|
response.interceptAsHtml { doc ->
|
||||||
if (doc.title().trim().equals("Login - MangaDex", true)) {
|
if (doc.title().trim().equals("Login - MangaDex", true)) {
|
||||||
BrowserActionActivity.launchAction(
|
BrowserActionActivity.launchAction(
|
||||||
@ -36,14 +34,53 @@ fun OkHttpClient.Builder.attachMangaDexLogin() =
|
|||||||
HIDE_SCRIPT,
|
HIDE_SCRIPT,
|
||||||
"https://mangadex.org/login",
|
"https://mangadex.org/login",
|
||||||
"Login",
|
"Login",
|
||||||
(Injekt.get<SourceManager>().get(MANGADEX_SOURCE_ID) as? HttpSource)?.headers?.toMultimap()?.mapValues {
|
(Injekt.get<SourceManager>().get(sourceId) as? HttpSource)?.headers?.toMultimap()?.mapValues {
|
||||||
it.value.joinToString(",")
|
it.value.joinToString(",")
|
||||||
} ?: emptyMap()
|
} ?: emptyMap()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else response
|
} else response
|
||||||
}
|
}
|
||||||
|
|
||||||
const val MANGADEX_SOURCE_ID = 2499283573021220255
|
val MANGADEX_SOURCE_IDS = listOf(
|
||||||
|
2499283573021220255,
|
||||||
|
8033579885162383068,
|
||||||
|
1952071260038453057,
|
||||||
|
2098905203823335614,
|
||||||
|
5098537545549490547,
|
||||||
|
4505830566611664829,
|
||||||
|
9194073792736219759,
|
||||||
|
6400665728063187402,
|
||||||
|
4938773340256184018,
|
||||||
|
5860541308324630662,
|
||||||
|
5189216366882819742,
|
||||||
|
2655149515337070132,
|
||||||
|
1145824452519314725,
|
||||||
|
3846770256925560569,
|
||||||
|
3807502156582598786,
|
||||||
|
4284949320785450865,
|
||||||
|
5463447640980279236,
|
||||||
|
8578871918181236609,
|
||||||
|
6750440049024086587,
|
||||||
|
3339599426223341161,
|
||||||
|
5148895169070562838,
|
||||||
|
1493666528525752601,
|
||||||
|
1713554459881080228,
|
||||||
|
4150470519566206911,
|
||||||
|
1347402746269051958,
|
||||||
|
3578612018159256808,
|
||||||
|
425785191804166217,
|
||||||
|
8254121249433835847,
|
||||||
|
3260701926561129943,
|
||||||
|
1411768577036936240,
|
||||||
|
3285208643537017688,
|
||||||
|
737986167355114438,
|
||||||
|
1471784905273036181,
|
||||||
|
5967745367608513818,
|
||||||
|
3781216447842245147,
|
||||||
|
4774459486579224459,
|
||||||
|
4710920497926776490,
|
||||||
|
5779037855201976894
|
||||||
|
)
|
||||||
const val MANGADEX_DOMAIN = "mangadex.org"
|
const val MANGADEX_DOMAIN = "mangadex.org"
|
||||||
|
41
app/src/main/java/exh/patch/NetworkPatches.kt
Normal file
41
app/src/main/java/exh/patch/NetworkPatches.kt
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package exh.patch
|
||||||
|
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
|
||||||
|
typealias EHInterceptor = (request: Request, response: Response, sourceId: Long) -> Response
|
||||||
|
|
||||||
|
fun OkHttpClient.Builder.injectPatches(sourceIdProducer: () -> Long): OkHttpClient.Builder {
|
||||||
|
return addInterceptor { chain ->
|
||||||
|
val req = chain.request()
|
||||||
|
val response = chain.proceed(req)
|
||||||
|
val sourceId = sourceIdProducer()
|
||||||
|
findAndApplyPatches(sourceId)(req, response, sourceId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findAndApplyPatches(sourceId: Long): EHInterceptor {
|
||||||
|
return ((EH_INTERCEPTORS[sourceId] ?: emptyList()) +
|
||||||
|
(EH_INTERCEPTORS[EH_UNIVERSAL_INTERCEPTOR] ?: emptyList())).merge()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun List<EHInterceptor>.merge(): EHInterceptor {
|
||||||
|
return { request, response, sourceId ->
|
||||||
|
fold(response) { acc, int ->
|
||||||
|
int(request, acc, sourceId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const val EH_UNIVERSAL_INTERCEPTOR = -1L
|
||||||
|
private val EH_INTERCEPTORS: Map<Long, List<EHInterceptor>> = mapOf(
|
||||||
|
EH_UNIVERSAL_INTERCEPTOR to listOf(
|
||||||
|
CAPTCHA_DETECTION_PATCH // Auto captcha detection
|
||||||
|
),
|
||||||
|
|
||||||
|
// MangaDex login support
|
||||||
|
*MANGADEX_SOURCE_IDS.map { id ->
|
||||||
|
id to listOf(MANGADEX_LOGIN_PATCH)
|
||||||
|
}.toTypedArray()
|
||||||
|
)
|
@ -1,28 +1,23 @@
|
|||||||
package exh.patch
|
package exh.patch
|
||||||
|
|
||||||
import android.content.Context
|
import android.app.Application
|
||||||
import exh.ui.captcha.BrowserActionActivity
|
import exh.ui.captcha.BrowserActionActivity
|
||||||
import exh.util.interceptAsHtml
|
import exh.util.interceptAsHtml
|
||||||
import okhttp3.OkHttpClient
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
fun OkHttpClient.Builder.detectCaptchas(context: Context, sourceId: Long, domains: List<String>? = null): OkHttpClient.Builder {
|
val CAPTCHA_DETECTION_PATCH: EHInterceptor = { request, response, sourceId ->
|
||||||
return addInterceptor { chain ->
|
|
||||||
// Automatic captcha detection
|
|
||||||
val response = chain.proceed(chain.request())
|
|
||||||
if(!response.isSuccessful) {
|
if(!response.isSuccessful) {
|
||||||
if(domains != null && response.request().url().host() !in domains)
|
|
||||||
return@addInterceptor response
|
|
||||||
|
|
||||||
response.interceptAsHtml { doc ->
|
response.interceptAsHtml { doc ->
|
||||||
|
// Find captcha
|
||||||
if (doc.getElementsByClass("g-recaptcha").isNotEmpty()) {
|
if (doc.getElementsByClass("g-recaptcha").isNotEmpty()) {
|
||||||
// Found it, allow the user to solve this thing
|
// Found it, allow the user to solve this thing
|
||||||
BrowserActionActivity.launchUniversal(
|
BrowserActionActivity.launchUniversal(
|
||||||
context,
|
Injekt.get<Application>(),
|
||||||
sourceId,
|
sourceId,
|
||||||
chain.request().url().toString()
|
request.url().toString()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else response
|
} else response
|
||||||
}
|
|
||||||
}
|
}
|
Reference in New Issue
Block a user