From 179a724d20961a7bbe84641e652ccbadfa18abba Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Fri, 15 Oct 2021 16:28:17 +0600 Subject: [PATCH] several actualizations for behaviour builder with fsm --- .../BehaviourContextWithFSM.kt | 2 + .../BehaviourContextWithFSMBuilder.kt | 93 +++++++++++++++++-- .../behaviour_builder/TelegramBotWithFSM.kt | 93 +++++++++++++++++++ 3 files changed, 182 insertions(+), 6 deletions(-) create mode 100644 tgbotapi.extensions.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/TelegramBotWithFSM.kt diff --git a/tgbotapi.extensions.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSM.kt b/tgbotapi.extensions.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSM.kt index 26fc50ea66..98f79aa270 100644 --- a/tgbotapi.extensions.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSM.kt +++ b/tgbotapi.extensions.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSM.kt @@ -21,6 +21,8 @@ private suspend fun BehaviourContextWithFSM.launchStateHandling( } interface BehaviourContextWithFSM : BehaviourContext, StatesMachine { + suspend fun start() = start(this) + override fun copy( bot: TelegramBot, scope: CoroutineScope, diff --git a/tgbotapi.extensions.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSMBuilder.kt b/tgbotapi.extensions.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSMBuilder.kt index cdcd5d2e86..96ca461801 100644 --- a/tgbotapi.extensions.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSMBuilder.kt +++ b/tgbotapi.extensions.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSMBuilder.kt @@ -1,14 +1,18 @@ package dev.inmo.tgbotapi.extensions.behaviour_builder +import dev.inmo.micro_utils.coroutines.ContextSafelyExceptionHandler +import dev.inmo.micro_utils.coroutines.ExceptionHandler import dev.inmo.micro_utils.fsm.common.* import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManager import dev.inmo.micro_utils.fsm.common.managers.InMemoryDefaultStatesManagerRepo import dev.inmo.tgbotapi.bot.TelegramBot 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 kotlinx.coroutines.CoroutineScope +import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter +import dev.inmo.tgbotapi.utils.PreviewFeature +import kotlinx.coroutines.* import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.launch import kotlin.reflect.KClass class BehaviourContextWithFSMBuilder internal constructor( @@ -83,9 +87,13 @@ inline fun BehaviourContextWithFSMBuilder.strictlyOn(handler suspend fun TelegramBot.buildBehaviourWithFSM( upstreamUpdatesFlow: Flow? = null, scope: CoroutineScope = defaultCoroutineScopeProvider(), + statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), + handlersPreset: MutableList> = mutableListOf(), block: suspend BehaviourContextWithFSMBuilder.() -> Unit ) = BehaviourContextWithFSMBuilder( - DefaultBehaviourContext(this, scope, upstreamUpdatesFlow = upstreamUpdatesFlow) + DefaultBehaviourContext(this, scope, upstreamUpdatesFlow = upstreamUpdatesFlow), + statesManager, + handlersPreset ).apply { block() }.build() /** @@ -98,10 +106,83 @@ suspend fun TelegramBot.buildBehaviourWithFSM( suspend fun TelegramBot.buildBehaviourWithFSMAndLongPolling( upstreamUpdatesFlow: Flow? = null, scope: CoroutineScope = defaultCoroutineScopeProvider(), + statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), + presetHandlers: MutableList> = mutableListOf(), block: suspend BehaviourContextWithFSMBuilder.() -> Unit -) = buildBehaviourWithFSM(upstreamUpdatesFlow, scope, block).run { +) = buildBehaviourWithFSM(upstreamUpdatesFlow, scope, statesManager, presetHandlers, block).run { this to scope.launch { - longPolling(this@run, scope = this) - start(this) + start() + longPolling(flowsUpdatesFilter, scope = scope) + } +} + +/** + * Use this method in case you wish to make some additional actions with [flowUpdatesFilter]. + * + * **WARNING** This method WILL NOT launch any listening of updates. Use something like + * [startGettingOfUpdatesByLongPolling] (or just [longPolling]) or tools for work with webhooks + * + * @see BehaviourContext + * @see BehaviourContextWithFSM + * @see longPolling + * @see strictlyOn + * @see onStateOrSubstate + */ +@PreviewFeature +suspend fun TelegramBot.buildBehaviourWithFSM( + flowUpdatesFilter: FlowsUpdatesFilter, + scope: CoroutineScope = defaultCoroutineScopeProvider(), + defaultExceptionsHandler: ExceptionHandler? = null, + statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), + presetHandlers: MutableList> = mutableListOf(), + block: CustomBehaviourContextReceiver +): BehaviourContextWithFSM = BehaviourContextWithFSMBuilder( + BehaviourContext( + this, + scope.let { + if (defaultExceptionsHandler == null) { + it + } else { + it + ContextSafelyExceptionHandler(defaultExceptionsHandler) + } + }, + flowUpdatesFilter + ), + statesManager, + presetHandlers +).apply { block() }.build() + +/** + * Use this method to build bot behaviour with FSM and run it via long polling. In case you wish to use + * [FlowsUpdatesFilter] of result [BehaviourContextWithFSM] for additional manipulations, you must provide external + * [FlowsUpdatesFilter] in other [buildBehaviourWithFSM] function. + * + * @see buildBehaviourWithFSM + * @see BehaviourContext + * @see longPolling + * @see strictlyOn + * @see onStateOrSubstate + */ +@PreviewFeature +suspend fun TelegramBot.buildBehaviourWithFSM( + scope: CoroutineScope = defaultCoroutineScopeProvider(), + defaultExceptionsHandler: ExceptionHandler? = null, + statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), + presetHandlers: MutableList> = mutableListOf(), + block: BehaviourContextReceiver +) = FlowsUpdatesFilter().let { + buildBehaviourWithFSM( + it, + scope, + defaultExceptionsHandler, + statesManager, + presetHandlers, + block + ).run { + start() + longPolling( + flowsUpdatesFilter, + scope = scope + ) } } diff --git a/tgbotapi.extensions.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/TelegramBotWithFSM.kt b/tgbotapi.extensions.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/TelegramBotWithFSM.kt new file mode 100644 index 0000000000..23eb0fe7bc --- /dev/null +++ b/tgbotapi.extensions.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/TelegramBotWithFSM.kt @@ -0,0 +1,93 @@ +package dev.inmo.tgbotapi.extensions.behaviour_builder + +import dev.inmo.micro_utils.coroutines.ExceptionHandler +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.InMemoryDefaultStatesManagerRepo +import dev.inmo.tgbotapi.bot.Ktor.KtorRequestsExecutorBuilder +import dev.inmo.tgbotapi.bot.Ktor.telegramBot +import dev.inmo.tgbotapi.bot.TelegramBot +import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling +import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter +import dev.inmo.tgbotapi.utils.telegramBotAPIDefaultUrl +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlin.coroutines.coroutineContext + + +/** + * Create bot using [telegramBot] and start listening for updates using [buildBehaviour]. + * Use this method in case you wish to make some additional actions with [flowsUpdatesFilter]. + * + * **WARNING** This method WILL NOT launch any listening of updates. Use something like + * [startGettingOfUpdatesByLongPolling] or tools for work with webhooks + * + * @return Created bot which has been used to create [BehaviourContext] via [buildBehaviour] + * + * @see [BehaviourContext] + * @see [buildBehaviour] + * @see startGettingOfUpdatesByLongPolling + */ +suspend fun telegramBotWithBehaviourAndFSM( + token: String, + flowsUpdatesFilter: FlowsUpdatesFilter, + scope: CoroutineScope? = null, + apiUrl: String = telegramBotAPIDefaultUrl, + builder: KtorRequestsExecutorBuilder.() -> Unit = {}, + defaultExceptionsHandler: ExceptionHandler? = null, + statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), + presetHandlers: MutableList> = mutableListOf(), + block: BehaviourContextReceiver +): TelegramBot = telegramBot( + token, + apiUrl, + builder +).apply { + buildBehaviourWithFSM( + flowsUpdatesFilter, + scope ?: CoroutineScope(coroutineContext), + defaultExceptionsHandler, + statesManager, + presetHandlers, + block + ) +} + +/** + * Create bot using [telegramBot] and start listening for updates using [buildBehaviour]. + * Use this method in case you wish to make some additional actions with [flowsUpdatesFilter]. + * + * **WARNING** This method WILL NOT launch any listening of updates. Use something like + * [startGettingOfUpdatesByLongPolling] or tools for work with webhooks + * + * @return Pair of [TelegramBot] and [Job]. This [Job] can be used to stop listening updates in your [block] you passed + * here + * + * @see [BehaviourContext] + * @see [buildBehaviour] + * @see startGettingOfUpdatesByLongPolling + */ +suspend fun telegramBotWithBehaviourAndFSM( + token: String, + scope: CoroutineScope? = null, + apiUrl: String = telegramBotAPIDefaultUrl, + builder: KtorRequestsExecutorBuilder.() -> Unit = {}, + defaultExceptionsHandler: ExceptionHandler? = null, + statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), + presetHandlers: MutableList> = mutableListOf(), + block: BehaviourContextReceiver +): Pair { + return telegramBot( + token, + apiUrl, + builder + ).let { + it to it.buildBehaviourWithFSM ( + scope ?: CoroutineScope(coroutineContext), + defaultExceptionsHandler, + statesManager, + presetHandlers, + block + ) + } +}