diff --git a/build.gradle b/build.gradle index 1143920..41f99fe 100644 --- a/build.gradle +++ b/build.gradle @@ -33,6 +33,7 @@ dependencies { implementation libs.krontab implementation libs.psql implementation libs.imageboard + implementation libs.ktor.client.okhttp } application { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7a3e4cc..f9e5814 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,6 +6,7 @@ microutils = "0.20.7" imageboard = "2.6.0" krontab = "2.2.1" kslog = "1.2.1" +ktor = "2.3.5" exposed = "0.44.0" psql = "42.6.0" clikt = "4.2.1" @@ -23,6 +24,8 @@ kslog = { module = "dev.inmo:kslog", version.ref = "kslog" } exposed = { module = "org.jetbrains.exposed:exposed-jdbc", version.ref = "exposed" } psql = { module = "org.postgresql:postgresql", version.ref = "psql" } +ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" } + imageboard = { module = "com.github.Kodehawa:imageboard-api", version.ref = "imageboard" } clikt = { module = "com.github.ajalt.clikt:clikt", version.ref = "clikt" } diff --git a/src/main/kotlin/App.kt b/src/main/kotlin/App.kt index 7b952f3..2d7347f 100644 --- a/src/main/kotlin/App.kt +++ b/src/main/kotlin/App.kt @@ -6,12 +6,12 @@ import dev.inmo.micro_utils.repos.cache.full.fullyCached import dev.inmo.micro_utils.repos.exposed.keyvalue.ExposedKeyValueRepo import dev.inmo.micro_utils.repos.exposed.onetomany.ExposedKeyValuesRepo import dev.inmo.micro_utils.repos.mappers.withMapper -import dev.inmo.tgbotapi.bot.ktor.telegramBot import dev.inmo.tgbotapi.extensions.api.bot.getMe import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands import dev.inmo.tgbotapi.extensions.api.delete import dev.inmo.tgbotapi.extensions.api.send.media.* import dev.inmo.tgbotapi.extensions.api.send.reply +import dev.inmo.tgbotapi.extensions.api.telegramBot import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand import dev.inmo.tgbotapi.requests.abstracts.FileUrl @@ -20,6 +20,7 @@ import dev.inmo.tgbotapi.types.chat.ChannelChat import dev.inmo.tgbotapi.types.chat.PrivateChat import dev.inmo.tgbotapi.types.media.TelegramMediaPhoto import dev.inmo.tgbotapi.utils.code +import io.ktor.client.engine.okhttp.* import java.io.File import kotlinx.coroutines.* import kotlinx.coroutines.sync.Mutex @@ -29,6 +30,9 @@ import models.Config import net.kodehawa.lib.imageboards.ImageBoard import net.kodehawa.lib.imageboards.boards.DefaultBoards import net.kodehawa.lib.imageboards.entities.BoardImage +import okhttp3.OkHttpClient + +internal lateinit var InternalBoards: Boards /** * This method by default expects one argument in [args] field: telegram bot configuration @@ -39,7 +43,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, OkHttp) { + config.client ?.apply { setupConfig() } + } + InternalBoards = Boards( + OkHttpClient.Builder().apply { + config.client ?.apply { setupConfig() } + }.build() + ) ImageBoard.setUserAgent("WhoAmI?") diff --git a/src/main/kotlin/Boards.kt b/src/main/kotlin/Boards.kt new file mode 100644 index 0000000..cebced9 --- /dev/null +++ b/src/main/kotlin/Boards.kt @@ -0,0 +1,25 @@ +import net.kodehawa.lib.imageboards.ImageBoard +import net.kodehawa.lib.imageboards.boards.DefaultBoards +import net.kodehawa.lib.imageboards.entities.impl.* +import okhttp3.OkHttpClient +import java.util.concurrent.TimeUnit + + +class Boards( + private val client: OkHttpClient = OkHttpClient.Builder() + .connectTimeout(3, TimeUnit.SECONDS) + .readTimeout(3, TimeUnit.SECONDS) + .build() +) { + val E621 = ImageBoard(client, DefaultBoards.E621, FurryImage::class.java) + val KONACHAN = ImageBoard(client, DefaultBoards.KONACHAN, KonachanImage::class.java) + val RULE34 = ImageBoard(client, DefaultBoards.R34, Rule34Image::class.java) + val YANDERE = ImageBoard(client, DefaultBoards.YANDERE, YandereImage::class.java) + val DANBOORU = ImageBoard(client, DefaultBoards.DANBOORU, DanbooruImage::class.java) + val SAFEBOORU = ImageBoard( + client, DefaultBoards.SAFEBOORU, + SafebooruImage::class.java + ) + val E926 = ImageBoard(client, DefaultBoards.E926, SafeFurryImage::class.java) + val GELBOORU = ImageBoard(client, DefaultBoards.GELBOORU, GelbooruImage::class.java) +} \ No newline at end of file diff --git a/src/main/kotlin/ChatSettings.kt b/src/main/kotlin/ChatSettings.kt index f15b94b..a862ef1 100644 --- a/src/main/kotlin/ChatSettings.kt +++ b/src/main/kotlin/ChatSettings.kt @@ -6,7 +6,6 @@ import kotlinx.serialization.builtins.serializer import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder -import net.kodehawa.lib.imageboards.DefaultImageBoards import net.kodehawa.lib.imageboards.ImageBoard import net.kodehawa.lib.imageboards.boards.DefaultBoards import net.kodehawa.lib.imageboards.entities.BoardImage @@ -29,14 +28,14 @@ data class ChatSettings( val board: ImageBoard<*> get() = when (boardBase) { - DefaultBoards.R34 -> DefaultImageBoards.RULE34 - DefaultBoards.E621 -> DefaultImageBoards.E621 - DefaultBoards.KONACHAN -> DefaultImageBoards.KONACHAN - DefaultBoards.YANDERE -> DefaultImageBoards.YANDERE - DefaultBoards.DANBOORU -> DefaultImageBoards.DANBOORU - DefaultBoards.SAFEBOORU -> DefaultImageBoards.SAFEBOORU - DefaultBoards.GELBOORU -> DefaultImageBoards.GELBOORU - DefaultBoards.E926 -> DefaultImageBoards.E926 + DefaultBoards.R34 -> InternalBoards.RULE34 + DefaultBoards.E621 -> InternalBoards.E621 + DefaultBoards.KONACHAN -> InternalBoards.KONACHAN + DefaultBoards.YANDERE -> InternalBoards.YANDERE + DefaultBoards.DANBOORU -> InternalBoards.DANBOORU + DefaultBoards.SAFEBOORU -> InternalBoards.SAFEBOORU + DefaultBoards.GELBOORU -> InternalBoards.GELBOORU + DefaultBoards.E926 -> InternalBoards.E926 } suspend fun makeRequest(page: Int): List { diff --git a/src/main/kotlin/models/Config.kt b/src/main/kotlin/models/Config.kt index af967d2..29be0aa 100644 --- a/src/main/kotlin/models/Config.kt +++ b/src/main/kotlin/models/Config.kt @@ -5,5 +5,6 @@ import kotlinx.serialization.Serializable @Serializable data class Config( val token: String, - val database: DatabaseConfig + val database: DatabaseConfig, + val client: HttpClientConfig? = null ) diff --git a/src/main/kotlin/models/HttpClientConfig.kt b/src/main/kotlin/models/HttpClientConfig.kt new file mode 100644 index 0000000..2577f98 --- /dev/null +++ b/src/main/kotlin/models/HttpClientConfig.kt @@ -0,0 +1,96 @@ +package models + +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 okhttp3.OkHttpClient +import java.net.* +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 OkHttpClient.Builder.setupConfig() { + // 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 -> + if (proxyConfig.type == 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 if (requestingHost.lowercase() == proxyConfig.hostname.lowercase()) { + passwordAuthentication + } else { + null + } + } + }) + } + } + } + } + + fun HttpClientConfig.setupConfig() { + // setting up telegram bot client + engine { + // Start setup bot client engine configuration + config { + setupConfig() + } + } + + proxy ?.username ?.let { username -> + if (proxy.type == ProxyConfig.ProxyType.http) { + val passwordSuffix = proxy.password ?.let { ":$it" } + val credentials = Base64.getEncoder().encodeToString("${username}${passwordSuffix}".toByteArray()) + this@setupConfig.defaultRequest { + header(HttpHeaders.ProxyAuthorization, "Basic $credentials") + } + } + } + } +} \ No newline at end of file