From 22562426e772861a11de525b8cc85dcc911e5256 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Wed, 11 Oct 2023 21:02:36 +0600 Subject: [PATCH] update template --- README.md | 48 ++++++++++++ build.gradle | 1 + example.config.json | 14 +++- gradle/libs.versions.toml | 3 + src/main/kotlin/App.kt | 12 ++- src/main/kotlin/Config.kt | 6 -- src/main/kotlin/config/Config.kt | 9 +++ src/main/kotlin/config/HttpClientConfig.kt | 89 ++++++++++++++++++++++ 8 files changed, 174 insertions(+), 8 deletions(-) delete mode 100644 src/main/kotlin/Config.kt create mode 100644 src/main/kotlin/config/Config.kt create mode 100644 src/main/kotlin/config/HttpClientConfig.kt diff --git a/README.md b/README.md index b917abe..ca0e0ca 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,54 @@ there is [Makefile](Makefile) and you may use something like `make buildAndStart It is important to replace `"TOKEN"` in [Dockerfile](Dockerfile) or remove and add some config as a volume. +### Config + +But you may set up several things for your bot. You should start with adding client object: + +```json +{ + "token": "your bot token", + "client": { + "connectionTimeoutMillis": 10000, + "requestTimeoutMillis": 10000, + "responseTimeoutMillis": 10000, + "proxy": { + "hostname": "127.0.0.1", + "port": 1080, + "type": "socks", + "username": "username", + "password": "password" + } + } +} +``` + +__Required__ fields: + +* `token` +* `client/proxy/hostname` (if you pass `client` and `proxy` fields) - hostname of proxy server +* `client/proxy/password` - password for authentication on proxy server, required if `client/proxy/type` is `socks` and `client/proxy/username` passed + +__Optional__ fields: + +* `client` - object with client configs +* `client/connectionTimeoutMillis` - timeout for connection to the server +* `client/requestTimeoutMillis` - timeout for request complete (when request taken on server) +* `client/responseTimeoutMillis` - timeout for getting a response after request taken on server +* `client/proxy` - proxy settings +* `client/proxy/port` - port of proxy server +* `client/proxy/type` - type of proxy server (can be `socks` or `http`) +* `client/proxy/username` - username for authentication on proxy server +* `client/proxy/password` - password for authentication on proxy server + +Basically, your config looks like an object with token: + +```json +{ + "token": "your bot token" +} +``` + ## What next? There are several ways to continue: diff --git a/build.gradle b/build.gradle index c097e27..f55437c 100644 --- a/build.gradle +++ b/build.gradle @@ -24,6 +24,7 @@ dependencies { implementation libs.kotlin implementation libs.tgbotapi + implementation libs.ktor.client.okhttp } application { diff --git a/example.config.json b/example.config.json index 3ea1497..747003b 100644 --- a/example.config.json +++ b/example.config.json @@ -1,3 +1,15 @@ { - "token": "your bot token" + "token": "your bot token", + "client": { + "connectionTimeoutMillis": 10000, + "requestTimeoutMillis": 10000, + "responseTimeoutMillis": 10000, + "proxy": { + "hostname": "127.0.0.1", + "port": 1080, + "type": "socks", + "username": "username", + "password": "password" + } + } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7de1abe..58ed3e5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,12 +2,15 @@ kotlin = "1.8.22" tgbotapi = "9.2.2" +ktor = "2.3.5" [libraries] kotlin = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" } tgbotapi = { module = "dev.inmo:tgbotapi", version.ref = "tgbotapi" } +ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" } + # Libs for classpath kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } kotlin-serialization-plugin = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlin" } diff --git a/src/main/kotlin/App.kt b/src/main/kotlin/App.kt index 9f6978f..4c44e9b 100644 --- a/src/main/kotlin/App.kt +++ b/src/main/kotlin/App.kt @@ -1,8 +1,11 @@ +import config.Config import dev.inmo.tgbotapi.bot.ktor.telegramBot import dev.inmo.tgbotapi.extensions.api.bot.getMe import dev.inmo.tgbotapi.extensions.api.send.reply import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand +import io.ktor.client.* +import io.ktor.client.engine.okhttp.* import java.io.File import kotlinx.coroutines.* import kotlinx.serialization.json.Json @@ -16,7 +19,14 @@ suspend fun main(args: Array) { // decode config val config: Config = json.decodeFromString(Config.serializer(), File(args.first()).readText()) // that is your bot - val bot = telegramBot(config.token) + val bot = telegramBot(config.token) { + client = HttpClient(OkHttp) { + config.client ?.apply { + // setting up telegram bot client + setupConfig() + } + } + } // that is kotlin coroutine scope which will be used in requests and parallel works under the hood val scope = CoroutineScope(Dispatchers.Default) diff --git a/src/main/kotlin/Config.kt b/src/main/kotlin/Config.kt deleted file mode 100644 index 3e26922..0000000 --- a/src/main/kotlin/Config.kt +++ /dev/null @@ -1,6 +0,0 @@ -import kotlinx.serialization.Serializable - -@Serializable -data class Config( - val token: String -) diff --git a/src/main/kotlin/config/Config.kt b/src/main/kotlin/config/Config.kt new file mode 100644 index 0000000..641db94 --- /dev/null +++ b/src/main/kotlin/config/Config.kt @@ -0,0 +1,9 @@ +package config + +import kotlinx.serialization.Serializable + +@Serializable +data class Config( + val token: String, + val client: HttpClientConfig? = null +) diff --git a/src/main/kotlin/config/HttpClientConfig.kt b/src/main/kotlin/config/HttpClientConfig.kt new file mode 100644 index 0000000..e96bdcc --- /dev/null +++ b/src/main/kotlin/config/HttpClientConfig.kt @@ -0,0 +1,89 @@ +package config + +import dev.inmo.tgbotapi.types.MilliSeconds +import io.ktor.client.HttpClientConfig +import io.ktor.client.engine.okhttp.* +import io.ktor.client.plugins.* +import io.ktor.client.request.* +import io.ktor.http.* +import kotlinx.serialization.Serializable +import java.net.Authenticator +import java.net.InetSocketAddress +import java.net.PasswordAuthentication +import java.net.Proxy +import java.util.* +import java.util.concurrent.TimeUnit + +@Serializable +data class HttpClientConfig( + val connectionTimeoutMillis: MilliSeconds? = null, + val requestTimeoutMillis: MilliSeconds? = null, + val responseTimeoutMillis: MilliSeconds? = null, + val proxy: ProxyConfig? = null +) { + @Serializable + data class ProxyConfig( + val hostname: String, + val type: ProxyType = ProxyType.socks, + val port: Int = type.defaultPort, + val username: String? = null, + val password: String? = null + ) { + @Serializable + enum class ProxyType(val defaultPort: Int, val proxyType: Proxy.Type) { + socks(1080, Proxy.Type.SOCKS), + http(3128, Proxy.Type.HTTP), + } + + val socketAddress + get() = InetSocketAddress(hostname, port) + } + + fun HttpClientConfig.setupConfig() { + // setting up telegram bot client + engine { + // Start setup bot client engine configuration + config { + // setting up connection timeout millis + connectionTimeoutMillis ?.let { connectTimeout(it, TimeUnit.MILLISECONDS) } + // setting up write timeout millis + requestTimeoutMillis ?.let { writeTimeout(it, TimeUnit.MILLISECONDS) } + // setting up read timeout millis + responseTimeoutMillis ?.let { readTimeout(it, TimeUnit.MILLISECONDS) } + + // Start setup bot client engine proxy + this@HttpClientConfig.proxy ?.let { proxyConfig -> + proxy( + Proxy( + proxyConfig.type.proxyType, + proxyConfig.socketAddress + ) + ) + + proxyConfig.username ?.let { username -> + when (proxyConfig.type) { + config.HttpClientConfig.ProxyConfig.ProxyType.socks -> { + val passwordAuthentication = PasswordAuthentication( + username, + proxyConfig.password ?.toCharArray() ?: error("For Socks proxy you should use both username and password or do not use authentication at all") + ) + Authenticator.setDefault(object : Authenticator() { + override fun getPasswordAuthentication(): PasswordAuthentication { + return passwordAuthentication + } + }) + } + config.HttpClientConfig.ProxyConfig.ProxyType.http -> { + val passwordSuffix = proxyConfig.password ?.let { ":$it" } + val credentials = Base64.getEncoder().encodeToString("${username}${passwordSuffix}".toByteArray()) + this@setupConfig.defaultRequest { + header(HttpHeaders.ProxyAuthorization, "Basic $credentials") + } + } + } + } + } + } + } + } +}