diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d6f3c2..800d709 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 11.1.0 + +* `Bot`: + * Add opportunity to setup proxy + ## 11.0.0 * `Versions`: diff --git a/bot/build.gradle b/bot/build.gradle index 34c2f9f..5327d56 100644 --- a/bot/build.gradle +++ b/bot/build.gradle @@ -22,6 +22,8 @@ dependencies { api libs.microutils.koin api libs.microutils.startup.launcher + api libs.ktor.engine.okhttp + api libs.sqlite testImplementation libs.kt.test.junit diff --git a/bot/src/main/kotlin/dev/inmo/plagubot/PlaguBot.kt b/bot/src/main/kotlin/dev/inmo/plagubot/PlaguBot.kt index 26e1084..610c5e5 100644 --- a/bot/src/main/kotlin/dev/inmo/plagubot/PlaguBot.kt +++ b/bot/src/main/kotlin/dev/inmo/plagubot/PlaguBot.kt @@ -3,7 +3,6 @@ package dev.inmo.plagubot import dev.inmo.kslog.common.* import dev.inmo.micro_utils.common.Warning import dev.inmo.micro_utils.coroutines.runCatchingLogging -import dev.inmo.micro_utils.coroutines.runCatchingSafely import dev.inmo.micro_utils.fsm.common.State import dev.inmo.micro_utils.fsm.common.StatesManager import dev.inmo.micro_utils.fsm.common.managers.* @@ -16,6 +15,10 @@ import dev.inmo.tgbotapi.bot.ktor.telegramBot import dev.inmo.tgbotapi.extensions.api.webhook.deleteWebhook import dev.inmo.tgbotapi.extensions.behaviour_builder.* import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling +import io.ktor.client.HttpClient +import io.ktor.client.engine.HttpClientEngine +import io.ktor.client.engine.HttpClientEngineFactory +import io.ktor.client.engine.okhttp.OkHttp import kotlinx.coroutines.* import kotlinx.serialization.Serializable import kotlinx.serialization.json.* @@ -58,6 +61,19 @@ object PlaguBot : Plugin { * @param params Raw JSON params of the bot part of the configuration */ override fun KtorRequestsExecutorBuilder.setupBotClient(scope: Scope, params: JsonObject) { + val config = scope.get() + if (config.proxy != null) { + val initialClient = config.proxy.createDefaultClient() + val clientFromHttpClientEngine = scope.getOrNull() ?.let { + HttpClient(it) + } + val clientFromKoin = clientFromHttpClientEngine ?: (scope.getOrNull>() ?: OkHttp).let { + HttpClient(it) + } + this@setupBotClient.client = initialClient.config { + install(clientFromKoin) + } + } scope.plugins.filter { it !== this@PlaguBot }.forEach { with(it) { setupBotClient(scope, params) diff --git a/bot/src/main/kotlin/dev/inmo/plagubot/config/Config.kt b/bot/src/main/kotlin/dev/inmo/plagubot/config/Config.kt index 8b82cd1..30c718b 100644 --- a/bot/src/main/kotlin/dev/inmo/plagubot/config/Config.kt +++ b/bot/src/main/kotlin/dev/inmo/plagubot/config/Config.kt @@ -14,5 +14,6 @@ data class Config( @SerialName("database") val databaseConfig: DatabaseConfig = DatabaseConfig(), val botApiServer: String = telegramBotAPIDefaultUrl, - val testServer: Boolean = false + val testServer: Boolean = false, + val proxy: ProxyConfig? = null, ) diff --git a/bot/src/main/kotlin/dev/inmo/plagubot/config/ProxyConfig.kt b/bot/src/main/kotlin/dev/inmo/plagubot/config/ProxyConfig.kt new file mode 100644 index 0000000..0b954d3 --- /dev/null +++ b/bot/src/main/kotlin/dev/inmo/plagubot/config/ProxyConfig.kt @@ -0,0 +1,47 @@ +package dev.inmo.plagubot.config + +import io.ktor.client.* +import io.ktor.client.engine.okhttp.OkHttp +import io.ktor.client.plugins.* +import io.ktor.client.request.* +import io.ktor.http.* +import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient +import java.net.Authenticator +import java.net.InetSocketAddress +import java.net.PasswordAuthentication +import java.net.Proxy +import kotlin.io.encoding.Base64 + +@Serializable +data class ProxyConfig( + val host: String, + val port: Int, + val username: String? = null, + val password: String? = null +) { + @Transient + private val proxy = Proxy(Proxy.Type.SOCKS, InetSocketAddress(host, port)) + fun createDefaultClient() = HttpClient(OkHttp) { + engine { + config { + proxy(this@ProxyConfig.proxy) + if (username != null && password != null) { + val passwordAuthentication = PasswordAuthentication( + username, + password.toCharArray() + ) + Authenticator.setDefault(object : Authenticator() { + override fun getPasswordAuthentication(): PasswordAuthentication? { + return if (requestingHost.lowercase() == host.lowercase()) { + passwordAuthentication + } else { + null + } + } + }) + } + } + } + } +} diff --git a/gradle.properties b/gradle.properties index 6c13cfe..a193d1c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,4 +5,4 @@ kotlin.js.generate.externals=true kotlin.incremental=true group=dev.inmo -version=11.0.0 +version=11.1.0 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 75ee4a3..d5cba08 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,6 +6,7 @@ kt-coroutines = "1.10.2" microutils = "0.29.1" tgbotapi = "32.0.0" +ktor = "3.4.1" ksp = "2.3.6" @@ -35,6 +36,8 @@ microutils-koin-generator = { module = "dev.inmo:micro_utils.koin.generator", ve microutils-startup-launcher = { module = "dev.inmo:micro_utils.startup.launcher", version.ref = "microutils" } microutils-startup-plugin = { module = "dev.inmo:micro_utils.startup.plugin", version.ref = "microutils" } +ktor-engine-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" } + koin = { module = "io.insert-koin:koin-core", version.ref = "koin" } jb-exposed-jdbc = { module = "org.jetbrains.exposed:exposed-jdbc", version.ref = "jb-exposed" } diff --git a/template.config.json b/template.config.json index 4d7245f..87f17e1 100644 --- a/template.config.json +++ b/template.config.json @@ -10,6 +10,13 @@ } }, "botToken": "1234567890:ABCDEFGHIJKLMNOP_qrstuvwxyz12345678", + "proxy": { + "host": "127.0.0.1", + "port": 1080, + "username": "OPTIONAL username", + "password": "OPTIONAL password", + "_note": "THIS OBJECT IS OPTIONAL" + }, "plugins": [ "dev.inmo.plagubot.HelloPlugin" ],