1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2025-12-01 11:55:43 +00:00

Compare commits

..

11 Commits

30 changed files with 236 additions and 69 deletions

1
.gitignore vendored
View File

@@ -9,6 +9,7 @@ settings.xml
.gradle/
build/
out/
bin/
local.properties
kotlin-js-store/

View File

@@ -1,6 +1,26 @@
# TelegramBotAPI changelog
## 26.2.0
## 27.1.0
* `Core`:
* Improve support of local bot api servers files. Next call factories will try to resolve file locally before real call:
* [DownloadFileRequestCallFactory.kt](tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/ktor/base/DownloadFileRequestCallFactory.kt) (for [DownloadFile.kt](tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/DownloadFile.kt) as well)
* [DownloadFileChannelRequestCallFactory.kt](tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/ktor/base/DownloadFileChannelRequestCallFactory.kt) (for [DownloadFileStream.kt](tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/DownloadFileStream.kt) as well)
## 27.0.0
**THIS UPDATE MAY CONTAIN BREAKING CHANGES. IN CASE OF ANY MIGRATION PROBLEMS FEEL FREE TO ASK IN [OUR CHAT](https://t.me/ktgbotapi_chat)**
* `Version`:
* `Kotlin`: `2.1.20` -> `2.2.0`
* `Serialization`: `1.8.1` -> `1.9.0`
* `Ktor`: `3.1.3` -> `3.2.2`
* `MicroUtils`: `0.25.7` -> `0.26.1`
* `KSLog`: `1.4.1` -> `1.5.0`
* `Common`:
* In most `data` classes with non-public constructors has been added `ConsistentCopyVisibility` annotation, preventing
public nature in difference with constructor
* Absence of several API methods for requests has been fixed
## 26.1.0

View File

@@ -120,3 +120,20 @@ suspend fun main() {
You may find examples in [this project](https://github.com/InsanusMokrassar/TelegramBotAPI-examples). Besides, you are
always welcome in our [docs](https://docs.inmo.dev/tgbotapi/index.html) and
[chat](https://t.me/InMoTelegramBotAPIChat).
### Bot API Server Notice
Under the hood, default bots realizations will try to use links
([PathedFile](tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/files/PathedFile.kt)#filePath) as files each
time you are trying to download file from telegram in any way - via saving to file, use stream or download as byte array.
To let bot correctly download files from bot api server, you must:
* Run bot api server locally
to proxy requests for files to the server where bot api server has been hosted
* In case of local bot api server (shared one host machine) you must ensure that access to bot api server has been
granted for your bot. For example, [aiogram/telegram-bot-api](https://hub.docker.com/r/aiogram/telegram-bot-api) image
use `101` UID/GID in linux for user and group as owners. So, your bot must run under user included in `101` group
(like `systemd-journal`) or be `101` UID user (like `systemd-resolve`)
* **OR** Use some reverse proxy (like nginx). It will allow you to broadcast your bots files without linux rights problems
* Set [TelegramAPIUrlsKeeper](tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper.kt)#fileLinkUrlMapper
to map urls to let bot execute requests to your nginx proxy

View File

@@ -9,4 +9,4 @@ kotlin.incremental.js=true
ksp.useKSP2=false
library_group=dev.inmo
library_version=26.2.0
library_version=27.1.0

View File

@@ -8,12 +8,12 @@ javax-activation = "1.1.1"
korlibs = "5.4.0"
uuid = "0.8.4"
ktor = "3.2.1"
ktor = "3.2.2"
ksp = "2.2.0-2.0.2"
kotlin-poet = "2.0.0"
microutils = "0.26.0"
microutils = "0.26.1"
kslog = "1.5.0"
versions = "0.52.0"
@@ -21,8 +21,8 @@ versions = "0.52.0"
github-release-plugin = "2.5.2"
dokka = "2.0.0"
validator = "0.17.0"
nmcp = "1.0.1"
validator = "0.18.1"
nmcp = "1.0.2"
[libraries]

View File

@@ -15,8 +15,8 @@ public final class dev/inmo/tgbotapi/extensions/api/BotBuilder {
}
public final class dev/inmo/tgbotapi/extensions/api/BotBuilderKt {
public static final fun buildBot (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static synthetic fun buildBot$default (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static final fun buildBot (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static synthetic fun buildBot$default (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
}
public final class dev/inmo/tgbotapi/extensions/api/BotExtensionsKt {
@@ -24,17 +24,17 @@ public final class dev/inmo/tgbotapi/extensions/api/BotExtensionsKt {
public static final fun telegramBot (Ldev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper;Lio/ktor/client/engine/HttpClientEngine;Lkotlin/jvm/functions/Function1;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static final fun telegramBot (Ldev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper;Lio/ktor/client/engine/HttpClientEngineFactory;Lkotlin/jvm/functions/Function1;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static final fun telegramBot (Ldev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper;Lkotlin/jvm/functions/Function1;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static final fun telegramBot (Ljava/lang/String;Lio/ktor/client/engine/HttpClientEngine;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static final fun telegramBot (Ljava/lang/String;Lio/ktor/client/engine/HttpClientEngineFactory;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static final fun telegramBot (Ljava/lang/String;Ljava/lang/String;ZLio/ktor/client/HttpClient;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static final fun telegramBot (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static final fun telegramBot (Ljava/lang/String;Lio/ktor/client/engine/HttpClientEngine;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static final fun telegramBot (Ljava/lang/String;Lio/ktor/client/engine/HttpClientEngineFactory;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static final fun telegramBot (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;Lio/ktor/client/HttpClient;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static final fun telegramBot (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static synthetic fun telegramBot$default (Ldev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper;Lio/ktor/client/HttpClient;ILjava/lang/Object;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static synthetic fun telegramBot$default (Ldev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper;Lio/ktor/client/engine/HttpClientEngine;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static synthetic fun telegramBot$default (Ldev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper;Lio/ktor/client/engine/HttpClientEngineFactory;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static synthetic fun telegramBot$default (Ljava/lang/String;Lio/ktor/client/engine/HttpClientEngine;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static synthetic fun telegramBot$default (Ljava/lang/String;Lio/ktor/client/engine/HttpClientEngineFactory;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static synthetic fun telegramBot$default (Ljava/lang/String;Ljava/lang/String;ZLio/ktor/client/HttpClient;ILjava/lang/Object;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static synthetic fun telegramBot$default (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static synthetic fun telegramBot$default (Ljava/lang/String;Lio/ktor/client/engine/HttpClientEngine;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static synthetic fun telegramBot$default (Ljava/lang/String;Lio/ktor/client/engine/HttpClientEngineFactory;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static synthetic fun telegramBot$default (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;Lio/ktor/client/HttpClient;ILjava/lang/Object;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static synthetic fun telegramBot$default (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
}
public final class dev/inmo/tgbotapi/extensions/api/CloseKt {

View File

@@ -16,7 +16,7 @@ import io.ktor.client.engine.*
public data class BotBuilder internal constructor(
var proxy: ProxyConfig? = null,
var ktorClientEngineFactory: HttpClientEngineFactory<HttpClientEngineConfig>? = null,
var ktorClientConfig: (HttpClientConfig<*>.() -> Unit) ? = null
var ktorClientConfig: (HttpClientConfig<*>.() -> Unit) ? = null,
) {
internal fun createHttpClient(): HttpClient = ktorClientEngineFactory ?.let {
HttpClient(
@@ -42,8 +42,14 @@ public fun buildBot(
token: String,
apiUrl: String = telegramBotAPIDefaultUrl,
testServer: Boolean = false,
fileLinkUrlMapper: TelegramAPIUrlsKeeper.(String) -> String = { "${fileBaseUrl}/$it" },
block: BotBuilder.() -> Unit
): TelegramBot = telegramBot(
TelegramAPIUrlsKeeper(token, testServer, apiUrl),
BotBuilder().apply(block).createHttpClient()
urlsKeeper = TelegramAPIUrlsKeeper(
token = token,
testServer = testServer,
hostUrl = apiUrl,
fileLinkUrlMapper = fileLinkUrlMapper
),
client = BotBuilder().apply(block).createHttpClient()
)

View File

@@ -63,22 +63,24 @@ public inline fun telegramBot(
* Allows to create bot using bot [token], [apiUrl] (for custom api servers) and already prepared [client]
*/
@Suppress("NOTHING_TO_INLINE")
public inline fun telegramBot(
public fun telegramBot(
token: String,
apiUrl: String = telegramBotAPIDefaultUrl,
testServer: Boolean = false,
fileLinkUrlMapper: TelegramAPIUrlsKeeper.(String) -> String = { "${fileBaseUrl}/$it" },
client: HttpClient = HttpClient()
): TelegramBot = telegramBot(TelegramAPIUrlsKeeper(token, testServer, apiUrl), client)
): TelegramBot = telegramBot(TelegramAPIUrlsKeeper(token, testServer, apiUrl, fileLinkUrlMapper), client)
@Suppress("NOTHING_TO_INLINE")
public inline fun <T: HttpClientEngineConfig> telegramBot(
public fun <T: HttpClientEngineConfig> telegramBot(
token: String,
clientFactory: HttpClientEngineFactory<T>,
apiUrl: String = telegramBotAPIDefaultUrl,
testServer: Boolean = false,
noinline clientConfig: HttpClientConfig<T>.() -> Unit = {}
fileLinkUrlMapper: TelegramAPIUrlsKeeper.(String) -> String = { "${fileBaseUrl}/$it" },
clientConfig: HttpClientConfig<T>.() -> Unit = {}
): TelegramBot = telegramBot(
TelegramAPIUrlsKeeper(token, testServer, apiUrl),
TelegramAPIUrlsKeeper(token, testServer, apiUrl, fileLinkUrlMapper),
clientFactory,
clientConfig
)
@@ -87,15 +89,15 @@ public inline fun <T: HttpClientEngineConfig> telegramBot(
* Allows to create bot using bot [token] and specify [HttpClientEngine] by passing [clientEngine] param and optionally
* configure [HttpClient] using [clientConfig]
*/
@Suppress("NOTHING_TO_INLINE")
public inline fun telegramBot(
public fun telegramBot(
token: String,
clientEngine: HttpClientEngine,
apiUrl: String = telegramBotAPIDefaultUrl,
testServer: Boolean = false,
noinline clientConfig: HttpClientConfig<*>.() -> Unit = {}
fileLinkUrlMapper: TelegramAPIUrlsKeeper.(String) -> String = { "${fileBaseUrl}/$it" },
clientConfig: HttpClientConfig<*>.() -> Unit = {}
): TelegramBot = telegramBot(
TelegramAPIUrlsKeeper(token, testServer, apiUrl),
TelegramAPIUrlsKeeper(token, testServer, apiUrl, fileLinkUrlMapper),
clientEngine,
clientConfig
)
@@ -104,13 +106,13 @@ public inline fun telegramBot(
* Allows to create bot using bot [token] and [apiUrl] and specify [HttpClientEngine] by configuring [HttpClient] using
* [clientConfig]
*/
@Suppress("NOTHING_TO_INLINE")
public inline fun telegramBot(
public fun telegramBot(
token: String,
apiUrl: String = telegramBotAPIDefaultUrl,
testServer: Boolean = false,
noinline clientConfig: HttpClientConfig<*>.() -> Unit
fileLinkUrlMapper: TelegramAPIUrlsKeeper.(String) -> String = { "${fileBaseUrl}/$it" },
clientConfig: HttpClientConfig<*>.() -> Unit
): TelegramBot = telegramBot(
TelegramAPIUrlsKeeper(token, testServer, apiUrl),
TelegramAPIUrlsKeeper(token, testServer, apiUrl, fileLinkUrlMapper),
clientConfig
)

View File

@@ -128,9 +128,9 @@ public final class dev/inmo/tgbotapi/extensions/behaviour_builder/DefaultBehavio
}
public final class dev/inmo/tgbotapi/extensions/behaviour_builder/TelegramBotWithFSMKt {
public static final fun telegramBotWithBehaviourAndFSM (Ljava/lang/String;Ldev/inmo/tgbotapi/updateshandlers/FlowsUpdatesFilter;Lkotlinx/coroutines/CoroutineScope;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Ldev/inmo/micro_utils/fsm/common/StatesManager;Ljava/util/List;Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourWithFSMStateHandlerHolder;ZLkotlin/jvm/functions/Function3;IZZLjava/lang/Long;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun telegramBotWithBehaviourAndFSM$default (Ljava/lang/String;Ldev/inmo/tgbotapi/updateshandlers/FlowsUpdatesFilter;Lkotlinx/coroutines/CoroutineScope;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Ldev/inmo/micro_utils/fsm/common/StatesManager;Ljava/util/List;Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourWithFSMStateHandlerHolder;ZLkotlin/jvm/functions/Function3;IZZLjava/lang/Long;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public static final fun telegramBotWithBehaviourAndFSMAndStartLongPolling (Ljava/lang/String;Lkotlinx/coroutines/CoroutineScope;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Ldev/inmo/micro_utils/fsm/common/StatesManager;Ljava/util/List;Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourWithFSMStateHandlerHolder;ZLkotlin/jvm/functions/Function3;IZZLjava/lang/Long;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun telegramBotWithBehaviourAndFSMAndStartLongPolling$default (Ljava/lang/String;Lkotlinx/coroutines/CoroutineScope;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Ldev/inmo/micro_utils/fsm/common/StatesManager;Ljava/util/List;Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourWithFSMStateHandlerHolder;ZLkotlin/jvm/functions/Function3;IZZLjava/lang/Long;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public static final fun telegramBotWithBehaviourAndFSM (Ljava/lang/String;Ldev/inmo/tgbotapi/updateshandlers/FlowsUpdatesFilter;Lkotlinx/coroutines/CoroutineScope;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Ldev/inmo/micro_utils/fsm/common/StatesManager;Ljava/util/List;Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourWithFSMStateHandlerHolder;ZLkotlin/jvm/functions/Function3;IZZLjava/lang/Long;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun telegramBotWithBehaviourAndFSM$default (Ljava/lang/String;Ldev/inmo/tgbotapi/updateshandlers/FlowsUpdatesFilter;Lkotlinx/coroutines/CoroutineScope;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Ldev/inmo/micro_utils/fsm/common/StatesManager;Ljava/util/List;Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourWithFSMStateHandlerHolder;ZLkotlin/jvm/functions/Function3;IZZLjava/lang/Long;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public static final fun telegramBotWithBehaviourAndFSMAndStartLongPolling (Ljava/lang/String;Lkotlinx/coroutines/CoroutineScope;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Ldev/inmo/micro_utils/fsm/common/StatesManager;Ljava/util/List;Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourWithFSMStateHandlerHolder;ZLkotlin/jvm/functions/Function3;IZZLjava/lang/Long;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun telegramBotWithBehaviourAndFSMAndStartLongPolling$default (Ljava/lang/String;Lkotlinx/coroutines/CoroutineScope;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Ldev/inmo/micro_utils/fsm/common/StatesManager;Ljava/util/List;Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourWithFSMStateHandlerHolder;ZLkotlin/jvm/functions/Function3;IZZLjava/lang/Long;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
}

View File

@@ -15,6 +15,7 @@ import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.updateHandlerWithMe
import dev.inmo.tgbotapi.types.Seconds
import dev.inmo.tgbotapi.types.update.abstracts.Update
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
import dev.inmo.tgbotapi.utils.telegramBotAPIDefaultUrl
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@@ -56,11 +57,13 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSM(
mediaGroupsDebounceTimeMillis: Long? = 1000L,
subcontextInitialAction: CustomBehaviourContextAndTypeReceiver<BehaviourContext, Unit, Update> = {},
stateInitialAction: CustomBehaviourContextAndTypeReceiver<BehaviourContextWithFSM<T>, Unit, T> = {},
fileLinkUrlMapper: TelegramAPIUrlsKeeper.(String) -> String = { "${fileBaseUrl}/$it" },
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): TelegramBot = telegramBot(
token,
apiUrl,
testServer,
fileLinkUrlMapper,
builder
).apply {
buildBehaviourWithFSMAndStartLongPolling(
@@ -113,12 +116,14 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSMAndStartLongPolling(
mediaGroupsDebounceTimeMillis: Long? = 1000L,
subcontextInitialAction: CustomBehaviourContextAndTypeReceiver<BehaviourContext, Unit, Update> = {},
stateInitialAction: CustomBehaviourContextAndTypeReceiver<BehaviourContextWithFSM<T>, Unit, T> = {},
fileLinkUrlMapper: TelegramAPIUrlsKeeper.(String) -> String = { "${fileBaseUrl}/$it" },
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): Pair<TelegramBot, Job> {
return telegramBot(
token,
apiUrl,
testServer,
fileLinkUrlMapper,
builder
).let {
it to it.buildBehaviourWithFSMAndStartLongPolling (

View File

@@ -134,10 +134,10 @@ public final class dev/inmo/tgbotapi/extensions/behaviour_builder/DefaultCorouti
}
public final class dev/inmo/tgbotapi/extensions/behaviour_builder/TelegramBotKt {
public static final fun telegramBotWithBehaviour (Ljava/lang/String;Ldev/inmo/tgbotapi/updateshandlers/FlowsUpdatesFilter;Lkotlinx/coroutines/CoroutineScope;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun telegramBotWithBehaviour$default (Ljava/lang/String;Ldev/inmo/tgbotapi/updateshandlers/FlowsUpdatesFilter;Lkotlinx/coroutines/CoroutineScope;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public static final fun telegramBotWithBehaviourAndLongPolling (Ljava/lang/String;Lkotlinx/coroutines/CoroutineScope;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ZIZZLjava/lang/Long;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun telegramBotWithBehaviourAndLongPolling$default (Ljava/lang/String;Lkotlinx/coroutines/CoroutineScope;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ZIZZLjava/lang/Long;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public static final fun telegramBotWithBehaviour (Ljava/lang/String;Ldev/inmo/tgbotapi/updateshandlers/FlowsUpdatesFilter;Lkotlinx/coroutines/CoroutineScope;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun telegramBotWithBehaviour$default (Ljava/lang/String;Ldev/inmo/tgbotapi/updateshandlers/FlowsUpdatesFilter;Lkotlinx/coroutines/CoroutineScope;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public static final fun telegramBotWithBehaviourAndLongPolling (Ljava/lang/String;Lkotlinx/coroutines/CoroutineScope;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ZIZZLjava/lang/Long;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun telegramBotWithBehaviourAndLongPolling$default (Ljava/lang/String;Lkotlinx/coroutines/CoroutineScope;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ZIZZLjava/lang/Long;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
}
public final class dev/inmo/tgbotapi/extensions/behaviour_builder/VariantsKt {

View File

@@ -1,3 +1,5 @@
@file:Suppress("unused")
package dev.inmo.tgbotapi.extensions.behaviour_builder
import dev.inmo.micro_utils.coroutines.ExceptionHandler
@@ -9,6 +11,7 @@ import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.updateHandlerWithMe
import dev.inmo.tgbotapi.types.Seconds
import dev.inmo.tgbotapi.types.update.abstracts.Update
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
import dev.inmo.tgbotapi.utils.telegramBotAPIDefaultUrl
import kotlinx.coroutines.*
import kotlin.coroutines.coroutineContext
@@ -35,11 +38,13 @@ suspend fun telegramBotWithBehaviour(
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
testServer: Boolean = false,
subcontextInitialAction: CustomBehaviourContextAndTypeReceiver<BehaviourContext, Unit, Update> = {},
fileLinkUrlMapper: TelegramAPIUrlsKeeper.(String) -> String = { "${fileBaseUrl}/$it" },
block: BehaviourContextReceiver<Unit>
): TelegramBot = telegramBot(
token,
apiUrl,
testServer,
fileLinkUrlMapper,
builder
).apply {
buildBehaviour(
@@ -80,12 +85,14 @@ suspend fun telegramBotWithBehaviourAndLongPolling(
autoSkipTimeoutExceptions: Boolean = true,
mediaGroupsDebounceTimeMillis: Long? = 1000L,
subcontextInitialAction: CustomBehaviourContextAndTypeReceiver<BehaviourContext, Unit, Update> = {},
fileLinkUrlMapper: TelegramAPIUrlsKeeper.(String) -> String = { "${fileBaseUrl}/$it" },
block: BehaviourContextReceiver<Unit>
): Pair<TelegramBot, Job> {
return telegramBot(
token,
apiUrl,
testServer,
fileLinkUrlMapper,
builder
).let {
it to it.buildBehaviourWithLongPolling(

View File

@@ -3,6 +3,8 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling
import dev.inmo.micro_utils.coroutines.SpecialMutableStateFlow
import dev.inmo.micro_utils.coroutines.runCatchingLogging
import dev.inmo.micro_utils.coroutines.subscribeAsync
import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptionsAsync
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
@@ -69,12 +71,16 @@ internal fun <BC : BehaviourContext, T> BC.on(
createSubContextAndDoSynchronouslyWithUpdatesFilter(behaviourContextReceiver = { scenarioReceiver(triggerData) })
}
markerFactory ?.let {
subscribeSafelyWithoutExceptionsAsync(
subscribeAsync(
scope,
{ markerFactory(it.second) },
block = handler
)
} ?: subscribeLoggingDropExceptions(scope) {
logger = Log
) {
runCatchingLogging(logger = Log) {
handler(it)
}
}
} ?: subscribeLoggingDropExceptions(scope, logger = Log) {
scope.launchWithBotLogger {
handler(it)
}

View File

@@ -377,9 +377,9 @@ public final class dev/inmo/tgbotapi/bot/ktor/KtorRequestsExecutorFactoriesKt {
public static final fun createTelegramBotDefaultKtorCallRequestsFactories (Ldev/inmo/kslog/common/KSLog;)Ljava/util/List;
public static synthetic fun createTelegramBotDefaultKtorCallRequestsFactories$default (Ldev/inmo/kslog/common/KSLog;ILjava/lang/Object;)Ljava/util/List;
public static final fun telegramBot (Ldev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper;Lkotlin/jvm/functions/Function1;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static final fun telegramBot (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static final fun telegramBot (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static synthetic fun telegramBot$default (Ldev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static synthetic fun telegramBot$default (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
public static synthetic fun telegramBot$default (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/inmo/tgbotapi/bot/RequestsExecutor;
}
public final class dev/inmo/tgbotapi/bot/ktor/KtorRequestsExecutorKt {
@@ -30491,6 +30491,11 @@ public final class dev/inmo/tgbotapi/utils/ByteReadChannelAllocatorDeserializati
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
}
public final class dev/inmo/tgbotapi/utils/CausedByCancellationKt {
public static final fun causedCancellationException (Ljava/lang/Throwable;)Ljava/util/concurrent/CancellationException;
public static final fun isCausedByCancellation (Ljava/lang/Throwable;)Z
}
public final class dev/inmo/tgbotapi/utils/DefaultKSLogKt {
public static final fun SetDefaultKTgBotAPIKSLog (ZLkotlin/jvm/functions/Function1;)V
public static synthetic fun SetDefaultKTgBotAPIKSLog$default (ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
@@ -30827,10 +30832,12 @@ public final class dev/inmo/tgbotapi/utils/StringFileExtensionKt {
}
public final class dev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;ZLjava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;ZLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public static final field Companion Ldev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper$Companion;
public static final field DEFAULT_URL Ljava/lang/String;
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function2;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;ZLjava/lang/String;Lkotlin/jvm/functions/Function2;)V
public synthetic fun <init> (Ljava/lang/String;ZLjava/lang/String;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun checkWebAppData (Ljava/lang/String;Ljava/lang/String;)Z
public final fun createFileLinkUrl (Ljava/lang/String;)Ljava/lang/String;
public final fun getCommonAPIUrl ()Ljava/lang/String;
@@ -30839,6 +30846,9 @@ public final class dev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper {
public final fun getWebAppDataSecretKeyHash ()Lkorlibs/crypto/Hash;
}
public final class dev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper$Companion {
}
public final class dev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeperKt {
public static final field telegramBotAPIDefaultUrl Ljava/lang/String;
}

View File

@@ -53,11 +53,19 @@ inline fun telegramBot(
/**
* Shortcut for [telegramBot]
*/
@Suppress("NOTHING_TO_INLINE")
inline fun telegramBot(
fun telegramBot(
token: String,
apiUrl: String = telegramBotAPIDefaultUrl,
testServer: Boolean = false,
fileLinkUrlMapper: TelegramAPIUrlsKeeper.(String) -> String = { "${fileBaseUrl}/$it" },
builder: KtorRequestsExecutorBuilder.() -> Unit = {}
): TelegramBot = telegramBot(TelegramAPIUrlsKeeper(token, testServer, apiUrl), builder)
): TelegramBot = telegramBot(
telegramAPIUrlsKeeper = TelegramAPIUrlsKeeper(
token = token,
testServer = testServer,
hostUrl = apiUrl,
fileLinkUrlMapper = fileLinkUrlMapper
),
builder = builder
)

View File

@@ -1,6 +1,7 @@
package dev.inmo.tgbotapi.bot.ktor.base
import dev.inmo.kslog.common.*
import dev.inmo.micro_utils.coroutines.runCatchingLogging
import dev.inmo.tgbotapi.bot.BaseRequestsExecutor
import dev.inmo.tgbotapi.bot.exceptions.BotException
import dev.inmo.tgbotapi.bot.exceptions.CommonBotException
@@ -16,6 +17,7 @@ import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
import io.ktor.client.*
import io.ktor.client.plugins.*
import io.ktor.client.statement.*
import kotlinx.coroutines.CancellationException
import kotlinx.serialization.json.Json
class DefaultKtorRequestsExecutor internal constructor(
@@ -47,7 +49,7 @@ class DefaultKtorRequestsExecutor internal constructor(
}
override suspend fun <T : Any> execute(request: Request<T>): T {
return runCatching {
return runCatchingLogging(logger = logger) {
logger.v { "Start request $request" }
pipelineStepsHolder.onBeforeSearchCallFactory(request, callsFactories)
requestsLimiter.limit(request) {
@@ -95,6 +97,7 @@ class DefaultKtorRequestsExecutor internal constructor(
CommonBotException(cause = e)
} ?: exceptionResult.getOrThrow()
}
is CancellationException,
is BotException -> e
else -> CommonBotException(cause = e)
}.also { newException ->

View File

@@ -1,6 +1,5 @@
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
@@ -22,7 +21,17 @@ object DownloadFileChannelRequestCallFactory : KtorCallFactory {
request: Request<T>,
jsonFormatter: Json
): T? = (request as? DownloadFileStream) ?.let {
val fullUrl = urlsKeeper.createFileLinkUrl(it.filePath)
val bodyChannelAllocator: suspend () -> ByteReadChannel = resolveFile(it.filePath) ?.let {
{
byteReadChannel(it)
}
} ?: let { _ ->
val fullUrl = urlsKeeper.createFileLinkUrl(it.filePath)
return@let {
val response = client.get(fullUrl)
response.bodyAsChannel()
}
}
@Suppress("UNCHECKED_CAST")
ByteReadChannelAllocator {
@@ -30,8 +39,7 @@ object DownloadFileChannelRequestCallFactory : KtorCallFactory {
val outChannel = ByteChannel()
scope.launch {
runCatching {
val response = client.get(fullUrl)
val channel: ByteReadChannel = response.bodyAsChannel()
val channel: ByteReadChannel = bodyChannelAllocator()
channel.copyAndClose(outChannel)
}
scope.cancel()

View File

@@ -1,5 +1,6 @@
package dev.inmo.tgbotapi.bot.ktor.base
import dev.inmo.micro_utils.common.bytes
import dev.inmo.micro_utils.coroutines.safely
import dev.inmo.tgbotapi.bot.ktor.KtorCallFactory
import dev.inmo.tgbotapi.requests.DownloadFile
@@ -19,6 +20,10 @@ object DownloadFileRequestCallFactory : KtorCallFactory {
request: Request<T>,
jsonFormatter: Json,
): T? = (request as? DownloadFile)?.let {
resolveFile(it.filePath) ?.let {
return@makeCall it.bytes() as T // Always ByteArray
}
val fullUrl = urlsKeeper.createFileLinkUrl(it.filePath)
@Suppress("UNCHECKED_CAST")

View File

@@ -0,0 +1,10 @@
package dev.inmo.tgbotapi.bot.ktor.base
import dev.inmo.micro_utils.common.MPPFile
import dev.inmo.micro_utils.ktor.common.input
import io.ktor.utils.io.ByteReadChannel
internal fun byteReadChannel(file: MPPFile): ByteReadChannel {
return ByteReadChannel(file.input())
}
internal expect fun resolveFile(filename: String): MPPFile?

View File

@@ -0,0 +1,18 @@
package dev.inmo.tgbotapi.utils
import kotlinx.coroutines.CancellationException
fun Throwable.causedCancellationException(): CancellationException? {
var current = this
while (current !is CancellationException) {
when {
// It is possible, that API will be changed and cancellation will be caused by something else
current is CancellationException && current.cause == null -> return current
else -> current = current.cause ?: return null
}
}
return current
}
fun Throwable.isCausedByCancellation(): Boolean = causedCancellationException() == null

View File

@@ -19,7 +19,7 @@ private inline fun CreateDefaultKSLogger(
): KSLog {
val filter: MessageFilter? = if (dropCancellationExceptions) {
{ ll, message, e ->
e !is CancellationException
e ?.isCausedByCancellation() != true
}
} else {
null

View File

@@ -4,8 +4,6 @@ import korlibs.crypto.*
import io.ktor.http.decodeURLQueryComponent
import io.ktor.utils.io.core.toByteArray
const val telegramBotAPIDefaultUrl = "https://api.telegram.org"
private inline val String.withoutLastSlash: String
get() {
var correctedUrl = this
@@ -21,7 +19,8 @@ private inline val String.withoutLastSlash: String
class TelegramAPIUrlsKeeper(
token: String,
hostUrl: String = telegramBotAPIDefaultUrl,
urlsSuffixes: String = ""
urlsSuffixes: String = "",
private val fileLinkUrlMapper: TelegramAPIUrlsKeeper.(String) -> String = { "${fileBaseUrl}/$it" }
) {
val webAppDataSecretKeyHash by lazy {
HMAC.hmacSHA256("WebAppData".toByteArray(), token.toByteArray())
@@ -32,10 +31,16 @@ class TelegramAPIUrlsKeeper(
val commonAPIUrl: String
val fileBaseUrl: String
constructor(token: String, testServer: Boolean, hostUrl: String = telegramBotAPIDefaultUrl) : this(
token,
hostUrl,
"/test".takeIf { testServer } ?: ""
constructor(
token: String,
testServer: Boolean,
hostUrl: String = telegramBotAPIDefaultUrl,
fileLinkUrlMapper: TelegramAPIUrlsKeeper.(String) -> String = { "${fileBaseUrl}/$it" }
) : this(
token = token,
hostUrl = hostUrl,
urlsSuffixes = "/test".takeIf { testServer } ?: "",
fileLinkUrlMapper = fileLinkUrlMapper
)
init {
@@ -44,7 +49,7 @@ class TelegramAPIUrlsKeeper(
fileBaseUrl = "$correctedHost/file/bot$token$urlsSuffixes"
}
fun createFileLinkUrl(filePath: String) = "${fileBaseUrl}/$filePath"
fun createFileLinkUrl(filePath: String) = fileLinkUrlMapper(filePath)
/**
* @param rawData Data from [dev.inmo.tgbotapi.webapps.WebApp.initData]
@@ -60,4 +65,10 @@ class TelegramAPIUrlsKeeper(
return HMAC.hmacSHA256(webAppDataSecretKeyHash.bytes, preparedData.toByteArray()).hexLower == hash.lowercase()
}
companion object {
const val DEFAULT_URL = "https://api.telegram.org"
}
}
const val telegramBotAPIDefaultUrl = TelegramAPIUrlsKeeper.DEFAULT_URL

View File

@@ -0,0 +1,5 @@
package dev.inmo.tgbotapi.bot.ktor.base
import dev.inmo.micro_utils.common.MPPFile
internal actual fun resolveFile(filename: String): MPPFile? = null // on JS in common case there is no opportunity to take file based on its name

View File

@@ -0,0 +1,7 @@
package dev.inmo.tgbotapi.bot.ktor.base
import dev.inmo.micro_utils.common.MPPFile
internal actual fun resolveFile(filename: String): MPPFile? = runCatching {
MPPFile(filename).takeIf { it.exists() && it.isFile }
}.getOrElse { null }

View File

@@ -0,0 +1,11 @@
package dev.inmo.tgbotapi.bot.ktor.base
import okio.FileSystem
import okio.Path
import okio.Path.Companion.toPath
internal actual fun resolveFile(filename: String): dev.inmo.micro_utils.common.MPPFile? = runCatching {
with(Path) { filename.toPath() }.takeIf {
FileSystem.SYSTEM.exists(it) && FileSystem.SYSTEM.metadata(it).isRegularFile
}
}.getOrElse { null }

View File

@@ -1,7 +1,8 @@
@file:Suppress("OPT_IN_USAGE")
package dev.inmo.tgbotapi.ksp.processor
import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.getAnnotationsByType
import com.google.devtools.ksp.isAnnotationPresent
import com.google.devtools.ksp.symbol.*
import com.squareup.kotlinpoet.*

View File

@@ -15,6 +15,8 @@ import dev.inmo.tgbotapi.types.update.abstracts.BaseSentMessageUpdate
import dev.inmo.tgbotapi.types.update.abstracts.Update
import dev.inmo.tgbotapi.updateshandlers.*
import dev.inmo.tgbotapi.utils.DefaultKTgBotAPIKSLog
import dev.inmo.tgbotapi.utils.causedCancellationException
import dev.inmo.tgbotapi.utils.isCausedByCancellation
import dev.inmo.tgbotapi.utils.subscribeWithBotLogger
import io.ktor.client.plugins.HttpRequestTimeoutException
import io.ktor.utils.io.CancellationException
@@ -106,7 +108,7 @@ fun TelegramBot.longPollingFlow(
}
}
}.onFailure {
if (it is CancellationException) {
it.causedCancellationException() ?.let {
cancel(it)
}
}

View File

@@ -1,3 +1,5 @@
@file:Suppress("INLINE_CLASS_IN_EXTERNAL_DECLARATION_WARNING")
package dev.inmo.tgbotapi.webapps
import dev.inmo.tgbotapi.types.*

View File

@@ -1,3 +1,5 @@
@file:Suppress("INLINE_CLASS_IN_EXTERNAL_DECLARATION_WARNING")
package dev.inmo.tgbotapi.webapps
import dev.inmo.micro_utils.language_codes.IetfLang