mirror of
https://github.com/InsanusMokrassar/TelegramBotAPI.git
synced 2024-11-25 19:48:43 +00:00
deprecation of dev.inmo.tgbotapi.bot.Ktor
This commit is contained in:
parent
bf8f4c75f8
commit
2275a61d73
@ -7,6 +7,7 @@ __This update contains including of [Telegram Bot API 6.0](https://core.telegram
|
|||||||
__All the `tgbotapi.extensions.*` packages have been removed__
|
__All the `tgbotapi.extensions.*` packages have been removed__
|
||||||
|
|
||||||
* `Core`:
|
* `Core`:
|
||||||
|
* **`Ktor` package renamed. Migration:** `dev.inmo.tgbotapi.bot.Ktor` -> `dev.inmo.tgbotapi.bot.ktor`
|
||||||
* Constructor of `UnknownInlineKeyboardButton` is not internal and can be created with any `json` ([#563](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/563))
|
* Constructor of `UnknownInlineKeyboardButton` is not internal and can be created with any `json` ([#563](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/563))
|
||||||
* All the interfaces from `dev.inmo.tgbotapi.types.files.abstracts` have been replaced to `dev.inmo.tgbotapi.types.files` and converted to sealed ([#550](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/550))
|
* All the interfaces from `dev.inmo.tgbotapi.types.files.abstracts` have been replaced to `dev.inmo.tgbotapi.types.files` and converted to sealed ([#550](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/550))
|
||||||
* `PassportFile` has been replaced to `dev.inmo.tgbotapi.types.files`
|
* `PassportFile` has been replaced to `dev.inmo.tgbotapi.types.files`
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package dev.inmo.tgbotapi.extensions.api
|
package dev.inmo.tgbotapi.extensions.api
|
||||||
|
|
||||||
import dev.inmo.tgbotapi.bot.Ktor.telegramBot
|
import dev.inmo.tgbotapi.bot.ktor.telegramBot
|
||||||
import dev.inmo.tgbotapi.bot.TelegramBot
|
import dev.inmo.tgbotapi.bot.TelegramBot
|
||||||
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
|
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
|
||||||
import dev.inmo.tgbotapi.utils.telegramBotAPIDefaultUrl
|
import dev.inmo.tgbotapi.utils.telegramBotAPIDefaultUrl
|
||||||
|
@ -5,8 +5,8 @@ import dev.inmo.micro_utils.fsm.common.State
|
|||||||
import dev.inmo.micro_utils.fsm.common.StatesManager
|
import dev.inmo.micro_utils.fsm.common.StatesManager
|
||||||
import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManager
|
import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManager
|
||||||
import dev.inmo.micro_utils.fsm.common.managers.InMemoryDefaultStatesManagerRepo
|
import dev.inmo.micro_utils.fsm.common.managers.InMemoryDefaultStatesManagerRepo
|
||||||
import dev.inmo.tgbotapi.bot.Ktor.KtorRequestsExecutorBuilder
|
import dev.inmo.tgbotapi.bot.ktor.KtorRequestsExecutorBuilder
|
||||||
import dev.inmo.tgbotapi.bot.Ktor.telegramBot
|
import dev.inmo.tgbotapi.bot.ktor.telegramBot
|
||||||
import dev.inmo.tgbotapi.bot.TelegramBot
|
import dev.inmo.tgbotapi.bot.TelegramBot
|
||||||
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling
|
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling
|
||||||
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
|
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package dev.inmo.tgbotapi.extensions.behaviour_builder
|
package dev.inmo.tgbotapi.extensions.behaviour_builder
|
||||||
|
|
||||||
import dev.inmo.micro_utils.coroutines.ExceptionHandler
|
import dev.inmo.micro_utils.coroutines.ExceptionHandler
|
||||||
import dev.inmo.tgbotapi.bot.Ktor.KtorRequestsExecutorBuilder
|
import dev.inmo.tgbotapi.bot.ktor.KtorRequestsExecutorBuilder
|
||||||
import dev.inmo.tgbotapi.bot.Ktor.telegramBot
|
import dev.inmo.tgbotapi.bot.ktor.telegramBot
|
||||||
import dev.inmo.tgbotapi.bot.TelegramBot
|
import dev.inmo.tgbotapi.bot.TelegramBot
|
||||||
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling
|
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling
|
||||||
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
|
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
|
||||||
|
@ -1,16 +1,4 @@
|
|||||||
package dev.inmo.tgbotapi.bot.Ktor
|
package dev.inmo.tgbotapi.bot.Ktor
|
||||||
|
|
||||||
import dev.inmo.micro_utils.common.Optional
|
@Deprecated("Replaced", ReplaceWith("KtorCallFactory", "dev.inmo.tgbotapi.bot.ktor.KtorCallFactory"))
|
||||||
import dev.inmo.tgbotapi.requests.abstracts.Request
|
typealias KtorCallFactory = dev.inmo.tgbotapi.bot.ktor.KtorCallFactory
|
||||||
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
|
|
||||||
import io.ktor.client.HttpClient
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
|
|
||||||
interface KtorCallFactory {
|
|
||||||
suspend fun <T: Any> makeCall(
|
|
||||||
client: HttpClient,
|
|
||||||
urlsKeeper: TelegramAPIUrlsKeeper,
|
|
||||||
request: Request<T>,
|
|
||||||
jsonFormatter: Json
|
|
||||||
): T?
|
|
||||||
}
|
|
||||||
|
@ -1,127 +1,29 @@
|
|||||||
package dev.inmo.tgbotapi.bot.Ktor
|
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.TelegramBot
|
import dev.inmo.tgbotapi.bot.TelegramBot
|
||||||
import dev.inmo.tgbotapi.bot.exceptions.newRequestException
|
|
||||||
import dev.inmo.tgbotapi.bot.ktor.KtorPipelineStepsHolder
|
|
||||||
import dev.inmo.tgbotapi.bot.settings.limiters.ExceptionsOnlyLimiter
|
|
||||||
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 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
|
|
||||||
|
|
||||||
@RiskFeature
|
@RiskFeature
|
||||||
fun createTelegramBotDefaultKtorCallRequestsFactories() = listOf(
|
@Deprecated("Replaced", ReplaceWith("createTelegramBotDefaultKtorCallRequestsFactories", "dev.inmo.tgbotapi.bot.ktor.createTelegramBotDefaultKtorCallRequestsFactories"))
|
||||||
SimpleRequestCallFactory(),
|
fun createTelegramBotDefaultKtorCallRequestsFactories() = dev.inmo.tgbotapi.bot.ktor.createTelegramBotDefaultKtorCallRequestsFactories()
|
||||||
MultipartRequestCallFactory(),
|
|
||||||
DownloadFileRequestCallFactory,
|
|
||||||
DownloadFileChannelRequestCallFactory
|
|
||||||
)
|
|
||||||
|
|
||||||
class KtorRequestsExecutor(
|
@Deprecated("Replaced", ReplaceWith("KtorRequestsExecutor", "dev.inmo.tgbotapi.bot.ktor.KtorRequestsExecutor"))
|
||||||
telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper,
|
typealias KtorRequestsExecutor = dev.inmo.tgbotapi.bot.ktor.KtorRequestsExecutor
|
||||||
client: HttpClient = HttpClient(),
|
|
||||||
callsFactories: List<KtorCallFactory> = emptyList(),
|
|
||||||
excludeDefaultFactories: Boolean = false,
|
|
||||||
private val requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter(),
|
|
||||||
private val jsonFormatter: Json = nonstrictJsonFormat,
|
|
||||||
private val pipelineStepsHolder: KtorPipelineStepsHolder = KtorPipelineStepsHolder
|
|
||||||
) : BaseRequestsExecutor(telegramAPIUrlsKeeper) {
|
|
||||||
private val callsFactories: List<KtorCallFactory> = callsFactories.run {
|
|
||||||
if (!excludeDefaultFactories) {
|
|
||||||
this + createTelegramBotDefaultKtorCallRequestsFactories()
|
|
||||||
} else {
|
|
||||||
this
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val client = client.config {
|
@Deprecated("Replaced", ReplaceWith("KtorRequestsExecutorBuilder", "dev.inmo.tgbotapi.bot.ktor.KtorRequestsExecutorBuilder"))
|
||||||
if (client.feature(HttpTimeout) == null) {
|
typealias KtorRequestsExecutorBuilder = dev.inmo.tgbotapi.bot.ktor.KtorRequestsExecutorBuilder
|
||||||
install(HttpTimeout)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun <T : Any> execute(request: Request<T>): T {
|
|
||||||
return runCatching {
|
|
||||||
safely(
|
|
||||||
{ e ->
|
|
||||||
pipelineStepsHolder.onRequestException(request, e) ?.let { return@safely it }
|
|
||||||
|
|
||||||
throw if (e is ClientRequestException) {
|
|
||||||
val content = e.response.readText()
|
|
||||||
val responseObject = jsonFormatter.decodeFromString(Response.serializer(), content)
|
|
||||||
newRequestException(
|
|
||||||
responseObject,
|
|
||||||
content,
|
|
||||||
"Can't get result object from $content"
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
e
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
pipelineStepsHolder.onBeforeSearchCallFactory(request, callsFactories)
|
|
||||||
requestsLimiter.limit {
|
|
||||||
var result: T? = null
|
|
||||||
lateinit var factoryHandledRequest: KtorCallFactory
|
|
||||||
for (potentialFactory in callsFactories) {
|
|
||||||
pipelineStepsHolder.onBeforeCallFactoryMakeCall(request, potentialFactory)
|
|
||||||
result = potentialFactory.makeCall(
|
|
||||||
client,
|
|
||||||
telegramAPIUrlsKeeper,
|
|
||||||
request,
|
|
||||||
jsonFormatter
|
|
||||||
)
|
|
||||||
result = pipelineStepsHolder.onAfterCallFactoryMakeCall(result, request, potentialFactory)
|
|
||||||
if (result != null) {
|
|
||||||
factoryHandledRequest = potentialFactory
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result ?.let {
|
|
||||||
pipelineStepsHolder.onRequestResultPresented(it, request, factoryHandledRequest, callsFactories)
|
|
||||||
} ?: pipelineStepsHolder.onRequestResultAbsent(request, callsFactories) ?: error("Can't execute request: $request")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.let {
|
|
||||||
pipelineStepsHolder.onRequestReturnResult(it, request, callsFactories)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
client.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class KtorRequestsExecutorBuilder(
|
|
||||||
var telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper
|
|
||||||
) {
|
|
||||||
var client: HttpClient = HttpClient()
|
|
||||||
var callsFactories: List<KtorCallFactory> = emptyList()
|
|
||||||
var excludeDefaultFactories: Boolean = false
|
|
||||||
var requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter()
|
|
||||||
var jsonFormatter: Json = nonstrictJsonFormat
|
|
||||||
|
|
||||||
fun build() = KtorRequestsExecutor(telegramAPIUrlsKeeper, client, callsFactories, excludeDefaultFactories, requestsLimiter, jsonFormatter)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@Deprecated("telegramBot", ReplaceWith("createTelegramBotDefaultKtorCallRequestsFactories", "dev.inmo.tgbotapi.bot.ktor.telegramBot"))
|
||||||
inline fun telegramBot(
|
inline fun telegramBot(
|
||||||
telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper,
|
telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper,
|
||||||
builder: KtorRequestsExecutorBuilder.() -> Unit = {}
|
builder: KtorRequestsExecutorBuilder.() -> Unit = {}
|
||||||
): TelegramBot = KtorRequestsExecutorBuilder(telegramAPIUrlsKeeper).apply(builder).build()
|
): TelegramBot = dev.inmo.tgbotapi.bot.ktor.telegramBot(telegramAPIUrlsKeeper, builder)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shortcut for [telegramBot]
|
* Shortcut for [telegramBot]
|
||||||
*/
|
*/
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
@Deprecated("telegramBot", ReplaceWith("createTelegramBotDefaultKtorCallRequestsFactories", "dev.inmo.tgbotapi.bot.ktor.telegramBot"))
|
||||||
inline fun telegramBot(
|
inline fun telegramBot(
|
||||||
token: String,
|
token: String,
|
||||||
apiUrl: String = telegramBotAPIDefaultUrl,
|
apiUrl: String = telegramBotAPIDefaultUrl,
|
||||||
|
@ -1,80 +1,6 @@
|
|||||||
package dev.inmo.tgbotapi.bot.Ktor.base
|
package dev.inmo.tgbotapi.bot.Ktor.base
|
||||||
|
|
||||||
import dev.inmo.micro_utils.coroutines.safely
|
var defaultUpdateTimeoutForZeroDelay = dev.inmo.tgbotapi.bot.ktor.base.defaultUpdateTimeoutForZeroDelay
|
||||||
import dev.inmo.micro_utils.coroutines.safelyWithResult
|
|
||||||
import dev.inmo.tgbotapi.bot.Ktor.KtorCallFactory
|
|
||||||
import dev.inmo.tgbotapi.bot.exceptions.newRequestException
|
|
||||||
import dev.inmo.tgbotapi.requests.GetUpdates
|
|
||||||
import dev.inmo.tgbotapi.requests.abstracts.Request
|
|
||||||
import dev.inmo.tgbotapi.types.Response
|
|
||||||
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
|
|
||||||
import io.ktor.client.HttpClient
|
|
||||||
import io.ktor.client.call.receive
|
|
||||||
import io.ktor.client.features.timeout
|
|
||||||
import io.ktor.client.request.*
|
|
||||||
import io.ktor.client.statement.HttpResponse
|
|
||||||
import io.ktor.http.ContentType
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import kotlin.collections.set
|
|
||||||
|
|
||||||
var defaultUpdateTimeoutForZeroDelay = 1000L
|
@Deprecated("Replaced", ReplaceWith("AbstractRequestCallFactory", "dev.inmo.tgbotapi.bot.ktor.base.AbstractRequestCallFactory"))
|
||||||
|
typealias AbstractRequestCallFactory = dev.inmo.tgbotapi.bot.ktor.base.AbstractRequestCallFactory
|
||||||
abstract class AbstractRequestCallFactory : KtorCallFactory {
|
|
||||||
private val methodsCache: MutableMap<String, String> = mutableMapOf()
|
|
||||||
override suspend fun <T : Any> makeCall(
|
|
||||||
client: HttpClient,
|
|
||||||
urlsKeeper: TelegramAPIUrlsKeeper,
|
|
||||||
request: Request<T>,
|
|
||||||
jsonFormatter: Json
|
|
||||||
): T? {
|
|
||||||
val preparedBody = prepareCallBody(client, urlsKeeper, request) ?: return null
|
|
||||||
|
|
||||||
client.post<HttpResponse> {
|
|
||||||
url(
|
|
||||||
methodsCache[request.method()] ?: "${urlsKeeper.commonAPIUrl}/${request.method()}".also {
|
|
||||||
methodsCache[request.method()] = it
|
|
||||||
}
|
|
||||||
)
|
|
||||||
accept(ContentType.Application.Json)
|
|
||||||
|
|
||||||
if (request is GetUpdates) {
|
|
||||||
request.timeout?.times(1000L)?.let { customTimeoutMillis ->
|
|
||||||
if (customTimeoutMillis > 0) {
|
|
||||||
timeout {
|
|
||||||
requestTimeoutMillis = customTimeoutMillis
|
|
||||||
socketTimeoutMillis = customTimeoutMillis
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
timeout {
|
|
||||||
requestTimeoutMillis = defaultUpdateTimeoutForZeroDelay
|
|
||||||
socketTimeoutMillis = defaultUpdateTimeoutForZeroDelay
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
body = preparedBody
|
|
||||||
}.let { response ->
|
|
||||||
val content = response.receive<String>()
|
|
||||||
val responseObject = jsonFormatter.decodeFromString(Response.serializer(), content)
|
|
||||||
|
|
||||||
return safelyWithResult {
|
|
||||||
(responseObject.result?.let {
|
|
||||||
jsonFormatter.decodeFromJsonElement(request.resultDeserializer, it)
|
|
||||||
} ?: response.let {
|
|
||||||
throw newRequestException(
|
|
||||||
responseObject,
|
|
||||||
content,
|
|
||||||
"Can't get result object from $content"
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}.getOrThrow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract fun <T : Any> prepareCallBody(
|
|
||||||
client: HttpClient,
|
|
||||||
urlsKeeper: TelegramAPIUrlsKeeper,
|
|
||||||
request: Request<T>
|
|
||||||
): Any?
|
|
||||||
}
|
|
||||||
|
@ -1,42 +1,4 @@
|
|||||||
package dev.inmo.tgbotapi.bot.Ktor.base
|
package dev.inmo.tgbotapi.bot.Ktor.base
|
||||||
|
|
||||||
import dev.inmo.micro_utils.coroutines.*
|
@Deprecated("Replaced", ReplaceWith("DownloadFileChannelRequestCallFactory", "dev.inmo.tgbotapi.bot.ktor.base.DownloadFileRequestCallFactory"))
|
||||||
import dev.inmo.tgbotapi.bot.Ktor.KtorCallFactory
|
typealias DownloadFileChannelRequestCallFactory = dev.inmo.tgbotapi.bot.ktor.base.DownloadFileRequestCallFactory
|
||||||
import dev.inmo.tgbotapi.requests.DownloadFileStream
|
|
||||||
import dev.inmo.tgbotapi.requests.abstracts.Request
|
|
||||||
import dev.inmo.tgbotapi.utils.ByteReadChannelAllocator
|
|
||||||
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
|
|
||||||
import io.ktor.client.HttpClient
|
|
||||||
import io.ktor.client.call.receive
|
|
||||||
import io.ktor.client.request.get
|
|
||||||
import io.ktor.client.statement.HttpStatement
|
|
||||||
import io.ktor.utils.io.*
|
|
||||||
import kotlinx.coroutines.*
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import kotlin.coroutines.coroutineContext
|
|
||||||
|
|
||||||
object DownloadFileChannelRequestCallFactory : KtorCallFactory {
|
|
||||||
override suspend fun <T : Any> makeCall(
|
|
||||||
client: HttpClient,
|
|
||||||
urlsKeeper: TelegramAPIUrlsKeeper,
|
|
||||||
request: Request<T>,
|
|
||||||
jsonFormatter: Json
|
|
||||||
): T? = (request as? DownloadFileStream) ?.let {
|
|
||||||
val fullUrl = urlsKeeper.createFileLinkUrl(it.filePath)
|
|
||||||
|
|
||||||
ByteReadChannelAllocator {
|
|
||||||
val scope = CoroutineScope(currentCoroutineContext() + SupervisorJob())
|
|
||||||
val outChannel = ByteChannel()
|
|
||||||
scope.launch {
|
|
||||||
runCatchingSafely {
|
|
||||||
client.get<HttpStatement>(fullUrl).execute { httpResponse ->
|
|
||||||
val channel: ByteReadChannel = httpResponse.receive()
|
|
||||||
channel.copyAndClose(outChannel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scope.cancel()
|
|
||||||
}
|
|
||||||
outChannel
|
|
||||||
} as T
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,26 +1,4 @@
|
|||||||
package dev.inmo.tgbotapi.bot.Ktor.base
|
package dev.inmo.tgbotapi.bot.Ktor.base
|
||||||
|
|
||||||
import dev.inmo.micro_utils.coroutines.safely
|
@Deprecated("Replaced", ReplaceWith("DownloadFileRequestCallFactory", "dev.inmo.tgbotapi.bot.ktor.base.DownloadFileRequestCallFactory"))
|
||||||
import dev.inmo.tgbotapi.bot.Ktor.KtorCallFactory
|
typealias DownloadFileRequestCallFactory = dev.inmo.tgbotapi.bot.ktor.base.DownloadFileRequestCallFactory
|
||||||
import dev.inmo.tgbotapi.requests.DownloadFile
|
|
||||||
import dev.inmo.tgbotapi.requests.abstracts.Request
|
|
||||||
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
|
|
||||||
import io.ktor.client.HttpClient
|
|
||||||
import io.ktor.client.request.get
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
|
|
||||||
object DownloadFileRequestCallFactory : KtorCallFactory {
|
|
||||||
override suspend fun <T : Any> makeCall(
|
|
||||||
client: HttpClient,
|
|
||||||
urlsKeeper: TelegramAPIUrlsKeeper,
|
|
||||||
request: Request<T>,
|
|
||||||
jsonFormatter: Json
|
|
||||||
): T? = (request as? DownloadFile) ?.let {
|
|
||||||
val fullUrl = urlsKeeper.createFileLinkUrl(it.filePath)
|
|
||||||
|
|
||||||
safely {
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
client.get<ByteArray>(fullUrl) as T // always ByteArray
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,37 +1,4 @@
|
|||||||
package dev.inmo.tgbotapi.bot.Ktor.base
|
package dev.inmo.tgbotapi.bot.Ktor.base
|
||||||
|
|
||||||
import dev.inmo.tgbotapi.requests.abstracts.*
|
@Deprecated("Replaced", ReplaceWith("MultipartRequestCallFactory", "dev.inmo.tgbotapi.bot.ktor.base.MultipartRequestCallFactory"))
|
||||||
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
|
typealias MultipartRequestCallFactory = dev.inmo.tgbotapi.bot.ktor.base.MultipartRequestCallFactory
|
||||||
import dev.inmo.tgbotapi.utils.mapWithCommonValues
|
|
||||||
import io.ktor.client.HttpClient
|
|
||||||
import io.ktor.client.request.forms.MultiPartFormDataContent
|
|
||||||
import io.ktor.client.request.forms.formData
|
|
||||||
import io.ktor.http.Headers
|
|
||||||
import io.ktor.http.HttpHeaders
|
|
||||||
|
|
||||||
class MultipartRequestCallFactory : AbstractRequestCallFactory() {
|
|
||||||
override fun <T : Any> prepareCallBody(
|
|
||||||
client: HttpClient,
|
|
||||||
urlsKeeper: TelegramAPIUrlsKeeper,
|
|
||||||
request: Request<T>
|
|
||||||
): Any? = (request as? MultipartRequest) ?.let { castedRequest ->
|
|
||||||
MultiPartFormDataContent(
|
|
||||||
formData {
|
|
||||||
val params = castedRequest.paramsJson.mapWithCommonValues()
|
|
||||||
for ((key, value) in castedRequest.mediaMap + params) {
|
|
||||||
when (value) {
|
|
||||||
is MultipartFile -> appendInput(
|
|
||||||
key,
|
|
||||||
Headers.build {
|
|
||||||
append(HttpHeaders.ContentDisposition, "filename=${value.filename}")
|
|
||||||
},
|
|
||||||
block = value::input
|
|
||||||
)
|
|
||||||
is FileId -> append(key, value.fileId)
|
|
||||||
else -> append(key, value.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,23 +1,4 @@
|
|||||||
package dev.inmo.tgbotapi.bot.Ktor.base
|
package dev.inmo.tgbotapi.bot.Ktor.base
|
||||||
|
|
||||||
import dev.inmo.tgbotapi.bot.Ktor.KtorCallFactory
|
@Deprecated("Replaced", ReplaceWith("SimpleRequestCallFactory", "dev.inmo.tgbotapi.bot.ktor.base.SimpleRequestCallFactory"))
|
||||||
import dev.inmo.tgbotapi.requests.abstracts.*
|
typealias SimpleRequestCallFactory = dev.inmo.tgbotapi.bot.ktor.base.SimpleRequestCallFactory
|
||||||
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
|
|
||||||
import io.ktor.client.HttpClient
|
|
||||||
import io.ktor.http.ContentType
|
|
||||||
import io.ktor.http.content.TextContent
|
|
||||||
|
|
||||||
class SimpleRequestCallFactory : AbstractRequestCallFactory() {
|
|
||||||
override fun <T : Any> prepareCallBody(
|
|
||||||
client: HttpClient,
|
|
||||||
urlsKeeper: TelegramAPIUrlsKeeper,
|
|
||||||
request: Request<T>
|
|
||||||
): Any? = (request as? SimpleRequest<T>) ?.let { _ ->
|
|
||||||
val content = request.json().toString()
|
|
||||||
|
|
||||||
TextContent(
|
|
||||||
content,
|
|
||||||
ContentType.Application.Json
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
package dev.inmo.tgbotapi.bot.ktor
|
||||||
|
|
||||||
|
import dev.inmo.tgbotapi.requests.abstracts.Request
|
||||||
|
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
interface KtorCallFactory {
|
||||||
|
suspend fun <T: Any> makeCall(
|
||||||
|
client: HttpClient,
|
||||||
|
urlsKeeper: TelegramAPIUrlsKeeper,
|
||||||
|
request: Request<T>,
|
||||||
|
jsonFormatter: Json
|
||||||
|
): T?
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
package dev.inmo.tgbotapi.bot.ktor
|
package dev.inmo.tgbotapi.bot.ktor
|
||||||
|
|
||||||
import dev.inmo.tgbotapi.bot.Ktor.KtorCallFactory
|
|
||||||
import dev.inmo.tgbotapi.requests.abstracts.Request
|
import dev.inmo.tgbotapi.requests.abstracts.Request
|
||||||
|
|
||||||
interface KtorPipelineStepsHolder {
|
interface KtorPipelineStepsHolder {
|
||||||
|
@ -0,0 +1,128 @@
|
|||||||
|
package dev.inmo.tgbotapi.bot.ktor
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.coroutines.safely
|
||||||
|
import dev.inmo.tgbotapi.bot.BaseRequestsExecutor
|
||||||
|
import dev.inmo.tgbotapi.bot.TelegramBot
|
||||||
|
import dev.inmo.tgbotapi.bot.exceptions.newRequestException
|
||||||
|
import dev.inmo.tgbotapi.bot.ktor.base.*
|
||||||
|
import dev.inmo.tgbotapi.bot.settings.limiters.ExceptionsOnlyLimiter
|
||||||
|
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
|
||||||
|
|
||||||
|
@RiskFeature
|
||||||
|
fun createTelegramBotDefaultKtorCallRequestsFactories() = listOf(
|
||||||
|
SimpleRequestCallFactory(),
|
||||||
|
MultipartRequestCallFactory(),
|
||||||
|
DownloadFileRequestCallFactory,
|
||||||
|
DownloadFileChannelRequestCallFactory
|
||||||
|
)
|
||||||
|
|
||||||
|
class KtorRequestsExecutor(
|
||||||
|
telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper,
|
||||||
|
client: HttpClient = HttpClient(),
|
||||||
|
callsFactories: List<KtorCallFactory> = emptyList(),
|
||||||
|
excludeDefaultFactories: Boolean = false,
|
||||||
|
private val requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter(),
|
||||||
|
private val jsonFormatter: Json = nonstrictJsonFormat,
|
||||||
|
private val pipelineStepsHolder: KtorPipelineStepsHolder = KtorPipelineStepsHolder
|
||||||
|
) : BaseRequestsExecutor(telegramAPIUrlsKeeper) {
|
||||||
|
private val callsFactories: List<KtorCallFactory> = callsFactories.run {
|
||||||
|
if (!excludeDefaultFactories) {
|
||||||
|
this + createTelegramBotDefaultKtorCallRequestsFactories()
|
||||||
|
} else {
|
||||||
|
this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val client = client.config {
|
||||||
|
if (client.feature(HttpTimeout) == null) {
|
||||||
|
install(HttpTimeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun <T : Any> execute(request: Request<T>): T {
|
||||||
|
return runCatching {
|
||||||
|
safely(
|
||||||
|
{ e ->
|
||||||
|
pipelineStepsHolder.onRequestException(request, e) ?.let { return@safely it }
|
||||||
|
|
||||||
|
throw if (e is ClientRequestException) {
|
||||||
|
val content = e.response.readText()
|
||||||
|
val responseObject = jsonFormatter.decodeFromString(Response.serializer(), content)
|
||||||
|
newRequestException(
|
||||||
|
responseObject,
|
||||||
|
content,
|
||||||
|
"Can't get result object from $content"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
e
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
pipelineStepsHolder.onBeforeSearchCallFactory(request, callsFactories)
|
||||||
|
requestsLimiter.limit {
|
||||||
|
var result: T? = null
|
||||||
|
lateinit var factoryHandledRequest: KtorCallFactory
|
||||||
|
for (potentialFactory in callsFactories) {
|
||||||
|
pipelineStepsHolder.onBeforeCallFactoryMakeCall(request, potentialFactory)
|
||||||
|
result = potentialFactory.makeCall(
|
||||||
|
client,
|
||||||
|
telegramAPIUrlsKeeper,
|
||||||
|
request,
|
||||||
|
jsonFormatter
|
||||||
|
)
|
||||||
|
result = pipelineStepsHolder.onAfterCallFactoryMakeCall(result, request, potentialFactory)
|
||||||
|
if (result != null) {
|
||||||
|
factoryHandledRequest = potentialFactory
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result ?.let {
|
||||||
|
pipelineStepsHolder.onRequestResultPresented(it, request, factoryHandledRequest, callsFactories)
|
||||||
|
} ?: pipelineStepsHolder.onRequestResultAbsent(request, callsFactories) ?: error("Can't execute request: $request")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.let {
|
||||||
|
pipelineStepsHolder.onRequestReturnResult(it, request, callsFactories)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
client.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class KtorRequestsExecutorBuilder(
|
||||||
|
var telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper
|
||||||
|
) {
|
||||||
|
var client: HttpClient = HttpClient()
|
||||||
|
var callsFactories: List<KtorCallFactory> = emptyList()
|
||||||
|
var excludeDefaultFactories: Boolean = false
|
||||||
|
var requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter()
|
||||||
|
var jsonFormatter: Json = nonstrictJsonFormat
|
||||||
|
|
||||||
|
fun build() = KtorRequestsExecutor(telegramAPIUrlsKeeper, client, callsFactories, excludeDefaultFactories, requestsLimiter, jsonFormatter)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun telegramBot(
|
||||||
|
telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper,
|
||||||
|
builder: KtorRequestsExecutorBuilder.() -> Unit = {}
|
||||||
|
): TelegramBot = KtorRequestsExecutorBuilder(telegramAPIUrlsKeeper).apply(builder).build()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut for [telegramBot]
|
||||||
|
*/
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun telegramBot(
|
||||||
|
token: String,
|
||||||
|
apiUrl: String = telegramBotAPIDefaultUrl,
|
||||||
|
builder: KtorRequestsExecutorBuilder.() -> Unit = {}
|
||||||
|
): TelegramBot = telegramBot(TelegramAPIUrlsKeeper(token, apiUrl), builder)
|
@ -0,0 +1,79 @@
|
|||||||
|
package dev.inmo.tgbotapi.bot.ktor.base
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.coroutines.safelyWithResult
|
||||||
|
import dev.inmo.tgbotapi.bot.ktor.KtorCallFactory
|
||||||
|
import dev.inmo.tgbotapi.bot.exceptions.newRequestException
|
||||||
|
import dev.inmo.tgbotapi.requests.GetUpdates
|
||||||
|
import dev.inmo.tgbotapi.requests.abstracts.Request
|
||||||
|
import dev.inmo.tgbotapi.types.Response
|
||||||
|
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.call.receive
|
||||||
|
import io.ktor.client.features.timeout
|
||||||
|
import io.ktor.client.request.*
|
||||||
|
import io.ktor.client.statement.HttpResponse
|
||||||
|
import io.ktor.http.ContentType
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlin.collections.set
|
||||||
|
|
||||||
|
var defaultUpdateTimeoutForZeroDelay = 1000L
|
||||||
|
|
||||||
|
abstract class AbstractRequestCallFactory : KtorCallFactory {
|
||||||
|
private val methodsCache: MutableMap<String, String> = mutableMapOf()
|
||||||
|
override suspend fun <T : Any> makeCall(
|
||||||
|
client: HttpClient,
|
||||||
|
urlsKeeper: TelegramAPIUrlsKeeper,
|
||||||
|
request: Request<T>,
|
||||||
|
jsonFormatter: Json
|
||||||
|
): T? {
|
||||||
|
val preparedBody = prepareCallBody(client, urlsKeeper, request) ?: return null
|
||||||
|
|
||||||
|
client.post<HttpResponse> {
|
||||||
|
url(
|
||||||
|
methodsCache[request.method()] ?: "${urlsKeeper.commonAPIUrl}/${request.method()}".also {
|
||||||
|
methodsCache[request.method()] = it
|
||||||
|
}
|
||||||
|
)
|
||||||
|
accept(ContentType.Application.Json)
|
||||||
|
|
||||||
|
if (request is GetUpdates) {
|
||||||
|
request.timeout?.times(1000L)?.let { customTimeoutMillis ->
|
||||||
|
if (customTimeoutMillis > 0) {
|
||||||
|
timeout {
|
||||||
|
requestTimeoutMillis = customTimeoutMillis
|
||||||
|
socketTimeoutMillis = customTimeoutMillis
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
timeout {
|
||||||
|
requestTimeoutMillis = defaultUpdateTimeoutForZeroDelay
|
||||||
|
socketTimeoutMillis = defaultUpdateTimeoutForZeroDelay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body = preparedBody
|
||||||
|
}.let { response ->
|
||||||
|
val content = response.receive<String>()
|
||||||
|
val responseObject = jsonFormatter.decodeFromString(Response.serializer(), content)
|
||||||
|
|
||||||
|
return safelyWithResult {
|
||||||
|
(responseObject.result?.let {
|
||||||
|
jsonFormatter.decodeFromJsonElement(request.resultDeserializer, it)
|
||||||
|
} ?: response.let {
|
||||||
|
throw newRequestException(
|
||||||
|
responseObject,
|
||||||
|
content,
|
||||||
|
"Can't get result object from $content"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}.getOrThrow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract fun <T : Any> prepareCallBody(
|
||||||
|
client: HttpClient,
|
||||||
|
urlsKeeper: TelegramAPIUrlsKeeper,
|
||||||
|
request: Request<T>
|
||||||
|
): Any?
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package dev.inmo.tgbotapi.bot.ktor.base
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.coroutines.*
|
||||||
|
import dev.inmo.tgbotapi.bot.ktor.KtorCallFactory
|
||||||
|
import dev.inmo.tgbotapi.requests.DownloadFileStream
|
||||||
|
import dev.inmo.tgbotapi.requests.abstracts.Request
|
||||||
|
import dev.inmo.tgbotapi.utils.ByteReadChannelAllocator
|
||||||
|
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.call.receive
|
||||||
|
import io.ktor.client.request.get
|
||||||
|
import io.ktor.client.statement.HttpStatement
|
||||||
|
import io.ktor.utils.io.*
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
object DownloadFileChannelRequestCallFactory : KtorCallFactory {
|
||||||
|
override suspend fun <T : Any> makeCall(
|
||||||
|
client: HttpClient,
|
||||||
|
urlsKeeper: TelegramAPIUrlsKeeper,
|
||||||
|
request: Request<T>,
|
||||||
|
jsonFormatter: Json
|
||||||
|
): T? = (request as? DownloadFileStream) ?.let {
|
||||||
|
val fullUrl = urlsKeeper.createFileLinkUrl(it.filePath)
|
||||||
|
|
||||||
|
ByteReadChannelAllocator {
|
||||||
|
val scope = CoroutineScope(currentCoroutineContext() + SupervisorJob())
|
||||||
|
val outChannel = ByteChannel()
|
||||||
|
scope.launch {
|
||||||
|
runCatchingSafely {
|
||||||
|
client.get<HttpStatement>(fullUrl).execute { httpResponse ->
|
||||||
|
val channel: ByteReadChannel = httpResponse.receive()
|
||||||
|
channel.copyAndClose(outChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scope.cancel()
|
||||||
|
}
|
||||||
|
outChannel
|
||||||
|
} as T
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package dev.inmo.tgbotapi.bot.ktor.base
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.coroutines.safely
|
||||||
|
import dev.inmo.tgbotapi.bot.ktor.KtorCallFactory
|
||||||
|
import dev.inmo.tgbotapi.requests.DownloadFile
|
||||||
|
import dev.inmo.tgbotapi.requests.abstracts.Request
|
||||||
|
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.request.get
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
object DownloadFileRequestCallFactory : KtorCallFactory {
|
||||||
|
override suspend fun <T : Any> makeCall(
|
||||||
|
client: HttpClient,
|
||||||
|
urlsKeeper: TelegramAPIUrlsKeeper,
|
||||||
|
request: Request<T>,
|
||||||
|
jsonFormatter: Json
|
||||||
|
): T? = (request as? DownloadFile) ?.let {
|
||||||
|
val fullUrl = urlsKeeper.createFileLinkUrl(it.filePath)
|
||||||
|
|
||||||
|
safely {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
client.get<ByteArray>(fullUrl) as T // always ByteArray
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package dev.inmo.tgbotapi.bot.ktor.base
|
||||||
|
|
||||||
|
import dev.inmo.tgbotapi.requests.abstracts.*
|
||||||
|
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
|
||||||
|
import dev.inmo.tgbotapi.utils.mapWithCommonValues
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.request.forms.MultiPartFormDataContent
|
||||||
|
import io.ktor.client.request.forms.formData
|
||||||
|
import io.ktor.http.Headers
|
||||||
|
import io.ktor.http.HttpHeaders
|
||||||
|
|
||||||
|
class MultipartRequestCallFactory : AbstractRequestCallFactory() {
|
||||||
|
override fun <T : Any> prepareCallBody(
|
||||||
|
client: HttpClient,
|
||||||
|
urlsKeeper: TelegramAPIUrlsKeeper,
|
||||||
|
request: Request<T>
|
||||||
|
): Any? = (request as? MultipartRequest) ?.let { castedRequest ->
|
||||||
|
MultiPartFormDataContent(
|
||||||
|
formData {
|
||||||
|
val params = castedRequest.paramsJson.mapWithCommonValues()
|
||||||
|
for ((key, value) in castedRequest.mediaMap + params) {
|
||||||
|
when (value) {
|
||||||
|
is MultipartFile -> appendInput(
|
||||||
|
key,
|
||||||
|
Headers.build {
|
||||||
|
append(HttpHeaders.ContentDisposition, "filename=${value.filename}")
|
||||||
|
},
|
||||||
|
block = value::input
|
||||||
|
)
|
||||||
|
is FileId -> append(key, value.fileId)
|
||||||
|
else -> append(key, value.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package dev.inmo.tgbotapi.bot.ktor.base
|
||||||
|
|
||||||
|
import dev.inmo.tgbotapi.requests.abstracts.*
|
||||||
|
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.http.ContentType
|
||||||
|
import io.ktor.http.content.TextContent
|
||||||
|
|
||||||
|
class SimpleRequestCallFactory : AbstractRequestCallFactory() {
|
||||||
|
override fun <T : Any> prepareCallBody(
|
||||||
|
client: HttpClient,
|
||||||
|
urlsKeeper: TelegramAPIUrlsKeeper,
|
||||||
|
request: Request<T>
|
||||||
|
): Any? = (request as? SimpleRequest<T>) ?.let { _ ->
|
||||||
|
val content = request.json().toString()
|
||||||
|
|
||||||
|
TextContent(
|
||||||
|
content,
|
||||||
|
ContentType.Application.Json
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,11 @@
|
|||||||
package dev.inmo.tgbotapi.bot.multiserver
|
package dev.inmo.tgbotapi.bot.multiserver
|
||||||
|
|
||||||
import dev.inmo.tgbotapi.bot.Ktor.KtorRequestsExecutorBuilder
|
import dev.inmo.tgbotapi.bot.ktor.KtorRequestsExecutorBuilder
|
||||||
import dev.inmo.tgbotapi.bot.Ktor.telegramBot
|
import dev.inmo.tgbotapi.bot.ktor.telegramBot
|
||||||
import dev.inmo.tgbotapi.bot.RequestsExecutor
|
import dev.inmo.tgbotapi.bot.RequestsExecutor
|
||||||
import dev.inmo.tgbotapi.bot.TelegramBot
|
import dev.inmo.tgbotapi.bot.TelegramBot
|
||||||
import dev.inmo.tgbotapi.requests.abstracts.Request
|
import dev.inmo.tgbotapi.requests.abstracts.Request
|
||||||
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
|
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
|
||||||
import dev.inmo.tgbotapi.utils.telegramBotAPIDefaultUrl
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlin.js.JsName
|
import kotlin.js.JsName
|
||||||
import kotlin.jvm.JvmName
|
import kotlin.jvm.JvmName
|
||||||
|
Loading…
Reference in New Issue
Block a user