mirror of
https://github.com/InsanusMokrassar/TelegramBotAPI.git
synced 2024-11-26 03:58:44 +00:00
commit
6fe8e73fca
4
.github/workflows/packages_publishing.yml
vendored
4
.github/workflows/packages_publishing.yml
vendored
@ -8,12 +8,16 @@ jobs:
|
|||||||
- uses: actions/setup-java@v1
|
- uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 11
|
||||||
|
- name: Setup LibCurl
|
||||||
|
run: sudo apt install -y libcurl4-openssl-dev
|
||||||
- name: Rewrite version
|
- name: Rewrite version
|
||||||
run: |
|
run: |
|
||||||
branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`"
|
branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`"
|
||||||
cat gradle.properties | sed -e "s/^library_version=\([0-9\.]*\)/library_version=\1-branch_$branch-build${{ github.run_number }}/" > gradle.properties.tmp
|
cat gradle.properties | sed -e "s/^library_version=\([0-9\.]*\)/library_version=\1-branch_$branch-build${{ github.run_number }}/" > gradle.properties.tmp
|
||||||
rm gradle.properties
|
rm gradle.properties
|
||||||
mv gradle.properties.tmp gradle.properties
|
mv gradle.properties.tmp gradle.properties
|
||||||
|
- name: KotlinSymbolProcessing execution
|
||||||
|
run: ./gradlew ksp
|
||||||
- name: Build
|
- name: Build
|
||||||
run: ./gradlew build
|
run: ./gradlew build
|
||||||
- name: Publish to Gitea
|
- name: Publish to Gitea
|
||||||
|
15
CHANGELOG.md
15
CHANGELOG.md
@ -1,5 +1,20 @@
|
|||||||
# TelegramBotAPI changelog
|
# TelegramBotAPI changelog
|
||||||
|
|
||||||
|
## 7.0.2
|
||||||
|
|
||||||
|
_This update brings experimental support of `linuxX64` and `mingwX64` platforms_
|
||||||
|
|
||||||
|
* `Versions`:
|
||||||
|
* `Kotlin`: `1.8.10` -> `1.8.20`
|
||||||
|
* `MicroUtils`: `0.17.5` -> `0.17.8`
|
||||||
|
* `Ktor`: `2.2.4` -> `2.3.0`
|
||||||
|
* `Core`:
|
||||||
|
* New `RequestsExecutor` - `MultipleClientKtorRequestsExecutor`
|
||||||
|
* Old `KtorRequestsExecutor` has been renamed to `DefaultKtorRequestsExecutor`
|
||||||
|
* `KtorRequestsExecutor` now is `expect class`
|
||||||
|
* On `JS`, `JVM` and `MinGWX64` platforms it is `DefaultKtorRequestsExecutor`
|
||||||
|
* On `LinuxX64` platform it is `MultipleClientKtorRequestsExecutor`
|
||||||
|
|
||||||
## 7.0.1
|
## 7.0.1
|
||||||
|
|
||||||
* `Core`:
|
* `Core`:
|
||||||
|
10
README.md
10
README.md
@ -1,9 +1,11 @@
|
|||||||
# TelegramBotAPI [![Maven Central](https://maven-badges.herokuapp.com/maven-central/dev.inmo/tgbotapi/badge.svg)](https://maven-badges.herokuapp.com/maven-central/dev.inmo/tgbotapi) [![Supported version](https://img.shields.io/badge/Telegram%20Bot%20API-6.6-blue)](https://core.telegram.org/bots/api-changelog#march-9-2023)
|
# TelegramBotAPI [![Maven Central](https://maven-badges.herokuapp.com/maven-central/dev.inmo/tgbotapi/badge.svg)](https://maven-badges.herokuapp.com/maven-central/dev.inmo/tgbotapi) [![Supported version](https://img.shields.io/badge/Telegram%20Bot%20API-6.6-blue)](https://core.telegram.org/bots/api-changelog#march-9-2023)
|
||||||
|
|
||||||
| Docs | [![KDocs](https://img.shields.io/static/v1?label=Dokka&message=KDocs&color=blue&logo=kotlin)](https://tgbotapi.inmo.dev/index.html) [![Mini tutorial](https://img.shields.io/static/v1?label=Bookstack&message=Tutorial&color=blue&logo=bookstack)](https://bookstack.inmo.dev/books/telegrambotapi/chapter/introduction-tutorial) |
|
| Docs | [![KDocs](https://img.shields.io/static/v1?label=Dokka&message=KDocs&color=blue&logo=kotlin)](https://tgbotapi.inmo.dev/index.html) [![Mini tutorial](https://img.shields.io/static/v1?label=Bookstack&message=Tutorial&color=blue&logo=bookstack)](https://bookstack.inmo.dev/books/telegrambotapi/chapter/introduction-tutorial) |
|
||||||
|:---:|:---:|
|
|:----------------------:|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|
|
||||||
| Useful repos | [![Create bot](https://img.shields.io/static/v1?label=Github&message=Template&color=blue&logo=github)](https://github.com/InsanusMokrassar/TelegramBotAPI-bot_template/generate) [![Examples](https://img.shields.io/static/v1?label=Github&message=Examples&color=blue&logo=github)](https://github.com/InsanusMokrassar/TelegramBotAPI-examples/) |
|
| Useful repos | [![Create bot](https://img.shields.io/static/v1?label=Github&message=Template&color=blue&logo=github)](https://github.com/InsanusMokrassar/TelegramBotAPI-bot_template/generate) [![Examples](https://img.shields.io/static/v1?label=Github&message=Examples&color=blue&logo=github)](https://github.com/InsanusMokrassar/TelegramBotAPI-examples/) |
|
||||||
| Misc | [![Awesome Kotlin Badge](https://kotlin.link/awesome-kotlin.svg)](https://github.com/KotlinBy/awesome-kotlin) [![Small survey](https://img.shields.io/static/v1?label=Google&message=Survey&color=blue&logo=google-sheets)](https://docs.google.com/forms/d/e/1FAIpQLSctdJHT_aEniyYT0-IUAEfo1hsIlezX2owlkEAYX4KPl2V2_A/viewform?usp=sf_link) |
|
| Misc | [![Awesome Kotlin Badge](https://kotlin.link/awesome-kotlin.svg)](https://github.com/KotlinBy/awesome-kotlin) [![Small survey](https://img.shields.io/static/v1?label=Google&message=Survey&color=blue&logo=google-sheets)](https://docs.google.com/forms/d/e/1FAIpQLSctdJHT_aEniyYT0-IUAEfo1hsIlezX2owlkEAYX4KPl2V2_A/viewform?usp=sf_link) |
|
||||||
|
| Platforms | ![JVM](https://img.shields.io/badge/JVM-red?style=plastic&logo=openjdk&logoColor=white) ![Js](https://img.shields.io/badge/JavaScript-323330?style=plastic&logo=javascript&logoColor=F7DF1E) |
|
||||||
|
| Experimental Platforms | ![Linux x64](https://img.shields.io/badge/LinuxX64-FCC624?style=plastic&logo=linux&logoColor=black) ![MinGW x64](https://img.shields.io/badge/MinGWX64-black?style=plastic&logo=windows&logoColor=green) |
|
||||||
|
|
||||||
<!--- [![Telegram Channel](./resources/tg_channel_qr.jpg)](https://t.me/ktgbotapi) --->
|
<!--- [![Telegram Channel](./resources/tg_channel_qr.jpg)](https://t.me/ktgbotapi) --->
|
||||||
|
|
||||||
|
@ -6,4 +6,4 @@ kotlin.incremental=true
|
|||||||
kotlin.incremental.js=true
|
kotlin.incremental.js=true
|
||||||
|
|
||||||
library_group=dev.inmo
|
library_group=dev.inmo
|
||||||
library_version=7.0.1
|
library_version=7.0.2
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[versions]
|
[versions]
|
||||||
|
|
||||||
kotlin = "1.8.10"
|
kotlin = "1.8.20"
|
||||||
kotlin-serialization = "1.5.0"
|
kotlin-serialization = "1.5.0"
|
||||||
kotlin-coroutines = "1.6.4"
|
kotlin-coroutines = "1.6.4"
|
||||||
|
|
||||||
@ -8,12 +8,12 @@ javax-activation = "1.1.1"
|
|||||||
|
|
||||||
korlibs = "3.4.0"
|
korlibs = "3.4.0"
|
||||||
uuid = "0.7.0"
|
uuid = "0.7.0"
|
||||||
ktor = "2.2.4"
|
ktor = "2.3.0"
|
||||||
|
|
||||||
ksp = "1.8.10-1.0.9"
|
ksp = "1.8.20-1.0.11"
|
||||||
kotlin-poet = "1.12.0"
|
kotlin-poet = "1.13.0"
|
||||||
|
|
||||||
microutils = "0.17.5"
|
microutils = "0.17.8"
|
||||||
|
|
||||||
github-release-plugin = "2.4.1"
|
github-release-plugin = "2.4.1"
|
||||||
dokka = "1.8.10"
|
dokka = "1.8.10"
|
||||||
@ -31,6 +31,8 @@ kotlin-test-js = { module = "org.jetbrains.kotlin:kotlin-test-js", version.ref =
|
|||||||
|
|
||||||
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
|
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
|
||||||
ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" }
|
ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" }
|
||||||
|
ktor-client-curl = { module = "io.ktor:ktor-client-curl", version.ref = "ktor" }
|
||||||
|
ktor-client-winhttp = { module = "io.ktor:ktor-client-winhttp", version.ref = "ktor" }
|
||||||
ktor-server = { module = "io.ktor:ktor-server", version.ref = "ktor" }
|
ktor-server = { module = "io.ktor:ktor-server", version.ref = "ktor" }
|
||||||
ktor-server-host-common = { module = "io.ktor:ktor-server-host-common", version.ref = "ktor" }
|
ktor-server-host-common = { module = "io.ktor:ktor-server-host-common", version.ref = "ktor" }
|
||||||
|
|
||||||
|
@ -13,6 +13,8 @@ kotlin {
|
|||||||
browser()
|
browser()
|
||||||
nodejs()
|
nodejs()
|
||||||
}
|
}
|
||||||
|
linuxX64()
|
||||||
|
mingwX64()
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
|
@ -93,8 +93,9 @@ interface BehaviourContextWithFSM<T : State> : BehaviourContext, StatesMachine<T
|
|||||||
behaviourContext: BehaviourContext,
|
behaviourContext: BehaviourContext,
|
||||||
handlers: List<BehaviourWithFSMStateHandlerHolder<*, T>>,
|
handlers: List<BehaviourWithFSMStateHandlerHolder<*, T>>,
|
||||||
statesManager: StatesManager<T>,
|
statesManager: StatesManager<T>,
|
||||||
|
fallbackHandler: BehaviourWithFSMStateHandlerHolder<T, T>? = null,
|
||||||
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler()
|
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler()
|
||||||
) = DefaultBehaviourContextWithFSM<T>(behaviourContext, statesManager, handlers, onStateHandlingErrorHandler)
|
) = DefaultBehaviourContextWithFSM<T>(behaviourContext, statesManager, handlers, fallbackHandler, onStateHandlingErrorHandler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,11 +130,12 @@ class DefaultBehaviourContextWithFSM<T : State>(
|
|||||||
private val behaviourContext: BehaviourContext,
|
private val behaviourContext: BehaviourContext,
|
||||||
private val statesManager: StatesManager<T>,
|
private val statesManager: StatesManager<T>,
|
||||||
private val handlers: List<BehaviourWithFSMStateHandlerHolder<*, T>>,
|
private val handlers: List<BehaviourWithFSMStateHandlerHolder<*, T>>,
|
||||||
|
private val fallbackHandler: BehaviourWithFSMStateHandlerHolder<T, T>? = null,
|
||||||
private val onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler()
|
private val onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler()
|
||||||
) : BehaviourContext by behaviourContext, BehaviourContextWithFSM<T> {
|
) : BehaviourContext by behaviourContext, BehaviourContextWithFSM<T> {
|
||||||
private val updatesFlows = mutableMapOf<Any, DefaultBehaviourContextWithFSM<T>>()
|
private val updatesFlows = mutableMapOf<Any, DefaultBehaviourContextWithFSM<T>>()
|
||||||
private val additionalHandlers = mutableListOf<BehaviourWithFSMStateHandlerHolder<*, T>>()
|
private val additionalHandlers = mutableListOf<BehaviourWithFSMStateHandlerHolder<*, T>>()
|
||||||
private var actualHandlersList = additionalHandlers + handlers
|
private var actualHandlersList = additionalHandlers + handlers + listOfNotNull(fallbackHandler)
|
||||||
|
|
||||||
protected val statesJobs = mutableMapOf<T, Job>()
|
protected val statesJobs = mutableMapOf<T, Job>()
|
||||||
protected val statesJobsMutex = Mutex()
|
protected val statesJobsMutex = Mutex()
|
||||||
@ -250,6 +252,7 @@ class DefaultBehaviourContextWithFSM<T : State>(
|
|||||||
behaviourContext.copy(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, triggersHolder),
|
behaviourContext.copy(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, triggersHolder),
|
||||||
handlers,
|
handlers,
|
||||||
statesManager,
|
statesManager,
|
||||||
|
fallbackHandler,
|
||||||
onStateHandlingErrorHandler
|
onStateHandlingErrorHandler
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -265,6 +268,7 @@ class DefaultBehaviourContextWithFSM<T : State>(
|
|||||||
behaviourContext.copy(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, triggersHolder),
|
behaviourContext.copy(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, triggersHolder),
|
||||||
handlers,
|
handlers,
|
||||||
statesManager,
|
statesManager,
|
||||||
|
fallbackHandler,
|
||||||
onStateHandlingErrorHandler
|
onStateHandlingErrorHandler
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
|
|||||||
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
|
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
|
||||||
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
|
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
|
||||||
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
|
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
|
||||||
|
fallbackHandler: BehaviourWithFSMStateHandlerHolder<T, T>? = null,
|
||||||
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
|
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
|
||||||
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
|
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
|
||||||
): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM(
|
): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM(
|
||||||
@ -41,6 +42,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
|
|||||||
),
|
),
|
||||||
presetHandlers,
|
presetHandlers,
|
||||||
statesManager,
|
statesManager,
|
||||||
|
fallbackHandler,
|
||||||
onStateHandlingErrorHandler
|
onStateHandlingErrorHandler
|
||||||
).apply { block() }
|
).apply { block() }
|
||||||
|
|
||||||
@ -59,6 +61,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
|
|||||||
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
|
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
|
||||||
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
|
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
|
||||||
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
|
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
|
||||||
|
fallbackHandler: BehaviourWithFSMStateHandlerHolder<T, T>? = null,
|
||||||
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
|
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
|
||||||
timeoutSeconds: Seconds = 30,
|
timeoutSeconds: Seconds = 30,
|
||||||
autoDisableWebhooks: Boolean = true,
|
autoDisableWebhooks: Boolean = true,
|
||||||
@ -71,6 +74,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
|
|||||||
defaultExceptionsHandler,
|
defaultExceptionsHandler,
|
||||||
statesManager,
|
statesManager,
|
||||||
presetHandlers,
|
presetHandlers,
|
||||||
|
fallbackHandler,
|
||||||
onStateHandlingErrorHandler,
|
onStateHandlingErrorHandler,
|
||||||
block
|
block
|
||||||
).run {
|
).run {
|
||||||
@ -104,6 +108,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
|
|||||||
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
|
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
|
||||||
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
|
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
|
||||||
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
|
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
|
||||||
|
fallbackHandler: BehaviourWithFSMStateHandlerHolder<T, T>? = null,
|
||||||
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
|
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
|
||||||
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
|
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
|
||||||
): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM(
|
): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM(
|
||||||
@ -114,6 +119,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
|
|||||||
),
|
),
|
||||||
presetHandlers,
|
presetHandlers,
|
||||||
statesManager,
|
statesManager,
|
||||||
|
fallbackHandler,
|
||||||
onStateHandlingErrorHandler
|
onStateHandlingErrorHandler
|
||||||
).apply { block() }
|
).apply { block() }
|
||||||
|
|
||||||
@ -137,6 +143,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
|
|||||||
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
|
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
|
||||||
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
|
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
|
||||||
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
|
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
|
||||||
|
fallbackHandler: BehaviourWithFSMStateHandlerHolder<T, T>? = null,
|
||||||
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
|
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
|
||||||
timeoutSeconds: Seconds = 30,
|
timeoutSeconds: Seconds = 30,
|
||||||
autoDisableWebhooks: Boolean = true,
|
autoDisableWebhooks: Boolean = true,
|
||||||
@ -150,6 +157,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
|
|||||||
defaultExceptionsHandler,
|
defaultExceptionsHandler,
|
||||||
statesManager,
|
statesManager,
|
||||||
presetHandlers,
|
presetHandlers,
|
||||||
|
fallbackHandler,
|
||||||
onStateHandlingErrorHandler,
|
onStateHandlingErrorHandler,
|
||||||
block
|
block
|
||||||
).run {
|
).run {
|
||||||
|
@ -46,6 +46,7 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSM(
|
|||||||
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
|
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
|
||||||
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
|
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
|
||||||
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
|
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
|
||||||
|
fallbackHandler: BehaviourWithFSMStateHandlerHolder<T, T>? = null,
|
||||||
testServer: Boolean = false,
|
testServer: Boolean = false,
|
||||||
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
|
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
|
||||||
timeoutSeconds: Seconds = 30,
|
timeoutSeconds: Seconds = 30,
|
||||||
@ -65,6 +66,7 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSM(
|
|||||||
defaultExceptionsHandler,
|
defaultExceptionsHandler,
|
||||||
statesManager,
|
statesManager,
|
||||||
presetHandlers,
|
presetHandlers,
|
||||||
|
fallbackHandler,
|
||||||
onStateHandlingErrorHandler,
|
onStateHandlingErrorHandler,
|
||||||
timeoutSeconds,
|
timeoutSeconds,
|
||||||
autoDisableWebhooks,
|
autoDisableWebhooks,
|
||||||
@ -97,6 +99,7 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSMAndStartLongPolling(
|
|||||||
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
|
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
|
||||||
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
|
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
|
||||||
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
|
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
|
||||||
|
fallbackHandler: BehaviourWithFSMStateHandlerHolder<T, T>? = null,
|
||||||
testServer: Boolean = false,
|
testServer: Boolean = false,
|
||||||
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
|
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
|
||||||
timeoutSeconds: Seconds = 30,
|
timeoutSeconds: Seconds = 30,
|
||||||
@ -116,6 +119,7 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSMAndStartLongPolling(
|
|||||||
defaultExceptionsHandler,
|
defaultExceptionsHandler,
|
||||||
statesManager,
|
statesManager,
|
||||||
presetHandlers,
|
presetHandlers,
|
||||||
|
fallbackHandler,
|
||||||
onStateHandlingErrorHandler,
|
onStateHandlingErrorHandler,
|
||||||
timeoutSeconds,
|
timeoutSeconds,
|
||||||
autoDisableWebhooks,
|
autoDisableWebhooks,
|
||||||
|
@ -5,6 +5,7 @@ package dev.inmo.tgbotapi.extensions.behaviour_builder
|
|||||||
import dev.inmo.micro_utils.coroutines.*
|
import dev.inmo.micro_utils.coroutines.*
|
||||||
import dev.inmo.tgbotapi.bot.TelegramBot
|
import dev.inmo.tgbotapi.bot.TelegramBot
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.handlers_registrar.TriggersHolder
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.handlers_registrar.TriggersHolder
|
||||||
|
import dev.inmo.tgbotapi.types.UpdateIdentifier
|
||||||
import dev.inmo.tgbotapi.types.update.abstracts.Update
|
import dev.inmo.tgbotapi.types.update.abstracts.Update
|
||||||
import dev.inmo.tgbotapi.updateshandlers.*
|
import dev.inmo.tgbotapi.updateshandlers.*
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
@ -71,9 +72,17 @@ class DefaultBehaviourContext(
|
|||||||
private val additionalUpdatesSharedFlow = MutableSharedFlow<Update>(0, broadcastChannelsSize, onBufferOverflow)
|
private val additionalUpdatesSharedFlow = MutableSharedFlow<Update>(0, broadcastChannelsSize, onBufferOverflow)
|
||||||
override val allUpdatesFlow: Flow<Update> = (additionalUpdatesSharedFlow.asSharedFlow()).let {
|
override val allUpdatesFlow: Flow<Update> = (additionalUpdatesSharedFlow.asSharedFlow()).let {
|
||||||
if (upstreamUpdatesFlow != null) {
|
if (upstreamUpdatesFlow != null) {
|
||||||
var lastHandledUpdate = -1L
|
val handledUpdates = mutableSetOf<UpdateIdentifier>()
|
||||||
(it + upstreamUpdatesFlow).filter {
|
(it + upstreamUpdatesFlow).filter {
|
||||||
(it.updateId > lastHandledUpdate).also { passed -> if (passed) { lastHandledUpdate = it.updateId } }
|
val passed = handledUpdates.add(it.updateId)
|
||||||
|
(passed).also { passed ->
|
||||||
|
val needToDropCount = handledUpdates.size - broadcastChannelsSize
|
||||||
|
if (needToDropCount > 0) {
|
||||||
|
handledUpdates.removeAll(
|
||||||
|
handledUpdates.take(needToDropCount).ifEmpty { return@also }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
it
|
it
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
package dev.inmo.tgbotapi.extensions.behaviour_builder
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
||||||
|
actual var defaultCoroutineScopeProvider: () -> CoroutineScope = { CoroutineScope(Dispatchers.Default) }
|
@ -0,0 +1,6 @@
|
|||||||
|
package dev.inmo.tgbotapi.extensions.behaviour_builder
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
||||||
|
actual var defaultCoroutineScopeProvider: () -> CoroutineScope = { CoroutineScope(Dispatchers.Default) }
|
@ -48,11 +48,23 @@ kotlin {
|
|||||||
api libs.javax.activation
|
api libs.javax.activation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
linuxX64Main {
|
||||||
|
dependencies {
|
||||||
|
api libs.ktor.client.curl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mingwX64Main {
|
||||||
|
dependencies {
|
||||||
|
api libs.ktor.client.winhttp
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
add("kspJvm", project(":tgbotapi.ksp"))
|
add("kspCommonMainMetadata", project(":tgbotapi.ksp"))
|
||||||
}
|
}
|
||||||
|
|
||||||
ksp {
|
ksp {
|
||||||
|
@ -1,134 +1,25 @@
|
|||||||
package dev.inmo.tgbotapi.bot.ktor
|
package dev.inmo.tgbotapi.bot.ktor
|
||||||
|
|
||||||
import dev.inmo.micro_utils.coroutines.runCatchingSafely
|
|
||||||
import dev.inmo.micro_utils.coroutines.safely
|
|
||||||
import dev.inmo.tgbotapi.bot.BaseRequestsExecutor
|
import dev.inmo.tgbotapi.bot.BaseRequestsExecutor
|
||||||
import dev.inmo.tgbotapi.bot.TelegramBot
|
|
||||||
import dev.inmo.tgbotapi.bot.exceptions.*
|
|
||||||
import dev.inmo.tgbotapi.bot.ktor.base.*
|
|
||||||
import dev.inmo.tgbotapi.bot.settings.limiters.ExceptionsOnlyLimiter
|
import dev.inmo.tgbotapi.bot.settings.limiters.ExceptionsOnlyLimiter
|
||||||
import dev.inmo.tgbotapi.bot.settings.limiters.RequestLimiter
|
import dev.inmo.tgbotapi.bot.settings.limiters.RequestLimiter
|
||||||
import dev.inmo.tgbotapi.requests.abstracts.Request
|
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
|
||||||
import dev.inmo.tgbotapi.types.Response
|
import dev.inmo.tgbotapi.utils.nonstrictJsonFormat
|
||||||
import dev.inmo.tgbotapi.utils.*
|
import io.ktor.client.*
|
||||||
import io.ktor.client.HttpClient
|
|
||||||
import io.ktor.client.plugins.*
|
|
||||||
import io.ktor.client.statement.bodyAsText
|
|
||||||
import io.ktor.client.statement.readText
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
@RiskFeature
|
/**
|
||||||
fun createTelegramBotDefaultKtorCallRequestsFactories() = listOf(
|
* Represents default [BaseRequestsExecutor] working on [Ktor](https://ktor.io) [HttpClient]
|
||||||
SimpleRequestCallFactory(),
|
*
|
||||||
MultipartRequestCallFactory(),
|
* * On JS, JVM and MingwX64 platforms it is [dev.inmo.tgbotapi.bot.ktor.base.DefaultKtorRequestsExecutor]
|
||||||
DownloadFileRequestCallFactory,
|
* * On LinuxX64 it is [dev.inmo.tgbotapi.bot.ktor.base.MultipleClientKtorRequestsExecutor]
|
||||||
DownloadFileChannelRequestCallFactory
|
*/
|
||||||
)
|
expect class KtorRequestsExecutor (
|
||||||
|
|
||||||
class KtorRequestsExecutor(
|
|
||||||
telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper,
|
telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper,
|
||||||
client: HttpClient = HttpClient(),
|
client: HttpClient = HttpClient(),
|
||||||
callsFactories: List<KtorCallFactory> = emptyList(),
|
callsFactories: List<KtorCallFactory> = emptyList(),
|
||||||
excludeDefaultFactories: Boolean = false,
|
excludeDefaultFactories: Boolean = false,
|
||||||
private val requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter,
|
requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter,
|
||||||
private val jsonFormatter: Json = nonstrictJsonFormat,
|
jsonFormatter: Json = nonstrictJsonFormat,
|
||||||
private val pipelineStepsHolder: KtorPipelineStepsHolder = KtorPipelineStepsHolder
|
pipelineStepsHolder: KtorPipelineStepsHolder = KtorPipelineStepsHolder
|
||||||
) : BaseRequestsExecutor(telegramAPIUrlsKeeper) {
|
) : BaseRequestsExecutor
|
||||||
private val callsFactories: List<KtorCallFactory> = callsFactories.run {
|
|
||||||
if (!excludeDefaultFactories) {
|
|
||||||
this + createTelegramBotDefaultKtorCallRequestsFactories()
|
|
||||||
} else {
|
|
||||||
this
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val client = client.config {
|
|
||||||
if (client.pluginOrNull(HttpTimeout) == null) {
|
|
||||||
install(HttpTimeout)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun <T : Any> execute(request: Request<T>): T {
|
|
||||||
return runCatchingSafely {
|
|
||||||
pipelineStepsHolder.onBeforeSearchCallFactory(request, callsFactories)
|
|
||||||
requestsLimiter.limit(request) {
|
|
||||||
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 {
|
|
||||||
val result = it.exceptionOrNull() ?.let { e ->
|
|
||||||
pipelineStepsHolder.onRequestException(request, e) ?.let { return@let it }
|
|
||||||
|
|
||||||
when (e) {
|
|
||||||
is ClientRequestException -> {
|
|
||||||
val exceptionResult = runCatchingSafely {
|
|
||||||
val content = e.response.bodyAsText()
|
|
||||||
val responseObject = jsonFormatter.decodeFromString(Response.serializer(), content)
|
|
||||||
newRequestException(
|
|
||||||
responseObject,
|
|
||||||
content,
|
|
||||||
"Can't get result object from $content"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
exceptionResult.exceptionOrNull() ?.let {
|
|
||||||
CommonBotException(cause = e)
|
|
||||||
} ?: exceptionResult.getOrThrow()
|
|
||||||
}
|
|
||||||
is BotException -> e
|
|
||||||
else -> CommonBotException(cause = e)
|
|
||||||
}
|
|
||||||
} ?.let { Result.failure(it) } ?: it
|
|
||||||
pipelineStepsHolder.onRequestReturnResult(result, 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,
|
|
||||||
testServer: Boolean = false,
|
|
||||||
builder: KtorRequestsExecutorBuilder.() -> Unit = {}
|
|
||||||
): TelegramBot = telegramBot(TelegramAPIUrlsKeeper(token, testServer, apiUrl), builder)
|
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
package dev.inmo.tgbotapi.bot.ktor
|
||||||
|
|
||||||
|
import dev.inmo.tgbotapi.bot.BaseRequestsExecutor
|
||||||
|
import dev.inmo.tgbotapi.bot.TelegramBot
|
||||||
|
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.utils.*
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
@RiskFeature
|
||||||
|
fun createTelegramBotDefaultKtorCallRequestsFactories() = listOf(
|
||||||
|
SimpleRequestCallFactory(),
|
||||||
|
MultipartRequestCallFactory(),
|
||||||
|
DownloadFileRequestCallFactory,
|
||||||
|
DownloadFileChannelRequestCallFactory
|
||||||
|
)
|
||||||
|
|
||||||
|
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,
|
||||||
|
testServer: Boolean = false,
|
||||||
|
builder: KtorRequestsExecutorBuilder.() -> Unit = {}
|
||||||
|
): TelegramBot = telegramBot(TelegramAPIUrlsKeeper(token, testServer, apiUrl), builder)
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
package dev.inmo.tgbotapi.bot.ktor.base
|
package dev.inmo.tgbotapi.bot.ktor.base
|
||||||
|
|
||||||
import dev.inmo.micro_utils.coroutines.safelyWithResult
|
import dev.inmo.micro_utils.coroutines.runCatchingSafely
|
||||||
import dev.inmo.tgbotapi.bot.ktor.KtorCallFactory
|
import dev.inmo.tgbotapi.bot.ktor.KtorCallFactory
|
||||||
import dev.inmo.tgbotapi.bot.exceptions.newRequestException
|
import dev.inmo.tgbotapi.bot.exceptions.newRequestException
|
||||||
import dev.inmo.tgbotapi.requests.GetUpdates
|
import dev.inmo.tgbotapi.requests.GetUpdates
|
||||||
@ -56,7 +56,7 @@ abstract class AbstractRequestCallFactory : KtorCallFactory {
|
|||||||
val content = response.bodyAsText()
|
val content = response.bodyAsText()
|
||||||
val responseObject = jsonFormatter.decodeFromString(Response.serializer(), content)
|
val responseObject = jsonFormatter.decodeFromString(Response.serializer(), content)
|
||||||
|
|
||||||
return safelyWithResult {
|
return runCatchingSafely {
|
||||||
(responseObject.result?.let {
|
(responseObject.result?.let {
|
||||||
jsonFormatter.decodeFromJsonElement(request.resultDeserializer, it)
|
jsonFormatter.decodeFromJsonElement(request.resultDeserializer, it)
|
||||||
} ?: response.let {
|
} ?: response.let {
|
||||||
|
@ -0,0 +1,100 @@
|
|||||||
|
package dev.inmo.tgbotapi.bot.ktor.base
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.coroutines.runCatchingSafely
|
||||||
|
import dev.inmo.tgbotapi.bot.BaseRequestsExecutor
|
||||||
|
import dev.inmo.tgbotapi.bot.exceptions.BotException
|
||||||
|
import dev.inmo.tgbotapi.bot.exceptions.CommonBotException
|
||||||
|
import dev.inmo.tgbotapi.bot.exceptions.newRequestException
|
||||||
|
import dev.inmo.tgbotapi.bot.ktor.KtorCallFactory
|
||||||
|
import dev.inmo.tgbotapi.bot.ktor.KtorPipelineStepsHolder
|
||||||
|
import dev.inmo.tgbotapi.bot.ktor.createTelegramBotDefaultKtorCallRequestsFactories
|
||||||
|
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.TelegramAPIUrlsKeeper
|
||||||
|
import dev.inmo.tgbotapi.utils.nonstrictJsonFormat
|
||||||
|
import io.ktor.client.*
|
||||||
|
import io.ktor.client.plugins.*
|
||||||
|
import io.ktor.client.statement.*
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
class DefaultKtorRequestsExecutor(
|
||||||
|
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.pluginOrNull(HttpTimeout) == null) {
|
||||||
|
install(HttpTimeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun <T : Any> execute(request: Request<T>): T {
|
||||||
|
return runCatchingSafely {
|
||||||
|
pipelineStepsHolder.onBeforeSearchCallFactory(request, callsFactories)
|
||||||
|
requestsLimiter.limit(request) {
|
||||||
|
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 {
|
||||||
|
val result = it.exceptionOrNull() ?.let { e ->
|
||||||
|
pipelineStepsHolder.onRequestException(request, e) ?.let { return@let it }
|
||||||
|
|
||||||
|
when (e) {
|
||||||
|
is ClientRequestException -> {
|
||||||
|
val exceptionResult = runCatchingSafely {
|
||||||
|
val content = e.response.bodyAsText()
|
||||||
|
val responseObject = jsonFormatter.decodeFromString(Response.serializer(), content)
|
||||||
|
newRequestException(
|
||||||
|
responseObject,
|
||||||
|
content,
|
||||||
|
"Can't get result object from $content"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
exceptionResult.exceptionOrNull() ?.let {
|
||||||
|
CommonBotException(cause = e)
|
||||||
|
} ?: exceptionResult.getOrThrow()
|
||||||
|
}
|
||||||
|
is BotException -> e
|
||||||
|
else -> CommonBotException(cause = e)
|
||||||
|
}
|
||||||
|
} ?.let { Result.failure(it) } ?: it
|
||||||
|
pipelineStepsHolder.onRequestReturnResult(result, request, callsFactories)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
client.close()
|
||||||
|
}
|
||||||
|
}
|
@ -4,8 +4,7 @@ import dev.inmo.tgbotapi.requests.abstracts.*
|
|||||||
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
|
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
|
||||||
import dev.inmo.tgbotapi.utils.mapWithCommonValues
|
import dev.inmo.tgbotapi.utils.mapWithCommonValues
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.request.forms.MultiPartFormDataContent
|
import io.ktor.client.request.forms.*
|
||||||
import io.ktor.client.request.forms.formData
|
|
||||||
import io.ktor.http.Headers
|
import io.ktor.http.Headers
|
||||||
import io.ktor.http.HttpHeaders
|
import io.ktor.http.HttpHeaders
|
||||||
|
|
||||||
|
@ -0,0 +1,118 @@
|
|||||||
|
package dev.inmo.tgbotapi.bot.ktor.base
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.coroutines.runCatchingSafely
|
||||||
|
import dev.inmo.tgbotapi.bot.BaseRequestsExecutor
|
||||||
|
import dev.inmo.tgbotapi.bot.ktor.KtorCallFactory
|
||||||
|
import dev.inmo.tgbotapi.bot.ktor.KtorPipelineStepsHolder
|
||||||
|
import dev.inmo.tgbotapi.bot.settings.limiters.RequestLimiter
|
||||||
|
import dev.inmo.tgbotapi.requests.abstracts.Request
|
||||||
|
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
|
||||||
|
import io.ktor.client.*
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.mapNotNull
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is used in default constructor of [MultipleClientKtorRequestsExecutor] and on all non-native
|
||||||
|
* platforms and MingwX64 should return [client]
|
||||||
|
*
|
||||||
|
* On LinuxX64 it will create copy with Curl engine or throw an exception if engine is different with Curl
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException When pass non Curl-based [HttpClient] on LinuxX64
|
||||||
|
*/
|
||||||
|
internal expect inline fun platformClientCopy(client: HttpClient): HttpClient
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will use its parameters of constructor to create several [DefaultKtorRequestsExecutor] and use them in [execute]
|
||||||
|
* and [close] operations
|
||||||
|
*
|
||||||
|
* This [BaseRequestsExecutor] has been created for LinuxX64 target due to its inability of requests paralleling
|
||||||
|
*
|
||||||
|
* Under the hood on each [execute] it will take [DefaultKtorRequestsExecutor] and mark it as busy, execute
|
||||||
|
* [Request], free up taken [DefaultKtorRequestsExecutor] and return (or throw) the result of execution
|
||||||
|
*
|
||||||
|
* @param requestExecutorsCount Amount of [DefaultKtorRequestsExecutor] which will be created and used under the
|
||||||
|
* hood
|
||||||
|
*/
|
||||||
|
class MultipleClientKtorRequestsExecutor (
|
||||||
|
telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper,
|
||||||
|
callsFactories: List<KtorCallFactory>,
|
||||||
|
excludeDefaultFactories: Boolean,
|
||||||
|
requestsLimiter: RequestLimiter,
|
||||||
|
jsonFormatter: Json,
|
||||||
|
pipelineStepsHolder: KtorPipelineStepsHolder,
|
||||||
|
requestExecutorsCount: Int,
|
||||||
|
clientFactory: () -> HttpClient
|
||||||
|
) : BaseRequestsExecutor(telegramAPIUrlsKeeper) {
|
||||||
|
private val requestExecutors = (0 until requestExecutorsCount).map {
|
||||||
|
DefaultKtorRequestsExecutor(
|
||||||
|
telegramAPIUrlsKeeper,
|
||||||
|
clientFactory(),
|
||||||
|
callsFactories,
|
||||||
|
excludeDefaultFactories,
|
||||||
|
requestsLimiter,
|
||||||
|
jsonFormatter,
|
||||||
|
pipelineStepsHolder
|
||||||
|
)
|
||||||
|
}.toSet()
|
||||||
|
private val freeClients = MutableStateFlow<Set<DefaultKtorRequestsExecutor>>(requestExecutors)
|
||||||
|
private val clientAllocationMutex = Mutex()
|
||||||
|
private val takerFlow = freeClients.mapNotNull {
|
||||||
|
clientAllocationMutex.withLock {
|
||||||
|
freeClients.value.firstOrNull() ?.also {
|
||||||
|
freeClients.value -= it
|
||||||
|
} ?: return@mapNotNull null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper,
|
||||||
|
client: HttpClient,
|
||||||
|
callsFactories: List<KtorCallFactory>,
|
||||||
|
excludeDefaultFactories: Boolean,
|
||||||
|
requestsLimiter: RequestLimiter,
|
||||||
|
jsonFormatter: Json,
|
||||||
|
pipelineStepsHolder: KtorPipelineStepsHolder
|
||||||
|
) : this(
|
||||||
|
telegramAPIUrlsKeeper,
|
||||||
|
callsFactories,
|
||||||
|
excludeDefaultFactories,
|
||||||
|
requestsLimiter,
|
||||||
|
jsonFormatter,
|
||||||
|
pipelineStepsHolder,
|
||||||
|
client.engineConfig.threadsCount,
|
||||||
|
{ platformClientCopy(client) }
|
||||||
|
)
|
||||||
|
|
||||||
|
private suspend fun prepareRequestsExecutor(): DefaultKtorRequestsExecutor {
|
||||||
|
return takerFlow.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun freeRequestsExecutor(client: DefaultKtorRequestsExecutor) {
|
||||||
|
clientAllocationMutex.withLock {
|
||||||
|
freeClients.value += client
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun <T> withRequestExecutor(block: suspend (DefaultKtorRequestsExecutor) -> T): T {
|
||||||
|
val requestsExecutor = prepareRequestsExecutor()
|
||||||
|
val result = runCatchingSafely {
|
||||||
|
block(requestsExecutor)
|
||||||
|
}
|
||||||
|
freeRequestsExecutor(requestsExecutor)
|
||||||
|
return result.getOrThrow()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun <T : Any> execute(request: Request<T>): T = withRequestExecutor {
|
||||||
|
it.execute(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
requestExecutors.forEach {
|
||||||
|
it.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +1,27 @@
|
|||||||
package dev.inmo.tgbotapi.bot.settings.limiters
|
package dev.inmo.tgbotapi.bot.settings.limiters
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.coroutines.runCatchingSafely
|
||||||
import dev.inmo.tgbotapi.bot.exceptions.TooMuchRequestsException
|
import dev.inmo.tgbotapi.bot.exceptions.TooMuchRequestsException
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple limiter which will lock any request when [TooMuchRequestsExceptions] is thrown and rerun request after lock time
|
* Simple limiter which will lock any request when [TooMuchRequestsException] is thrown and rerun request after lock time
|
||||||
*/
|
*/
|
||||||
object ExceptionsOnlyLimiter : RequestLimiter {
|
object ExceptionsOnlyLimiter : RequestLimiter {
|
||||||
override suspend fun <T> limit(block: suspend () -> T): T {
|
override suspend fun <T> limit(block: suspend () -> T): T {
|
||||||
return try {
|
var result: Result<T>? = null
|
||||||
block()
|
while (result == null || result.isFailure) {
|
||||||
} catch (e: TooMuchRequestsException) {
|
result = runCatchingSafely {
|
||||||
delay(e.retryAfter.leftToRetry)
|
block()
|
||||||
limit(block)
|
}.onFailure {
|
||||||
|
it.printStackTrace()
|
||||||
|
if (it is TooMuchRequestsException) {
|
||||||
|
delay(it.retryAfter.leftToRetry)
|
||||||
|
} else {
|
||||||
|
throw it
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return result.getOrThrow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ data class GetChat(
|
|||||||
override val resultDeserializer: DeserializationStrategy<ExtendedChat> = if (chatId is ChatIdWithThreadId) {
|
override val resultDeserializer: DeserializationStrategy<ExtendedChat> = if (chatId is ChatIdWithThreadId) {
|
||||||
ExtendedChatSerializer.BasedOnForumThread(chatId.threadId)
|
ExtendedChatSerializer.BasedOnForumThread(chatId.threadId)
|
||||||
} else {
|
} else {
|
||||||
ExtendedChatSerializer
|
ExtendedChatSerializer.Companion
|
||||||
}
|
}
|
||||||
override val requestSerializer: SerializationStrategy<*>
|
override val requestSerializer: SerializationStrategy<*>
|
||||||
get() = serializer()
|
get() = serializer()
|
||||||
|
@ -5,17 +5,17 @@ import dev.inmo.tgbotapi.types.message.abstracts.Message
|
|||||||
import dev.inmo.tgbotapi.types.message.abstracts.TelegramBotAPIMessageDeserializeOnlySerializer
|
import dev.inmo.tgbotapi.types.message.abstracts.TelegramBotAPIMessageDeserializeOnlySerializer
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable(ExtendedChatSerializer::class)
|
@Serializable(ExtendedChatSerializer.Companion::class)
|
||||||
sealed interface ExtendedChannelChat : ChannelChat, ExtendedPublicChat, ExtendedChatWithUsername {
|
sealed interface ExtendedChannelChat : ChannelChat, ExtendedPublicChat, ExtendedChatWithUsername {
|
||||||
val linkedGroupChatId: IdChatIdentifier?
|
val linkedGroupChatId: IdChatIdentifier?
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable(ExtendedChatSerializer::class)
|
@Serializable(ExtendedChatSerializer.Companion::class)
|
||||||
sealed interface ExtendedGroupChat : GroupChat, ExtendedPublicChat {
|
sealed interface ExtendedGroupChat : GroupChat, ExtendedPublicChat {
|
||||||
val permissions: ChatPermissions
|
val permissions: ChatPermissions
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable(ExtendedChatSerializer::class)
|
@Serializable(ExtendedChatSerializer.Companion::class)
|
||||||
sealed interface ExtendedPrivateChat : PrivateChat, ExtendedChatWithUsername {
|
sealed interface ExtendedPrivateChat : PrivateChat, ExtendedChatWithUsername {
|
||||||
val bio: String
|
val bio: String
|
||||||
val hasPrivateForwards: Boolean
|
val hasPrivateForwards: Boolean
|
||||||
@ -34,7 +34,7 @@ sealed interface ExtendedPublicChat : ExtendedChat, PublicChat {
|
|||||||
val membersHidden: Boolean
|
val membersHidden: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable(ExtendedChatSerializer::class)
|
@Serializable(ExtendedChatSerializer.Companion::class)
|
||||||
sealed interface ExtendedSupergroupChat : SupergroupChat, ExtendedGroupChat, ExtendedChatWithUsername {
|
sealed interface ExtendedSupergroupChat : SupergroupChat, ExtendedGroupChat, ExtendedChatWithUsername {
|
||||||
val slowModeDelay: Long?
|
val slowModeDelay: Long?
|
||||||
val stickerSetName: StickerSetName?
|
val stickerSetName: StickerSetName?
|
||||||
@ -58,15 +58,15 @@ sealed interface ExtendedSupergroupChat : SupergroupChat, ExtendedGroupChat, Ext
|
|||||||
val isAggressiveAntiSpamEnabled: Boolean
|
val isAggressiveAntiSpamEnabled: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable(ExtendedChatSerializer::class)
|
@Serializable(ExtendedChatSerializer.Companion::class)
|
||||||
sealed interface ExtendedForumChat : ExtendedSupergroupChat, ForumChat
|
sealed interface ExtendedForumChat : ExtendedSupergroupChat, ForumChat
|
||||||
|
|
||||||
@Serializable(ExtendedChatSerializer::class)
|
@Serializable(ExtendedChatSerializer.Companion::class)
|
||||||
sealed interface ExtendedChat : Chat {
|
sealed interface ExtendedChat : Chat {
|
||||||
val chatPhoto: ChatPhoto?
|
val chatPhoto: ChatPhoto?
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable(ExtendedChatSerializer::class)
|
@Serializable(ExtendedChatSerializer.Companion::class)
|
||||||
sealed interface ExtendedChatWithUsername : UsernameChat, ExtendedChat {
|
sealed interface ExtendedChatWithUsername : UsernameChat, ExtendedChat {
|
||||||
val activeUsernames: List<Username>
|
val activeUsernames: List<Username>
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,78 @@
|
|||||||
package dev.inmo.tgbotapi.types.message.textsources
|
package dev.inmo.tgbotapi.types.message.textsources
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.serialization.mapper.MapperSerializer
|
||||||
import dev.inmo.micro_utils.serialization.typed_serializer.TypedSerializer
|
import dev.inmo.micro_utils.serialization.typed_serializer.TypedSerializer
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.encoding.CompositeEncoder
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
|
||||||
private val baseSerializers: Map<String, KSerializer<out TextSource>> = mapOf(
|
//private val baseSerializers: Map<String, KSerializer<out TextSource>> = mapOf(
|
||||||
"regular" to RegularTextSource.serializer(),
|
// "regular" to RegularTextSource.serializer(),
|
||||||
"text_link" to TextLinkTextSource.serializer(),
|
// "text_link" to TextLinkTextSource.serializer(),
|
||||||
"code" to CodeTextSource.serializer(),
|
// "code" to CodeTextSource.serializer(),
|
||||||
"url" to URLTextSource.serializer(),
|
// "url" to URLTextSource.serializer(),
|
||||||
"pre" to PreTextSource.serializer(),
|
// "pre" to PreTextSource.serializer(),
|
||||||
"bot_command" to BotCommandTextSource.serializer(),
|
// "bot_command" to BotCommandTextSource.serializer(),
|
||||||
"strikethrough" to StrikethroughTextSource.serializer(),
|
// "strikethrough" to StrikethroughTextSource.serializer(),
|
||||||
"italic" to ItalicTextSource.serializer(),
|
// "italic" to ItalicTextSource.serializer(),
|
||||||
"bold" to BoldTextSource.serializer(),
|
// "bold" to BoldTextSource.serializer(),
|
||||||
"email" to EMailTextSource.serializer(),
|
// "email" to EMailTextSource.serializer(),
|
||||||
"underline" to UnderlineTextSource.serializer(),
|
// "underline" to UnderlineTextSource.serializer(),
|
||||||
"mention" to MentionTextSource.serializer(),
|
// "mention" to MentionTextSource.serializer(),
|
||||||
"phone_number" to PhoneNumberTextSource.serializer(),
|
// "phone_number" to PhoneNumberTextSource.serializer(),
|
||||||
"text_mention" to TextMentionTextSource.serializer(),
|
// "text_mention" to TextMentionTextSource.serializer(),
|
||||||
"hashtag" to HashTagTextSource.serializer(),
|
// "hashtag" to HashTagTextSource.serializer(),
|
||||||
"cashtag" to CashTagTextSource.serializer(),
|
// "cashtag" to CashTagTextSource.serializer(),
|
||||||
"spoiler" to SpoilerTextSource.serializer(),
|
// "spoiler" to SpoilerTextSource.serializer(),
|
||||||
"custom_emoji" to CustomEmojiTextSource.serializer(),
|
// "custom_emoji" to CustomEmojiTextSource.serializer(),
|
||||||
)
|
//)
|
||||||
|
|
||||||
|
object TextSourceSerializer : TypedSerializer<TextSource>(TextSource::class, emptyMap()) {
|
||||||
|
private val baseSerializers: Map<String, KSerializer<out TextSource>> by lazy {
|
||||||
|
mapOf(
|
||||||
|
"regular" to RegularTextSource.serializer(),
|
||||||
|
"text_link" to TextLinkTextSource.serializer(),
|
||||||
|
"code" to CodeTextSource.serializer(),
|
||||||
|
"url" to URLTextSource.serializer(),
|
||||||
|
"pre" to PreTextSource.serializer(),
|
||||||
|
"bot_command" to BotCommandTextSource.serializer(),
|
||||||
|
"strikethrough" to StrikethroughTextSource.serializer(),
|
||||||
|
"italic" to ItalicTextSource.serializer(),
|
||||||
|
"bold" to BoldTextSource.serializer(),
|
||||||
|
"email" to EMailTextSource.serializer(),
|
||||||
|
"underline" to UnderlineTextSource.serializer(),
|
||||||
|
"mention" to MentionTextSource.serializer(),
|
||||||
|
"phone_number" to PhoneNumberTextSource.serializer(),
|
||||||
|
"text_mention" to TextMentionTextSource.serializer(),
|
||||||
|
"hashtag" to HashTagTextSource.serializer(),
|
||||||
|
"cashtag" to CashTagTextSource.serializer(),
|
||||||
|
"spoiler" to SpoilerTextSource.serializer(),
|
||||||
|
"custom_emoji" to CustomEmojiTextSource.serializer(),
|
||||||
|
).also {
|
||||||
|
it.forEach { (k, s) ->
|
||||||
|
include(k, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: TextSource) {
|
||||||
|
baseSerializers // init base serializers
|
||||||
|
super.serialize(encoder, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): TextSource {
|
||||||
|
baseSerializers // init base serializers
|
||||||
|
return super.deserialize(decoder)
|
||||||
|
}
|
||||||
|
|
||||||
object TextSourceSerializer : TypedSerializer<TextSource>(TextSource::class, baseSerializers) {
|
|
||||||
override fun <T: TextSource> include(type: String, serializer: KSerializer<T>) {
|
override fun <T: TextSource> include(type: String, serializer: KSerializer<T>) {
|
||||||
require(type !in baseSerializers.keys)
|
require(type !in serializers.keys)
|
||||||
super.include(type, serializer)
|
super.include(type, serializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun exclude(type: String) {
|
override fun exclude(type: String) {
|
||||||
require(type !in baseSerializers.keys)
|
require(type !in serializers.keys)
|
||||||
super.exclude(type)
|
super.exclude(type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
package dev.inmo.tgbotapi.bot.ktor
|
||||||
|
|
||||||
|
import dev.inmo.tgbotapi.bot.ktor.base.DefaultKtorRequestsExecutor
|
||||||
|
|
||||||
|
actual typealias KtorRequestsExecutor = DefaultKtorRequestsExecutor
|
@ -0,0 +1,13 @@
|
|||||||
|
package dev.inmo.tgbotapi.bot.ktor.base
|
||||||
|
|
||||||
|
import io.ktor.client.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is used in default constructor of [MultipleClientKtorRequestsExecutor] and on all non-native
|
||||||
|
* platforms and MingwX64 should return [client]
|
||||||
|
*
|
||||||
|
* On LinuxX64 it will create copy with Curl engine or throw an exception if engine is different with Curl
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException When pass non Curl-based [HttpClient] on LinuxX64
|
||||||
|
*/
|
||||||
|
internal actual inline fun platformClientCopy(client: HttpClient): HttpClient = client.config { }
|
@ -0,0 +1,5 @@
|
|||||||
|
package dev.inmo.tgbotapi.bot.ktor
|
||||||
|
|
||||||
|
import dev.inmo.tgbotapi.bot.ktor.base.DefaultKtorRequestsExecutor
|
||||||
|
|
||||||
|
actual typealias KtorRequestsExecutor = DefaultKtorRequestsExecutor
|
@ -0,0 +1,13 @@
|
|||||||
|
package dev.inmo.tgbotapi.bot.ktor.base
|
||||||
|
|
||||||
|
import io.ktor.client.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is used in default constructor of [MultipleClientKtorRequestsExecutor] and on all non-native
|
||||||
|
* platforms and MingwX64 should return [client]
|
||||||
|
*
|
||||||
|
* On LinuxX64 it will create copy with Curl engine or throw an exception if engine is different with Curl
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException When pass non Curl-based [HttpClient] on LinuxX64
|
||||||
|
*/
|
||||||
|
internal actual inline fun platformClientCopy(client: HttpClient): HttpClient = client.config { }
|
1
tgbotapi.core/src/linuxX64Main/kotlin/PackageInfo.kt
Normal file
1
tgbotapi.core/src/linuxX64Main/kotlin/PackageInfo.kt
Normal file
@ -0,0 +1 @@
|
|||||||
|
package dev.inmo.tgbotapi
|
@ -0,0 +1,5 @@
|
|||||||
|
package dev.inmo.tgbotapi.bot.ktor
|
||||||
|
|
||||||
|
import dev.inmo.tgbotapi.bot.ktor.base.MultipleClientKtorRequestsExecutor
|
||||||
|
|
||||||
|
actual typealias KtorRequestsExecutor = MultipleClientKtorRequestsExecutor
|
@ -0,0 +1,22 @@
|
|||||||
|
package dev.inmo.tgbotapi.bot.ktor.base
|
||||||
|
|
||||||
|
import io.ktor.client.*
|
||||||
|
import io.ktor.client.engine.curl.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is used in default constructor of [MultipleClientKtorRequestsExecutor] and on all non-native
|
||||||
|
* platforms and MingwX64 should return [client]
|
||||||
|
*
|
||||||
|
* On LinuxX64 it will create copy with Curl engine or throw an exception if engine is different with Curl
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException When pass non Curl-based [HttpClient] on LinuxX64
|
||||||
|
*/
|
||||||
|
internal actual inline fun platformClientCopy(client: HttpClient): HttpClient = (client.engineConfig as? CurlClientEngineConfig) ?.let {
|
||||||
|
lateinit var config: HttpClientConfig<out CurlClientEngineConfig>
|
||||||
|
client.config {
|
||||||
|
config = this as HttpClientConfig<out CurlClientEngineConfig>
|
||||||
|
}.close()
|
||||||
|
HttpClient(Curl) {
|
||||||
|
this.plusAssign(config)
|
||||||
|
}
|
||||||
|
} ?: throw IllegalArgumentException("On LinuxX64 TelegramBotAPI currently support only Curl Ktor HttpClient engine")
|
@ -0,0 +1,9 @@
|
|||||||
|
package dev.inmo.tgbotapi.requests.abstracts
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.common.MPPFile
|
||||||
|
import dev.inmo.micro_utils.ktor.common.input
|
||||||
|
import dev.inmo.tgbotapi.requests.abstracts.MultipartFile
|
||||||
|
|
||||||
|
actual fun MPPFile.asMultipartFile(): MultipartFile = MultipartFile(this.name) {
|
||||||
|
input()
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package dev.inmo.tgbotapi.utils
|
||||||
|
|
||||||
|
import io.ktor.utils.io.ByteReadChannel
|
||||||
|
import io.ktor.utils.io.core.Input
|
||||||
|
import io.ktor.utils.io.readRemaining
|
||||||
|
|
||||||
|
actual suspend fun ByteReadChannel.asInput(): Input = readRemaining()
|
@ -0,0 +1,11 @@
|
|||||||
|
package dev.inmo.tgbotapi.utils
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
//actual typealias MimeType = MimeType
|
||||||
|
|
||||||
|
@Serializable(MimeTypeSerializer::class)
|
||||||
|
actual data class MimeType(
|
||||||
|
actual val raw: String
|
||||||
|
)
|
||||||
|
internal actual fun createMimeType(raw: String): MimeType = MimeType(raw)
|
1
tgbotapi.core/src/mingwX64Main/kotlin/PackageInfo.kt
Normal file
1
tgbotapi.core/src/mingwX64Main/kotlin/PackageInfo.kt
Normal file
@ -0,0 +1 @@
|
|||||||
|
package dev.inmo.tgbotapi
|
@ -0,0 +1,5 @@
|
|||||||
|
package dev.inmo.tgbotapi.bot.ktor
|
||||||
|
|
||||||
|
import dev.inmo.tgbotapi.bot.ktor.base.DefaultKtorRequestsExecutor
|
||||||
|
|
||||||
|
actual typealias KtorRequestsExecutor = DefaultKtorRequestsExecutor
|
@ -0,0 +1,14 @@
|
|||||||
|
package dev.inmo.tgbotapi.bot.ktor.base
|
||||||
|
|
||||||
|
import io.ktor.client.*
|
||||||
|
import io.ktor.client.engine.winhttp.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is used in default constructor of [MultipleClientKtorRequestsExecutor] and on all non-native
|
||||||
|
* platforms and MingwX64 should return [client]
|
||||||
|
*
|
||||||
|
* On LinuxX64 it will create copy with Curl engine or throw an exception if engine is different with Curl
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException When pass non Curl-based [HttpClient] on LinuxX64
|
||||||
|
*/
|
||||||
|
internal actual inline fun platformClientCopy(client: HttpClient): HttpClient = client.config { }
|
@ -0,0 +1,9 @@
|
|||||||
|
package dev.inmo.tgbotapi.requests.abstracts
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.common.MPPFile
|
||||||
|
import dev.inmo.micro_utils.ktor.common.input
|
||||||
|
import dev.inmo.tgbotapi.requests.abstracts.MultipartFile
|
||||||
|
|
||||||
|
actual fun MPPFile.asMultipartFile(): MultipartFile = MultipartFile(this.name) {
|
||||||
|
input()
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package dev.inmo.tgbotapi.utils
|
||||||
|
|
||||||
|
import io.ktor.utils.io.ByteReadChannel
|
||||||
|
import io.ktor.utils.io.core.Input
|
||||||
|
import io.ktor.utils.io.readRemaining
|
||||||
|
|
||||||
|
actual suspend fun ByteReadChannel.asInput(): Input = readRemaining()
|
@ -0,0 +1,11 @@
|
|||||||
|
package dev.inmo.tgbotapi.utils
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
//actual typealias MimeType = MimeType
|
||||||
|
|
||||||
|
@Serializable(MimeTypeSerializer::class)
|
||||||
|
actual data class MimeType(
|
||||||
|
actual val raw: String
|
||||||
|
)
|
||||||
|
internal actual fun createMimeType(raw: String): MimeType = MimeType(raw)
|
@ -160,6 +160,7 @@ import dev.inmo.tgbotapi.types.chat.member.AdministratorChatMember
|
|||||||
import dev.inmo.tgbotapi.types.chat.member.AdministratorChatMemberImpl
|
import dev.inmo.tgbotapi.types.chat.member.AdministratorChatMemberImpl
|
||||||
import dev.inmo.tgbotapi.types.chat.member.BannedChatMember
|
import dev.inmo.tgbotapi.types.chat.member.BannedChatMember
|
||||||
import dev.inmo.tgbotapi.types.chat.member.ChatMember
|
import dev.inmo.tgbotapi.types.chat.member.ChatMember
|
||||||
|
import dev.inmo.tgbotapi.types.chat.member.ChatMemberUpdated
|
||||||
import dev.inmo.tgbotapi.types.chat.member.KickedChatMember
|
import dev.inmo.tgbotapi.types.chat.member.KickedChatMember
|
||||||
import dev.inmo.tgbotapi.types.chat.member.LeftChatMember
|
import dev.inmo.tgbotapi.types.chat.member.LeftChatMember
|
||||||
import dev.inmo.tgbotapi.types.chat.member.LeftChatMemberImpl
|
import dev.inmo.tgbotapi.types.chat.member.LeftChatMemberImpl
|
||||||
@ -659,6 +660,15 @@ public inline fun WithUser.chatMemberOrThrow(): ChatMember = this as
|
|||||||
public inline fun <T> WithUser.ifChatMember(block: (ChatMember) -> T): T? = chatMemberOrNull()
|
public inline fun <T> WithUser.ifChatMember(block: (ChatMember) -> T): T? = chatMemberOrNull()
|
||||||
?.let(block)
|
?.let(block)
|
||||||
|
|
||||||
|
public inline fun WithUser.chatMemberUpdatedOrNull(): ChatMemberUpdated? = this as?
|
||||||
|
dev.inmo.tgbotapi.types.chat.member.ChatMemberUpdated
|
||||||
|
|
||||||
|
public inline fun WithUser.chatMemberUpdatedOrThrow(): ChatMemberUpdated = this as
|
||||||
|
dev.inmo.tgbotapi.types.chat.member.ChatMemberUpdated
|
||||||
|
|
||||||
|
public inline fun <T> WithUser.ifChatMemberUpdated(block: (ChatMemberUpdated) -> T): T? =
|
||||||
|
chatMemberUpdatedOrNull() ?.let(block)
|
||||||
|
|
||||||
public inline fun WithUser.kickedChatMemberOrNull(): KickedChatMember? = this as?
|
public inline fun WithUser.kickedChatMemberOrNull(): KickedChatMember? = this as?
|
||||||
dev.inmo.tgbotapi.types.chat.member.KickedChatMember
|
dev.inmo.tgbotapi.types.chat.member.KickedChatMember
|
||||||
|
|
||||||
|
5
tgbotapi.webapps/src/commonMain/kotlin/PackageInfo.kt
Normal file
5
tgbotapi.webapps/src/commonMain/kotlin/PackageInfo.kt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/**
|
||||||
|
* This file has been created to fix problem with native targets which didn't compile empty project klib file. This problem
|
||||||
|
* leads to impossible project publishing
|
||||||
|
*/
|
||||||
|
package dev.inmo.tgbotapi.webapps
|
5
tgbotapi/src/commonMain/kotlin/PackageInfo.kt
Normal file
5
tgbotapi/src/commonMain/kotlin/PackageInfo.kt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/**
|
||||||
|
* This file has been created to fix problem with native targets which didn't compile empty project klib file. This problem
|
||||||
|
* leads to impossible project publishing
|
||||||
|
*/
|
||||||
|
package dev.inmo.tgbotapi
|
Loading…
Reference in New Issue
Block a user