Improvements to accuracy of universal captcha detection

This commit is contained in:
NerdNumber9
2019-04-21 23:01:41 -04:00
parent 59fcfbe9d2
commit c5ab79f618
5 changed files with 122 additions and 52 deletions

View File

@ -3,10 +3,6 @@ package eu.kanade.tachiyomi.network
import android.content.Context
import android.os.Build
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 java.io.File
import java.io.IOException
@ -35,8 +31,7 @@ open class NetworkHelper(context: Context) {
open val cloudflareClient = client.newBuilder()
.addInterceptor(CloudflareInterceptor(context))
.attachMangaDexLogin()
.detectCaptchas(context, MANGADEX_SOURCE_ID, listOf(MANGADEX_DOMAIN))
.maybeInjectEHLogger()
.build()
private fun OkHttpClient.Builder.enableTLS12(): OkHttpClient.Builder {

View File

@ -6,13 +6,12 @@ import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.network.*
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.model.*
import exh.patch.detectCaptchas
import exh.patch.injectPatches
import exh.source.DelegatedHttpSource
import okhttp3.*
import rx.Observable
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.lang.Exception
import java.net.URI
import java.net.URISyntaxException
@ -31,9 +30,15 @@ abstract class HttpSource : CatalogueSource {
object : NetworkHelper(Injekt.get<Application>()) {
override val client: OkHttpClient?
get() = delegate?.networkHttpClient ?: original.client
.newBuilder()
.injectPatches { id }
.build()
override val cloudflareClient: OkHttpClient?
get() = delegate?.networkCloudflareClient ?: original.cloudflareClient
.newBuilder()
.injectPatches { id }
.build()
override val cookieManager: AndroidCookieJar
get() = original.cookieManager
@ -79,9 +84,6 @@ abstract class HttpSource : CatalogueSource {
*/
open val client: OkHttpClient
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.

View File

@ -24,26 +24,63 @@ private fun verifyComplete(url: String): Boolean {
} ?: false
}
fun OkHttpClient.Builder.attachMangaDexLogin() =
addInterceptor { chain ->
val response = chain.proceed(chain.request())
if(response.request().url().host() == MANGADEX_DOMAIN) {
response.interceptAsHtml { doc ->
if (doc.title().trim().equals("Login - MangaDex", true)) {
BrowserActionActivity.launchAction(
Injekt.get<Application>(),
::verifyComplete,
HIDE_SCRIPT,
"https://mangadex.org/login",
"Login",
(Injekt.get<SourceManager>().get(MANGADEX_SOURCE_ID) as? HttpSource)?.headers?.toMultimap()?.mapValues {
it.value.joinToString(",")
} ?: emptyMap()
)
}
}
} else response
val MANGADEX_LOGIN_PATCH: EHInterceptor = { request, response, sourceId ->
if(request.url().host() == MANGADEX_DOMAIN) {
response.interceptAsHtml { doc ->
if (doc.title().trim().equals("Login - MangaDex", true)) {
BrowserActionActivity.launchAction(
Injekt.get<Application>(),
::verifyComplete,
HIDE_SCRIPT,
"https://mangadex.org/login",
"Login",
(Injekt.get<SourceManager>().get(sourceId) as? HttpSource)?.headers?.toMultimap()?.mapValues {
it.value.joinToString(",")
} ?: emptyMap()
)
}
}
} 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"

View 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()
)

View File

@ -1,28 +1,23 @@
package exh.patch
import android.content.Context
import android.app.Application
import exh.ui.captcha.BrowserActionActivity
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 {
return addInterceptor { chain ->
// Automatic captcha detection
val response = chain.proceed(chain.request())
if(!response.isSuccessful) {
if(domains != null && response.request().url().host() !in domains)
return@addInterceptor response
response.interceptAsHtml { doc ->
if (doc.getElementsByClass("g-recaptcha").isNotEmpty()) {
// Found it, allow the user to solve this thing
BrowserActionActivity.launchUniversal(
context,
sourceId,
chain.request().url().toString()
)
}
val CAPTCHA_DETECTION_PATCH: EHInterceptor = { request, response, sourceId ->
if(!response.isSuccessful) {
response.interceptAsHtml { doc ->
// Find captcha
if (doc.getElementsByClass("g-recaptcha").isNotEmpty()) {
// Found it, allow the user to solve this thing
BrowserActionActivity.launchUniversal(
Injekt.get<Application>(),
sourceId,
request.url().toString()
)
}
} else response
}
}
} else response
}