package dev.inmo.tgbotapi.bot.Ktor import dev.inmo.micro_utils.coroutines.safely import dev.inmo.tgbotapi.bot.BaseRequestsExecutor import dev.inmo.tgbotapi.bot.Ktor.base.* import dev.inmo.tgbotapi.bot.exceptions.newRequestException import dev.inmo.tgbotapi.bot.settings.limiters.EmptyLimiter import dev.inmo.tgbotapi.bot.settings.limiters.RequestLimiter import dev.inmo.tgbotapi.requests.abstracts.Request import dev.inmo.tgbotapi.types.Response import dev.inmo.tgbotapi.utils.* import io.ktor.client.HttpClient import io.ktor.client.features.* import io.ktor.client.statement.readText import kotlinx.serialization.json.Json class KtorRequestsExecutor( telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper, client: HttpClient = HttpClient(), callsFactories: List = emptyList(), excludeDefaultFactories: Boolean = false, private val requestsLimiter: RequestLimiter = EmptyLimiter, private val jsonFormatter: Json = nonstrictJsonFormat ) : BaseRequestsExecutor(telegramAPIUrlsKeeper) { private val callsFactories: List = callsFactories.run { if (!excludeDefaultFactories) { this + listOf(SimpleRequestCallFactory, MultipartRequestCallFactory, DownloadFileRequestCallFactory) } else { this } } private val client = client.config { if (client.feature(HttpTimeout) == null) { install(HttpTimeout) } } override suspend fun execute(request: Request): T { return safely( { e -> throw if (e is ClientRequestException) { val content = e.response ?.readText() ?: throw e val responseObject = jsonFormatter.decodeFromString(Response.serializer(), content) newRequestException( responseObject, content, "Can't get result object from $content" ) } else { e } } ) { requestsLimiter.limit { var result: T? = null for (potentialFactory in callsFactories) { result = potentialFactory.makeCall( client, telegramAPIUrlsKeeper, request, jsonFormatter ) if (result != null) { break } } result ?: error("Can't execute request: $request") } } } override fun close() { client.close() } }