refactor: split up core into multiple modules
This commit is contained in:
parent
2725342c3f
commit
0d84411f14
38 changed files with 344 additions and 149 deletions
65
api/rest/build.gradle.kts
Normal file
65
api/rest/build.gradle.kts
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.androidLibrary)
|
||||
alias(libs.plugins.kotlinMultiplatform)
|
||||
alias(libs.plugins.kotlinSerialization)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvm()
|
||||
androidTarget {
|
||||
compilerOptions {
|
||||
jvmTarget.set(JvmTarget.JVM_11)
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
commonMain.dependencies {
|
||||
implementation(project(":api:shared"))
|
||||
implementation(project(":common"))
|
||||
|
||||
implementation(libs.kermit)
|
||||
implementation(libs.ktor.client.core)
|
||||
implementation(libs.ktor.client.content.negotiation)
|
||||
implementation(libs.ktor.serialization.kotlinx.json)
|
||||
}
|
||||
commonTest.dependencies {
|
||||
implementation(libs.kotlin.test)
|
||||
}
|
||||
jvmMain.dependencies {
|
||||
implementation(libs.ktor.client.okhttp)
|
||||
}
|
||||
androidMain.dependencies {
|
||||
implementation(libs.ktor.client.okhttp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
coreLibraryDesugaring(libs.desugar)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "moe.lava.neon.api.rest"
|
||||
compileSdk = libs.versions.android.compileSdk.get().toInt()
|
||||
|
||||
defaultConfig {
|
||||
minSdk = libs.versions.android.minSdk.get().toInt()
|
||||
}
|
||||
packaging {
|
||||
resources {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
getByName("release") {
|
||||
isMinifyEnabled = false
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
package moe.lava.neon.api
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.plugins.HttpSend
|
||||
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
|
||||
import io.ktor.client.plugins.cookies.HttpCookies
|
||||
import io.ktor.client.plugins.defaultRequest
|
||||
import io.ktor.client.plugins.plugin
|
||||
import io.ktor.client.plugins.websocket.WebSockets
|
||||
import io.ktor.client.request.header
|
||||
import io.ktor.client.statement.bodyAsText
|
||||
import io.ktor.http.userAgent
|
||||
import io.ktor.serialization.kotlinx.json.json
|
||||
import io.ktor.util.appendAll
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import moe.lava.neon.common.captcha.CaptchaRequest
|
||||
import moe.lava.neon.common.captcha.CaptchaResponse
|
||||
|
||||
class ApiClient {
|
||||
private val logger = Logger.withTag("neon.core.api/client")
|
||||
|
||||
private var captchaHandler: (suspend (CaptchaRequest) -> CaptchaResponse)? = null
|
||||
|
||||
fun setCaptchaHandler(handler: suspend (CaptchaRequest) -> CaptchaResponse) {
|
||||
this.captchaHandler = handler
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
val client = HttpClient {
|
||||
expectSuccess = true
|
||||
install(ContentNegotiation) {
|
||||
json(ApiConstants.json)
|
||||
}
|
||||
install(WebSockets)
|
||||
install(HttpCookies)
|
||||
defaultRequest {
|
||||
url("https://discord.com/api/v9/")
|
||||
userAgent(ApiConstants.userAgent)
|
||||
headers.appendAll(ApiConstants.baseHeaders)
|
||||
}
|
||||
}.apply {
|
||||
plugin(HttpSend).intercept { req ->
|
||||
logger.d { "Intercepting ${req.url.buildString()}" }
|
||||
val call = execute(req)
|
||||
if (call.response.status.value != 400) return@intercept call
|
||||
logger.d { "Found 400 response: ${call.response.bodyAsText()}" }
|
||||
val captchaRequest = runCatching { call.response.body<CaptchaRequest>() }
|
||||
.getOrNull()
|
||||
?: return@intercept call
|
||||
|
||||
logger.d { "Starting captcha flow for: $captchaRequest" }
|
||||
|
||||
val captcha = captchaHandler
|
||||
if (captcha == null) {
|
||||
logger.w { "Captcha handler not found, passing through!" }
|
||||
return@intercept call
|
||||
}
|
||||
|
||||
val solved = captcha(captchaRequest)
|
||||
logger.d { "Captcha solved $solved" }
|
||||
if (solved !is CaptchaResponse.Success) {
|
||||
val failure = solved as CaptchaResponse.Failed
|
||||
logger.w(failure.error) { "Captcha failed" }
|
||||
return@intercept call
|
||||
}
|
||||
|
||||
logger.d { "Refiring" }
|
||||
req.apply {
|
||||
header("X-Captcha-Key", solved.token)
|
||||
if (captchaRequest.captchaSessionId != null) {
|
||||
header("X-Captcha-Session-Id", captchaRequest.captchaSessionId)
|
||||
}
|
||||
if (captchaRequest.captchaRqtoken != null) {
|
||||
header("X-Captcha-Rqtoken", captchaRequest.captchaRqtoken)
|
||||
}
|
||||
}.let { execute(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue