mirror of
https://github.com/InsanusMokrassar/TelegramBotAPI.git
synced 2025-12-30 18:09:25 +00:00
Compare commits
23 Commits
72d20d2344
...
1.1.4
| Author | SHA1 | Date | |
|---|---|---|---|
| 27a24a18a6 | |||
| 562e8c7429 | |||
| be027efc43 | |||
| e55195b308 | |||
| 14afb59b5f | |||
| 6b23347d56 | |||
| f54843fb60 | |||
| 8a3538d9c7 | |||
| e3db4250bb | |||
| 4643ac906a | |||
| 497974e45c | |||
| 78a00f0efb | |||
| 14ecb9d948 | |||
| c759b9a466 | |||
| 58c1f2ee6a | |||
| 9d16ca3b7e | |||
| 9d40e598f1 | |||
| f1be8bf16e | |||
| 56a8804e99 | |||
| 1e73aac750 | |||
| 82c6eda0b7 | |||
| e9ff93cde1 | |||
| 9b179ea1c9 |
33
CHANGELOG.md
33
CHANGELOG.md
@@ -1,5 +1,38 @@
|
|||||||
# TelegramBotAPI changelog
|
# TelegramBotAPI changelog
|
||||||
|
|
||||||
|
## 1.1.4
|
||||||
|
|
||||||
|
* `Behaviour Builder`:
|
||||||
|
* Improvements in updates passing inside of subcontexts
|
||||||
|
|
||||||
|
## 1.1.3
|
||||||
|
|
||||||
|
* `Behaviour Builder with FSM`:
|
||||||
|
* Typealias `StateHandlingErrorHandler` from `1.1.2` has been deprecated and should be replaced with the new one from microutils
|
||||||
|
|
||||||
|
## 1.1.2
|
||||||
|
|
||||||
|
* `Core`:
|
||||||
|
* Rename of `TelegramAPIUrlsKeeper#checkWebAppLink` -> `TelegramAPIUrlsKeeper#checkWebAppData` (fix of [#591](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/591))
|
||||||
|
* `Behaviour Builder with FSM`:
|
||||||
|
* New typealias `StateHandlingErrorHandler`
|
||||||
|
* `BehaviourBuilderWithFSM` now accepts new parameter `onStateHandlingErrorHandler` which will be used in case if state has not been successfully completed
|
||||||
|
|
||||||
|
## 1.1.1
|
||||||
|
|
||||||
|
* `Versions`:
|
||||||
|
* `MicroUtils.Crypto` will not be provided with that library anymore. Instead, it is recommended to use `Korlibs.Krypto`. You still can add crypto from microutils using next groovy dependency: `dev.inmo:micro_utils.crypto:$micro_utils_version`
|
||||||
|
* `Core`:
|
||||||
|
* Improvements in `TelegramAPIUrlsKeeper#checkWebAppLink`
|
||||||
|
* New field in `TelegramAPIUrlsKeeper#webAppDataSecretKeyHash`
|
||||||
|
* `Behaviour Builder`:
|
||||||
|
* Extension `TelegramBot#buildBehaviour` now returns `BehaviourContext`
|
||||||
|
|
||||||
|
## 1.1.0
|
||||||
|
|
||||||
|
* `Utils`:
|
||||||
|
* New parameter `additionalApplicationEngineEnvironmentConfigurator` in `RequestsExecutor#setWebhookInfoAndStartListenWebhooks` and `startListenWebhooks`
|
||||||
|
|
||||||
## 1.0.1
|
## 1.0.1
|
||||||
|
|
||||||
* `Versions`:
|
* `Versions`:
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ kotlin.incremental.js=true
|
|||||||
kotlin_version=1.6.21
|
kotlin_version=1.6.21
|
||||||
kotlin_coroutines_version=1.6.1
|
kotlin_coroutines_version=1.6.1
|
||||||
kotlin_serialisation_runtime_version=1.3.3
|
kotlin_serialisation_runtime_version=1.3.3
|
||||||
klock_version=2.7.0
|
korlibs_version=2.7.0
|
||||||
uuid_version=0.4.0
|
uuid_version=0.4.0
|
||||||
ktor_version=2.0.1
|
ktor_version=2.0.1
|
||||||
|
|
||||||
micro_utils_version=0.10.4
|
micro_utils_version=0.10.5
|
||||||
|
|
||||||
javax_activation_version=1.1.1
|
javax_activation_version=1.1.1
|
||||||
|
|
||||||
@@ -20,6 +20,6 @@ javax_activation_version=1.1.1
|
|||||||
dokka_version=1.6.21
|
dokka_version=1.6.21
|
||||||
|
|
||||||
library_group=dev.inmo
|
library_group=dev.inmo
|
||||||
library_version=1.0.1
|
library_version=1.1.4
|
||||||
|
|
||||||
github_release_plugin_version=2.3.7
|
github_release_plugin_version=2.3.7
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package dev.inmo.tgbotapi.extensions.behaviour_builder
|
package dev.inmo.tgbotapi.extensions.behaviour_builder
|
||||||
|
|
||||||
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
|
import dev.inmo.micro_utils.coroutines.*
|
||||||
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
|
||||||
import dev.inmo.micro_utils.fsm.common.*
|
import dev.inmo.micro_utils.fsm.common.*
|
||||||
|
import dev.inmo.micro_utils.fsm.common.utils.StateHandlingErrorHandler
|
||||||
|
import dev.inmo.micro_utils.fsm.common.utils.defaultStateHandlingErrorHandler
|
||||||
import dev.inmo.tgbotapi.bot.TelegramBot
|
import dev.inmo.tgbotapi.bot.TelegramBot
|
||||||
import dev.inmo.tgbotapi.types.update.abstracts.Update
|
import dev.inmo.tgbotapi.types.update.abstracts.Update
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.handlers_registrar.TriggersHolder
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.handlers_registrar.TriggersHolder
|
||||||
@@ -50,12 +51,32 @@ interface BehaviourContextWithFSM<T : State> : BehaviourContext, StatesMachine<T
|
|||||||
updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>?
|
updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>?
|
||||||
): BehaviourContextWithFSM<T>
|
): BehaviourContextWithFSM<T>
|
||||||
|
|
||||||
|
fun copy(
|
||||||
|
bot: TelegramBot = this.bot,
|
||||||
|
scope: CoroutineScope = this.scope,
|
||||||
|
broadcastChannelsSize: Int = 100,
|
||||||
|
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND,
|
||||||
|
upstreamUpdatesFlow: Flow<Update>? = null,
|
||||||
|
triggersHolder: TriggersHolder = this.triggersHolder,
|
||||||
|
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
|
||||||
|
updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>? = null
|
||||||
|
): BehaviourContextWithFSM<T> = copy(
|
||||||
|
bot,
|
||||||
|
scope,
|
||||||
|
broadcastChannelsSize,
|
||||||
|
onBufferOverflow,
|
||||||
|
upstreamUpdatesFlow,
|
||||||
|
triggersHolder,
|
||||||
|
updatesFilter
|
||||||
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
operator fun <T : State> invoke(
|
operator fun <T : State> invoke(
|
||||||
behaviourContext: BehaviourContext,
|
behaviourContext: BehaviourContext,
|
||||||
handlers: List<BehaviourWithFSMStateHandlerHolder<*, T>>,
|
handlers: List<BehaviourWithFSMStateHandlerHolder<*, T>>,
|
||||||
statesManager: StatesManager<T>
|
statesManager: StatesManager<T>,
|
||||||
) = DefaultBehaviourContextWithFSM<T>(behaviourContext, statesManager, handlers)
|
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler()
|
||||||
|
) = DefaultBehaviourContextWithFSM<T>(behaviourContext, statesManager, handlers, onStateHandlingErrorHandler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,16 +105,22 @@ inline fun <reified I : O, O: State> BehaviourContextWithFSM<O>.strictlyOn(handl
|
|||||||
/**
|
/**
|
||||||
* Default realization of [BehaviourContextWithFSM]. It uses [behaviourContext] as a base for this object as
|
* Default realization of [BehaviourContextWithFSM]. It uses [behaviourContext] as a base for this object as
|
||||||
* [BehaviourContext], but managing substates contexts updates for avoiding of updates lost between states
|
* [BehaviourContext], but managing substates contexts updates for avoiding of updates lost between states
|
||||||
|
* @param onStateHandlingErrorHandler Will be used in case if state handling has not been successfully completed in [launchStateHandling]
|
||||||
*/
|
*/
|
||||||
class DefaultBehaviourContextWithFSM<T : State>(
|
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 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
|
||||||
|
|
||||||
|
override suspend fun launchStateHandling(state: T, handlers: List<CheckableHandlerHolder<in T, T>>): T? {
|
||||||
|
return launchStateHandling(state, handlers, onStateHandlingErrorHandler)
|
||||||
|
}
|
||||||
|
|
||||||
private fun getSubContext(context: Any) = updatesFlows.getOrPut(context) {
|
private fun getSubContext(context: Any) = updatesFlows.getOrPut(context) {
|
||||||
createSubContext()
|
createSubContext()
|
||||||
}
|
}
|
||||||
@@ -174,6 +201,23 @@ class DefaultBehaviourContextWithFSM<T : State>(
|
|||||||
): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM(
|
): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM(
|
||||||
behaviourContext.copy(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, triggersHolder, updatesFilter),
|
behaviourContext.copy(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, triggersHolder, updatesFilter),
|
||||||
handlers,
|
handlers,
|
||||||
statesManager
|
statesManager,
|
||||||
|
onStateHandlingErrorHandler
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun copy(
|
||||||
|
bot: TelegramBot,
|
||||||
|
scope: CoroutineScope,
|
||||||
|
broadcastChannelsSize: Int,
|
||||||
|
onBufferOverflow: BufferOverflow,
|
||||||
|
upstreamUpdatesFlow: Flow<Update>?,
|
||||||
|
triggersHolder: TriggersHolder,
|
||||||
|
onStateHandlingErrorHandler: StateHandlingErrorHandler<T>,
|
||||||
|
updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>?
|
||||||
|
): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM(
|
||||||
|
behaviourContext.copy(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, triggersHolder, updatesFilter),
|
||||||
|
handlers,
|
||||||
|
statesManager,
|
||||||
|
onStateHandlingErrorHandler
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,15 +4,14 @@ import dev.inmo.micro_utils.coroutines.*
|
|||||||
import dev.inmo.micro_utils.fsm.common.*
|
import dev.inmo.micro_utils.fsm.common.*
|
||||||
import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManager
|
import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManager
|
||||||
import dev.inmo.micro_utils.fsm.common.managers.InMemoryDefaultStatesManagerRepo
|
import dev.inmo.micro_utils.fsm.common.managers.InMemoryDefaultStatesManagerRepo
|
||||||
|
import dev.inmo.micro_utils.fsm.common.utils.StateHandlingErrorHandler
|
||||||
|
import dev.inmo.micro_utils.fsm.common.utils.defaultStateHandlingErrorHandler
|
||||||
import dev.inmo.tgbotapi.bot.TelegramBot
|
import dev.inmo.tgbotapi.bot.TelegramBot
|
||||||
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.longPolling
|
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.longPolling
|
||||||
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling
|
|
||||||
import dev.inmo.tgbotapi.types.update.abstracts.Update
|
import dev.inmo.tgbotapi.types.update.abstracts.Update
|
||||||
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
|
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
|
||||||
import dev.inmo.tgbotapi.utils.PreviewFeature
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
@Deprecated("Will be removed soon")
|
@Deprecated("Will be removed soon")
|
||||||
typealias BehaviourContextWithFSMBuilder<T> = BehaviourContextWithFSM<T>
|
typealias BehaviourContextWithFSMBuilder<T> = BehaviourContextWithFSM<T>
|
||||||
@@ -33,6 +32,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(),
|
||||||
|
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
|
||||||
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
|
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
|
||||||
): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM(
|
): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM(
|
||||||
DefaultBehaviourContext(
|
DefaultBehaviourContext(
|
||||||
@@ -41,7 +41,8 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
|
|||||||
upstreamUpdatesFlow = upstreamUpdatesFlow
|
upstreamUpdatesFlow = upstreamUpdatesFlow
|
||||||
),
|
),
|
||||||
presetHandlers,
|
presetHandlers,
|
||||||
statesManager
|
statesManager,
|
||||||
|
onStateHandlingErrorHandler
|
||||||
).apply { block() }
|
).apply { block() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,6 +56,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(),
|
||||||
|
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
|
||||||
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
|
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
|
||||||
): Pair<DefaultBehaviourContextWithFSM<T>, Job> = buildBehaviourWithFSM(
|
): Pair<DefaultBehaviourContextWithFSM<T>, Job> = buildBehaviourWithFSM(
|
||||||
upstreamUpdatesFlow,
|
upstreamUpdatesFlow,
|
||||||
@@ -62,6 +64,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
|
|||||||
defaultExceptionsHandler,
|
defaultExceptionsHandler,
|
||||||
statesManager,
|
statesManager,
|
||||||
presetHandlers,
|
presetHandlers,
|
||||||
|
onStateHandlingErrorHandler,
|
||||||
block
|
block
|
||||||
).run {
|
).run {
|
||||||
this to scope.launch {
|
this to scope.launch {
|
||||||
@@ -94,6 +97,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(),
|
||||||
|
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
|
||||||
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
|
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
|
||||||
): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM(
|
): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM(
|
||||||
DefaultBehaviourContext(
|
DefaultBehaviourContext(
|
||||||
@@ -102,7 +106,8 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
|
|||||||
upstreamUpdatesFlow = flowUpdatesFilter.allUpdatesFlow
|
upstreamUpdatesFlow = flowUpdatesFilter.allUpdatesFlow
|
||||||
),
|
),
|
||||||
presetHandlers,
|
presetHandlers,
|
||||||
statesManager
|
statesManager,
|
||||||
|
onStateHandlingErrorHandler
|
||||||
).apply { block() }
|
).apply { block() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -121,6 +126,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(),
|
||||||
|
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
|
||||||
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
|
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
|
||||||
) = FlowsUpdatesFilter().let {
|
) = FlowsUpdatesFilter().let {
|
||||||
buildBehaviourWithFSM(
|
buildBehaviourWithFSM(
|
||||||
@@ -129,6 +135,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
|
|||||||
defaultExceptionsHandler,
|
defaultExceptionsHandler,
|
||||||
statesManager,
|
statesManager,
|
||||||
presetHandlers,
|
presetHandlers,
|
||||||
|
onStateHandlingErrorHandler,
|
||||||
block
|
block
|
||||||
).run {
|
).run {
|
||||||
start()
|
start()
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package dev.inmo.tgbotapi.extensions.behaviour_builder
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.fsm.common.utils.StateHandlingErrorHandler
|
||||||
|
|
||||||
|
@Deprecated("Has been added in microutils", ReplaceWith("StateHandlingErrorHandler", "dev.inmo.micro_utils.fsm.common.utils.StateHandlingErrorHandler"))
|
||||||
|
typealias StateHandlingErrorHandler<T> = StateHandlingErrorHandler<T>
|
||||||
|
@Deprecated("Has been added in microutils", ReplaceWith("DefaultStateHandlingErrorHandler", "dev.inmo.micro_utils.fsm.common.utils.DefaultStateHandlingErrorHandler"))
|
||||||
|
val DefaultStateHandlingErrorHandler = dev.inmo.micro_utils.fsm.common.utils.DefaultStateHandlingErrorHandler
|
||||||
|
@Deprecated("Has been added in microutils", ReplaceWith("defaultStateHandlingErrorHandler", "dev.inmo.micro_utils.fsm.common.utils.defaultStateHandlingErrorHandler"))
|
||||||
|
inline fun <T> defaultStateHandlingErrorHandler() = dev.inmo.micro_utils.fsm.common.utils.defaultStateHandlingErrorHandler<T>()
|
||||||
@@ -5,6 +5,8 @@ import dev.inmo.micro_utils.fsm.common.State
|
|||||||
import dev.inmo.micro_utils.fsm.common.StatesManager
|
import dev.inmo.micro_utils.fsm.common.StatesManager
|
||||||
import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManager
|
import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManager
|
||||||
import dev.inmo.micro_utils.fsm.common.managers.InMemoryDefaultStatesManagerRepo
|
import dev.inmo.micro_utils.fsm.common.managers.InMemoryDefaultStatesManagerRepo
|
||||||
|
import dev.inmo.micro_utils.fsm.common.utils.StateHandlingErrorHandler
|
||||||
|
import dev.inmo.micro_utils.fsm.common.utils.defaultStateHandlingErrorHandler
|
||||||
import dev.inmo.tgbotapi.bot.ktor.KtorRequestsExecutorBuilder
|
import dev.inmo.tgbotapi.bot.ktor.KtorRequestsExecutorBuilder
|
||||||
import dev.inmo.tgbotapi.bot.ktor.telegramBot
|
import dev.inmo.tgbotapi.bot.ktor.telegramBot
|
||||||
import dev.inmo.tgbotapi.bot.TelegramBot
|
import dev.inmo.tgbotapi.bot.TelegramBot
|
||||||
@@ -39,6 +41,7 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSM(
|
|||||||
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
|
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
|
||||||
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
|
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
|
||||||
testServer: Boolean = false,
|
testServer: Boolean = false,
|
||||||
|
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
|
||||||
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
|
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
|
||||||
): TelegramBot = telegramBot(
|
): TelegramBot = telegramBot(
|
||||||
token,
|
token,
|
||||||
@@ -52,6 +55,7 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSM(
|
|||||||
defaultExceptionsHandler,
|
defaultExceptionsHandler,
|
||||||
statesManager,
|
statesManager,
|
||||||
presetHandlers,
|
presetHandlers,
|
||||||
|
onStateHandlingErrorHandler,
|
||||||
block
|
block
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -76,6 +80,7 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSMAndStartLongPolling(
|
|||||||
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
|
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
|
||||||
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
|
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
|
||||||
testServer: Boolean = false,
|
testServer: Boolean = false,
|
||||||
|
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
|
||||||
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
|
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
|
||||||
): Pair<TelegramBot, Job> {
|
): Pair<TelegramBot, Job> {
|
||||||
return telegramBot(
|
return telegramBot(
|
||||||
@@ -89,6 +94,7 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSMAndStartLongPolling(
|
|||||||
defaultExceptionsHandler,
|
defaultExceptionsHandler,
|
||||||
statesManager,
|
statesManager,
|
||||||
presetHandlers,
|
presetHandlers,
|
||||||
|
onStateHandlingErrorHandler,
|
||||||
block
|
block
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.longPolling
|
|||||||
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling
|
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling
|
||||||
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
|
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
|
||||||
import dev.inmo.tgbotapi.utils.PreviewFeature
|
import dev.inmo.tgbotapi.utils.PreviewFeature
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.plus
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function is used in [buildBehaviour] extensions to provide default [CoroutineScope] and allow to avoid all
|
* This function is used in [buildBehaviour] extensions to provide default [CoroutineScope] and allow to avoid all
|
||||||
@@ -30,18 +29,18 @@ suspend fun TelegramBot.buildBehaviour(
|
|||||||
scope: CoroutineScope = defaultCoroutineScopeProvider(),
|
scope: CoroutineScope = defaultCoroutineScopeProvider(),
|
||||||
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
|
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
|
||||||
block: BehaviourContextReceiver<Unit>
|
block: BehaviourContextReceiver<Unit>
|
||||||
) {
|
): BehaviourContext = BehaviourContext(
|
||||||
BehaviourContext(
|
this,
|
||||||
this,
|
scope.let {
|
||||||
scope.let {
|
if (defaultExceptionsHandler == null) {
|
||||||
if (defaultExceptionsHandler == null) {
|
it
|
||||||
it
|
} else {
|
||||||
} else {
|
it + ContextSafelyExceptionHandler(defaultExceptionsHandler)
|
||||||
it + ContextSafelyExceptionHandler(defaultExceptionsHandler)
|
}
|
||||||
}
|
},
|
||||||
},
|
flowUpdatesFilter
|
||||||
flowUpdatesFilter
|
).apply {
|
||||||
).block()
|
block()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,15 +55,14 @@ suspend fun TelegramBot.buildBehaviourWithLongPolling(
|
|||||||
scope: CoroutineScope = defaultCoroutineScopeProvider(),
|
scope: CoroutineScope = defaultCoroutineScopeProvider(),
|
||||||
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
|
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
|
||||||
block: BehaviourContextReceiver<Unit>
|
block: BehaviourContextReceiver<Unit>
|
||||||
) = FlowsUpdatesFilter().let {
|
): Job {
|
||||||
buildBehaviour(
|
val behaviourContext = buildBehaviour(
|
||||||
it,
|
scope = scope,
|
||||||
scope,
|
defaultExceptionsHandler = defaultExceptionsHandler,
|
||||||
defaultExceptionsHandler,
|
block = block
|
||||||
block
|
|
||||||
)
|
)
|
||||||
longPolling(
|
return longPolling(
|
||||||
it,
|
behaviourContext,
|
||||||
scope = scope
|
scope = behaviourContext
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,19 @@ interface BehaviourContext : FlowsUpdatesFilter, TelegramBot, CoroutineScope {
|
|||||||
|
|
||||||
val triggersHolder: TriggersHolder
|
val triggersHolder: TriggersHolder
|
||||||
|
|
||||||
|
fun copy(
|
||||||
|
bot: TelegramBot = this.bot,
|
||||||
|
scope: CoroutineScope = this.scope,
|
||||||
|
broadcastChannelsSize: Int = 100,
|
||||||
|
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND,
|
||||||
|
upstreamUpdatesFlow: Flow<Update>? = null,
|
||||||
|
triggersHolder: TriggersHolder = TriggersHolder()
|
||||||
|
): BehaviourContext
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param updatesFilter unused
|
||||||
|
*/
|
||||||
|
@Deprecated("Do not use this method")
|
||||||
fun copy(
|
fun copy(
|
||||||
bot: TelegramBot = this.bot,
|
bot: TelegramBot = this.bot,
|
||||||
scope: CoroutineScope = this.scope,
|
scope: CoroutineScope = this.scope,
|
||||||
@@ -57,7 +70,7 @@ interface BehaviourContext : FlowsUpdatesFilter, TelegramBot, CoroutineScope {
|
|||||||
upstreamUpdatesFlow: Flow<Update>? = null,
|
upstreamUpdatesFlow: Flow<Update>? = null,
|
||||||
triggersHolder: TriggersHolder = TriggersHolder(),
|
triggersHolder: TriggersHolder = TriggersHolder(),
|
||||||
updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>? = null
|
updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>? = null
|
||||||
): BehaviourContext
|
): BehaviourContext = copy(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, triggersHolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
class DefaultBehaviourContext(
|
class DefaultBehaviourContext(
|
||||||
@@ -67,20 +80,17 @@ class DefaultBehaviourContext(
|
|||||||
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND,
|
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND,
|
||||||
private val upstreamUpdatesFlow: Flow<Update>? = null,
|
private val upstreamUpdatesFlow: Flow<Update>? = null,
|
||||||
override val triggersHolder: TriggersHolder = TriggersHolder(),
|
override val triggersHolder: TriggersHolder = TriggersHolder(),
|
||||||
|
@Deprecated("This method is not used anymore")
|
||||||
private val updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>? = null
|
private val updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>? = null
|
||||||
) : AbstractFlowsUpdatesFilter(), TelegramBot by bot, CoroutineScope by scope, BehaviourContext {
|
) : AbstractFlowsUpdatesFilter(), TelegramBot by bot, CoroutineScope by scope, BehaviourContext {
|
||||||
|
|
||||||
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) {
|
||||||
(it + upstreamUpdatesFlow).distinctUntilChanged { old, new -> old.updateId == new.updateId }
|
var lastHandledUpdate = -1L
|
||||||
} else {
|
(it + upstreamUpdatesFlow).filter {
|
||||||
it
|
(it.updateId > lastHandledUpdate).also { passed -> if (passed) { lastHandledUpdate = it.updateId } }
|
||||||
}
|
}
|
||||||
}.let {
|
|
||||||
val updatesFilter = updatesFilter
|
|
||||||
if (updatesFilter != null) {
|
|
||||||
it.filter { updatesFilter(it) }
|
|
||||||
} else {
|
} else {
|
||||||
it
|
it
|
||||||
}
|
}
|
||||||
@@ -93,9 +103,8 @@ class DefaultBehaviourContext(
|
|||||||
broadcastChannelsSize: Int,
|
broadcastChannelsSize: Int,
|
||||||
onBufferOverflow: BufferOverflow,
|
onBufferOverflow: BufferOverflow,
|
||||||
upstreamUpdatesFlow: Flow<Update>?,
|
upstreamUpdatesFlow: Flow<Update>?,
|
||||||
triggersHolder: TriggersHolder,
|
triggersHolder: TriggersHolder
|
||||||
updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>?
|
): DefaultBehaviourContext = DefaultBehaviourContext(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, triggersHolder)
|
||||||
): DefaultBehaviourContext = DefaultBehaviourContext(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, triggersHolder, updatesFilter)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun BehaviourContext(
|
fun BehaviourContext(
|
||||||
@@ -113,24 +122,34 @@ inline fun <T> BehaviourContext(
|
|||||||
crossinline block: BehaviourContext.() -> T
|
crossinline block: BehaviourContext.() -> T
|
||||||
) = DefaultBehaviourContext(bot, scope, upstreamUpdatesFlow = flowsUpdatesFilter.allUpdatesFlow, triggersHolder = triggersHolder).run(block)
|
) = DefaultBehaviourContext(bot, scope, upstreamUpdatesFlow = flowsUpdatesFilter.allUpdatesFlow, triggersHolder = triggersHolder).run(block)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new [BehaviourContext] using its [BehaviourContext.copy] method
|
||||||
|
*
|
||||||
|
* @param updatesFilter This param will not be used anymore
|
||||||
|
*/
|
||||||
fun <BC : BehaviourContext> BC.createSubContext(
|
fun <BC : BehaviourContext> BC.createSubContext(
|
||||||
scope: CoroutineScope = LinkedSupervisorScope(),
|
scope: CoroutineScope = LinkedSupervisorScope(),
|
||||||
triggersHolder: TriggersHolder = this.triggersHolder,
|
triggersHolder: TriggersHolder = this.triggersHolder,
|
||||||
updatesUpstreamFlow: Flow<Update> = allUpdatesFlow,
|
updatesUpstreamFlow: Flow<Update> = allUpdatesFlow,
|
||||||
updatesFilter: CustomBehaviourContextAndTypeReceiver<BC, Boolean, Update>? = null,
|
|
||||||
) = copy(
|
) = copy(
|
||||||
scope = scope,
|
scope = scope,
|
||||||
updatesFilter = updatesFilter ?.let { _ ->
|
|
||||||
{
|
|
||||||
(this as? BC) ?.run {
|
|
||||||
updatesFilter(it)
|
|
||||||
} ?: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
upstreamUpdatesFlow = updatesUpstreamFlow,
|
upstreamUpdatesFlow = updatesUpstreamFlow,
|
||||||
triggersHolder = triggersHolder
|
triggersHolder = triggersHolder
|
||||||
) as BC
|
) as BC
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new [BehaviourContext] using its [BehaviourContext.copy] method
|
||||||
|
*
|
||||||
|
* @param updatesFilter This param will not be used anymore
|
||||||
|
*/
|
||||||
|
@Deprecated("It is not recommended to use updates filter anymore")
|
||||||
|
fun <BC : BehaviourContext> BC.createSubContext(
|
||||||
|
scope: CoroutineScope = LinkedSupervisorScope(),
|
||||||
|
triggersHolder: TriggersHolder = this.triggersHolder,
|
||||||
|
updatesUpstreamFlow: Flow<Update> = allUpdatesFlow,
|
||||||
|
updatesFilter: CustomBehaviourContextAndTypeReceiver<BC, Boolean, Update>?,
|
||||||
|
) = createSubContext(scope, triggersHolder, updatesUpstreamFlow)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Launch [behaviourContextReceiver] in context of [this] as [BehaviourContext] and as [kotlin.coroutines.CoroutineContext]
|
* Launch [behaviourContextReceiver] in context of [this] as [BehaviourContext] and as [kotlin.coroutines.CoroutineContext]
|
||||||
*
|
*
|
||||||
@@ -155,21 +174,40 @@ suspend fun <T, BC : BehaviourContext> BC.createSubContextAndDoWithUpdatesFilter
|
|||||||
scope: CoroutineScope = LinkedSupervisorScope(),
|
scope: CoroutineScope = LinkedSupervisorScope(),
|
||||||
triggersHolder: TriggersHolder = this.triggersHolder,
|
triggersHolder: TriggersHolder = this.triggersHolder,
|
||||||
updatesUpstreamFlow: Flow<Update> = allUpdatesFlow,
|
updatesUpstreamFlow: Flow<Update> = allUpdatesFlow,
|
||||||
updatesFilter: CustomBehaviourContextAndTypeReceiver<BC, Boolean, Update>? = null,
|
|
||||||
stopOnCompletion: Boolean = true,
|
stopOnCompletion: Boolean = true,
|
||||||
behaviourContextReceiver: CustomBehaviourContextReceiver<BC, T>
|
behaviourContextReceiver: CustomBehaviourContextReceiver<BC, T>
|
||||||
): T {
|
): T {
|
||||||
return createSubContext(
|
return createSubContext(
|
||||||
scope,
|
scope,
|
||||||
triggersHolder,
|
triggersHolder,
|
||||||
updatesUpstreamFlow,
|
updatesUpstreamFlow
|
||||||
updatesFilter
|
|
||||||
).doInContext(
|
).doInContext(
|
||||||
stopOnCompletion,
|
stopOnCompletion,
|
||||||
behaviourContextReceiver
|
behaviourContextReceiver
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new one [BehaviourContext] using [createSubContext] and launches [behaviourContextReceiver] in a new context
|
||||||
|
* using [doInContext]
|
||||||
|
*
|
||||||
|
* @param stopOnCompletion ___TRUE BY DEFAULT___
|
||||||
|
* @param updatesFilter Is not used anymore
|
||||||
|
*/
|
||||||
|
@Deprecated("It is not recommended to use updates filter anymore")
|
||||||
|
suspend fun <T, BC : BehaviourContext> BC.createSubContextAndDoWithUpdatesFilter(
|
||||||
|
scope: CoroutineScope = LinkedSupervisorScope(),
|
||||||
|
triggersHolder: TriggersHolder = this.triggersHolder,
|
||||||
|
updatesUpstreamFlow: Flow<Update> = allUpdatesFlow,
|
||||||
|
updatesFilter: CustomBehaviourContextAndTypeReceiver<BC, Boolean, Update>?,
|
||||||
|
stopOnCompletion: Boolean = true,
|
||||||
|
behaviourContextReceiver: CustomBehaviourContextReceiver<BC, T>
|
||||||
|
): T {
|
||||||
|
return createSubContextAndDoWithUpdatesFilter(
|
||||||
|
scope, triggersHolder, updatesUpstreamFlow, stopOnCompletion, behaviourContextReceiver
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new one [BehaviourContext] using [createSubContext] and launches [behaviourContextReceiver] in a new context
|
* Creates new one [BehaviourContext] using [createSubContext] and launches [behaviourContextReceiver] in a new context
|
||||||
* using [doInContext]
|
* using [doInContext]
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ private suspend fun <O> BehaviourContext.waitCallbackQueries(
|
|||||||
count,
|
count,
|
||||||
errorFactory
|
errorFactory
|
||||||
) {
|
) {
|
||||||
val data = it.asCallbackQueryUpdate() ?.data
|
val data = it.asCallbackQueryUpdate() ?.data ?: return@expectFlow emptyList()
|
||||||
if (data != null && (filter == null || filter(data))) {
|
if (filter == null || filter(data)) {
|
||||||
data.mapper().let(::listOfNotNull)
|
data.mapper().let(::listOfNotNull)
|
||||||
} else {
|
} else {
|
||||||
emptyList()
|
emptyList()
|
||||||
|
|||||||
@@ -15,16 +15,17 @@ internal suspend inline fun <BC : BehaviourContext, reified T> BC.on(
|
|||||||
noinline updateToData: (Update) -> List<T>?
|
noinline updateToData: (Update) -> List<T>?
|
||||||
) = flowsUpdatesFilter.expectFlow(bot) {
|
) = flowsUpdatesFilter.expectFlow(bot) {
|
||||||
updateToData(it) ?.mapNotNull { data ->
|
updateToData(it) ?.mapNotNull { data ->
|
||||||
if (initialFilter ?.invoke(data) != false) data else null
|
if (initialFilter ?.invoke(data) != false) it to data else null
|
||||||
} ?: emptyList()
|
} ?: emptyList()
|
||||||
}.subscribeSafelyWithoutExceptionsAsync(
|
}.subscribeSafelyWithoutExceptionsAsync(
|
||||||
scope,
|
scope,
|
||||||
markerFactory::invoke
|
{ markerFactory(it.second) }
|
||||||
) { triggerData ->
|
) { (update, triggerData) ->
|
||||||
createSubContextAndDoWithUpdatesFilter(
|
createSubContextAndDoWithUpdatesFilter(
|
||||||
updatesFilter = subcontextUpdatesFilter ?.toOneType(triggerData),
|
|
||||||
stopOnCompletion = false
|
stopOnCompletion = false
|
||||||
) {
|
) {
|
||||||
scenarioReceiver(triggerData)
|
if (subcontextUpdatesFilter ?.invoke(this, triggerData, update) != false) {
|
||||||
|
scenarioReceiver(triggerData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,10 +47,10 @@ kotlin {
|
|||||||
api "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlin_serialisation_runtime_version"
|
api "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlin_serialisation_runtime_version"
|
||||||
api "org.jetbrains.kotlinx:kotlinx-serialization-properties:$kotlin_serialisation_runtime_version"
|
api "org.jetbrains.kotlinx:kotlinx-serialization-properties:$kotlin_serialisation_runtime_version"
|
||||||
|
|
||||||
api "com.soywiz.korlibs.klock:klock:$klock_version"
|
api "com.soywiz.korlibs.klock:klock:$korlibs_version"
|
||||||
|
api "com.soywiz.korlibs.krypto:krypto:$korlibs_version"
|
||||||
api "com.benasher44:uuid:$uuid_version"
|
api "com.benasher44:uuid:$uuid_version"
|
||||||
|
|
||||||
api "dev.inmo:micro_utils.crypto:$micro_utils_version"
|
|
||||||
api "dev.inmo:micro_utils.coroutines:$micro_utils_version"
|
api "dev.inmo:micro_utils.coroutines:$micro_utils_version"
|
||||||
api "dev.inmo:micro_utils.serialization.base64:$micro_utils_version"
|
api "dev.inmo:micro_utils.serialization.base64:$micro_utils_version"
|
||||||
api "dev.inmo:micro_utils.serialization.encapsulator:$micro_utils_version"
|
api "dev.inmo:micro_utils.serialization.encapsulator:$micro_utils_version"
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package dev.inmo.tgbotapi.utils
|
package dev.inmo.tgbotapi.utils
|
||||||
|
|
||||||
import dev.inmo.micro_utils.crypto.hex
|
import com.soywiz.krypto.*
|
||||||
import dev.inmo.micro_utils.crypto.hmacSha256
|
import io.ktor.http.decodeURLQueryComponent
|
||||||
|
import io.ktor.utils.io.core.toByteArray
|
||||||
|
|
||||||
const val telegramBotAPIDefaultUrl = "https://api.telegram.org"
|
const val telegramBotAPIDefaultUrl = "https://api.telegram.org"
|
||||||
|
|
||||||
@@ -22,9 +23,11 @@ class TelegramAPIUrlsKeeper(
|
|||||||
hostUrl: String = telegramBotAPIDefaultUrl,
|
hostUrl: String = telegramBotAPIDefaultUrl,
|
||||||
urlsSuffixes: String = ""
|
urlsSuffixes: String = ""
|
||||||
) {
|
) {
|
||||||
val webAppDataSecretKey by lazy {
|
val webAppDataSecretKeyHash by lazy {
|
||||||
token.hmacSha256("WebAppData")
|
HMAC.hmacSHA256("WebAppData".toByteArray(), token.toByteArray())
|
||||||
}
|
}
|
||||||
|
val webAppDataSecretKey
|
||||||
|
get() = webAppDataSecretKeyHash.hexLower
|
||||||
|
|
||||||
val commonAPIUrl: String
|
val commonAPIUrl: String
|
||||||
val fileBaseUrl: String
|
val fileBaseUrl: String
|
||||||
@@ -47,5 +50,21 @@ class TelegramAPIUrlsKeeper(
|
|||||||
* @param rawData Data from [dev.inmo.tgbotapi.webapps.WebApp.initData]
|
* @param rawData Data from [dev.inmo.tgbotapi.webapps.WebApp.initData]
|
||||||
* @param hash Data from [dev.inmo.tgbotapi.webapps.WebApp.initDataUnsafe] from the field [dev.inmo.tgbotapi.webapps.WebAppInitData.hash]
|
* @param hash Data from [dev.inmo.tgbotapi.webapps.WebApp.initDataUnsafe] from the field [dev.inmo.tgbotapi.webapps.WebAppInitData.hash]
|
||||||
*/
|
*/
|
||||||
fun checkWebAppLink(rawData: String, hash: String) = rawData.hmacSha256(webAppDataSecretKey).hex() == hash
|
fun checkWebAppData(rawData: String, hash: String): Boolean {
|
||||||
|
val preparedData = rawData
|
||||||
|
.decodeURLQueryComponent()
|
||||||
|
.split("&")
|
||||||
|
.filterNot { it.startsWith("hash=") }
|
||||||
|
.sorted()
|
||||||
|
.joinToString("\n")
|
||||||
|
|
||||||
|
return HMAC.hmacSHA256(webAppDataSecretKeyHash.bytes, preparedData.toByteArray()).hexLower == hash.lowercase()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param rawData Data from [dev.inmo.tgbotapi.webapps.WebApp.initData]
|
||||||
|
* @param hash Data from [dev.inmo.tgbotapi.webapps.WebApp.initDataUnsafe] from the field [dev.inmo.tgbotapi.webapps.WebAppInitData.hash]
|
||||||
|
*/
|
||||||
|
@Deprecated("Renamed", ReplaceWith("checkWebAppData"))
|
||||||
|
inline fun checkWebAppLink(rawData: String, hash: String) = checkWebAppData(rawData, hash)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package dev.inmo.tgbotapi.extensions.utils.updates.retrieving
|
package dev.inmo.tgbotapi.extensions.utils.updates.retrieving
|
||||||
|
|
||||||
import dev.inmo.micro_utils.coroutines.ExceptionHandler
|
import dev.inmo.micro_utils.coroutines.*
|
||||||
import dev.inmo.micro_utils.coroutines.safely
|
|
||||||
import dev.inmo.tgbotapi.bot.RequestsExecutor
|
import dev.inmo.tgbotapi.bot.RequestsExecutor
|
||||||
import dev.inmo.tgbotapi.extensions.utils.nonstrictJsonFormat
|
import dev.inmo.tgbotapi.extensions.utils.nonstrictJsonFormat
|
||||||
import dev.inmo.tgbotapi.extensions.utils.updates.flowsUpdatesFilter
|
import dev.inmo.tgbotapi.extensions.utils.updates.flowsUpdatesFilter
|
||||||
@@ -10,6 +9,7 @@ import dev.inmo.tgbotapi.types.update.abstracts.Update
|
|||||||
import dev.inmo.tgbotapi.types.update.abstracts.UpdateDeserializationStrategy
|
import dev.inmo.tgbotapi.types.update.abstracts.UpdateDeserializationStrategy
|
||||||
import dev.inmo.tgbotapi.updateshandlers.*
|
import dev.inmo.tgbotapi.updateshandlers.*
|
||||||
import dev.inmo.tgbotapi.updateshandlers.webhook.WebhookPrivateKeyConfig
|
import dev.inmo.tgbotapi.updateshandlers.webhook.WebhookPrivateKeyConfig
|
||||||
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.server.application.call
|
import io.ktor.server.application.call
|
||||||
import io.ktor.server.engine.*
|
import io.ktor.server.engine.*
|
||||||
import io.ktor.server.request.receiveText
|
import io.ktor.server.request.receiveText
|
||||||
@@ -39,18 +39,22 @@ fun Route.includeWebhookHandlingInRoute(
|
|||||||
) {
|
) {
|
||||||
val transformer = scope.updateHandlerWithMediaGroupsAdaptation(block, mediaGroupsDebounceTimeMillis)
|
val transformer = scope.updateHandlerWithMediaGroupsAdaptation(block, mediaGroupsDebounceTimeMillis)
|
||||||
post {
|
post {
|
||||||
safely(
|
try {
|
||||||
exceptionsHandler ?: {}
|
runCatchingSafely {
|
||||||
) {
|
val asJson = nonstrictJsonFormat.parseToJsonElement(call.receiveText())
|
||||||
val asJson =
|
val update = nonstrictJsonFormat.decodeFromJsonElement(
|
||||||
nonstrictJsonFormat.parseToJsonElement(call.receiveText())
|
UpdateDeserializationStrategy,
|
||||||
val update = nonstrictJsonFormat.decodeFromJsonElement(
|
asJson
|
||||||
UpdateDeserializationStrategy,
|
)
|
||||||
asJson
|
transformer(update)
|
||||||
)
|
}.onSuccess {
|
||||||
transformer(update)
|
call.respond(HttpStatusCode.OK)
|
||||||
|
}.onFailure {
|
||||||
|
call.respond(HttpStatusCode.InternalServerError)
|
||||||
|
}.getOrThrow()
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
exceptionsHandler ?.invoke(e)
|
||||||
}
|
}
|
||||||
call.respond("Ok")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,6 +91,7 @@ fun startListenWebhooks(
|
|||||||
privateKeyConfig: WebhookPrivateKeyConfig? = null,
|
privateKeyConfig: WebhookPrivateKeyConfig? = null,
|
||||||
scope: CoroutineScope = CoroutineScope(Executors.newFixedThreadPool(4).asCoroutineDispatcher()),
|
scope: CoroutineScope = CoroutineScope(Executors.newFixedThreadPool(4).asCoroutineDispatcher()),
|
||||||
mediaGroupsDebounceTimeMillis: Long = 1000L,
|
mediaGroupsDebounceTimeMillis: Long = 1000L,
|
||||||
|
additionalApplicationEngineEnvironmentConfigurator: ApplicationEngineEnvironmentBuilder.() -> Unit = {},
|
||||||
block: UpdateReceiver<Update>
|
block: UpdateReceiver<Update>
|
||||||
): ApplicationEngine {
|
): ApplicationEngine {
|
||||||
val env = applicationEngineEnvironment {
|
val env = applicationEngineEnvironment {
|
||||||
@@ -98,6 +103,7 @@ fun startListenWebhooks(
|
|||||||
} ?: includeWebhookHandlingInRoute(scope, exceptionsHandler, mediaGroupsDebounceTimeMillis, block)
|
} ?: includeWebhookHandlingInRoute(scope, exceptionsHandler, mediaGroupsDebounceTimeMillis, block)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
privateKeyConfig ?.let {
|
privateKeyConfig ?.let {
|
||||||
sslConnector(
|
sslConnector(
|
||||||
privateKeyConfig.keyStore,
|
privateKeyConfig.keyStore,
|
||||||
@@ -113,6 +119,7 @@ fun startListenWebhooks(
|
|||||||
port = listenPort
|
port = listenPort
|
||||||
}
|
}
|
||||||
|
|
||||||
|
additionalApplicationEngineEnvironmentConfigurator()
|
||||||
}
|
}
|
||||||
|
|
||||||
return embeddedServer(engineFactory, env).also {
|
return embeddedServer(engineFactory, env).also {
|
||||||
@@ -142,10 +149,11 @@ suspend fun RequestsExecutor.setWebhookInfoAndStartListenWebhooks(
|
|||||||
privateKeyConfig: WebhookPrivateKeyConfig? = null,
|
privateKeyConfig: WebhookPrivateKeyConfig? = null,
|
||||||
scope: CoroutineScope = CoroutineScope(Executors.newFixedThreadPool(4).asCoroutineDispatcher()),
|
scope: CoroutineScope = CoroutineScope(Executors.newFixedThreadPool(4).asCoroutineDispatcher()),
|
||||||
mediaGroupsDebounceTimeMillis: Long = 1000L,
|
mediaGroupsDebounceTimeMillis: Long = 1000L,
|
||||||
|
additionalApplicationEngineEnvironmentConfigurator: ApplicationEngineEnvironmentBuilder.() -> Unit = {},
|
||||||
block: UpdateReceiver<Update>
|
block: UpdateReceiver<Update>
|
||||||
): ApplicationEngine = try {
|
): ApplicationEngine = try {
|
||||||
execute(setWebhookRequest)
|
execute(setWebhookRequest)
|
||||||
startListenWebhooks(listenPort, engineFactory, exceptionsHandler, listenHost, listenRoute, privateKeyConfig, scope, mediaGroupsDebounceTimeMillis, block)
|
startListenWebhooks(listenPort, engineFactory, exceptionsHandler, listenHost, listenRoute, privateKeyConfig, scope, mediaGroupsDebounceTimeMillis, additionalApplicationEngineEnvironmentConfigurator, block)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package dev.inmo.tgbotapi.webapps
|
|||||||
|
|
||||||
import dev.inmo.micro_utils.crypto.CryptoJs
|
import dev.inmo.micro_utils.crypto.CryptoJs
|
||||||
|
|
||||||
|
@Deprecated("Useless")
|
||||||
fun CryptoJs.HmacSHA256(text: String, key: String) = this.asDynamic().HmacSHA256(text, key).unsafeCast<String>()
|
fun CryptoJs.HmacSHA256(text: String, key: String) = this.asDynamic().HmacSHA256(text, key).unsafeCast<String>()
|
||||||
|
|
||||||
|
@Deprecated("Useless")
|
||||||
fun CryptoJs.hex(text: String) = this.asDynamic().format.Hex(text).unsafeCast<String>()
|
fun CryptoJs.hex(text: String) = this.asDynamic().format.Hex(text).unsafeCast<String>()
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package dev.inmo.tgbotapi.webapps
|
package dev.inmo.tgbotapi.webapps
|
||||||
|
|
||||||
import dev.inmo.micro_utils.crypto.CryptoJS
|
|
||||||
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
|
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
|
||||||
|
|
||||||
external class WebApp {
|
external class WebApp {
|
||||||
@@ -77,7 +76,7 @@ fun WebApp.onMainButtonClicked(eventHandler: EventHandler) = onEvent(EventType.M
|
|||||||
*/
|
*/
|
||||||
fun WebApp.onViewportChanged(eventHandler: ViewportChangedEventHandler) = onEvent(EventType.ViewportChanged, eventHandler)
|
fun WebApp.onViewportChanged(eventHandler: ViewportChangedEventHandler) = onEvent(EventType.ViewportChanged, eventHandler)
|
||||||
|
|
||||||
fun WebApp.isInitDataSafe(botToken: String) = TelegramAPIUrlsKeeper(botToken).checkWebAppLink(
|
fun WebApp.isInitDataSafe(botToken: String) = TelegramAPIUrlsKeeper(botToken).checkWebAppData(
|
||||||
initData,
|
initData,
|
||||||
initDataUnsafe.hash
|
initDataUnsafe.hash
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user