diff --git a/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSM.kt b/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSM.kt index be5cdb8923..98ec32c183 100644 --- a/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSM.kt +++ b/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSM.kt @@ -9,6 +9,8 @@ import dev.inmo.micro_utils.coroutines.accumulatorFlow import kotlinx.coroutines.* import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.* +import kotlin.jvm.JvmName +import kotlin.reflect.KClass /** * Interface which combine [BehaviourContext] and [StatesMachine]. Subcontext of triggers and states contexts must have @@ -30,6 +32,25 @@ interface BehaviourContextWithFSM : BehaviourContext, StatesMachine add(kClass: KClass, strict: Boolean = false, handler: BehaviourWithFSMStateHandler) + + /** + * Add STRICT [handler] to list of available in future [BehaviourContextWithFSM]. Strict means that + * for input [State] will be used [State]::class == [kClass] and any [State] with exactly the same type will pass + * requirements + * + * @see BehaviourWithFSMStateHandlerHolder + * @see strictlyOn + */ + fun addStrict(kClass: KClass, handler: BehaviourWithFSMStateHandler) = add(kClass, strict = true, handler) + override fun copy( bot: TelegramBot, scope: CoroutineScope, @@ -40,14 +61,42 @@ interface BehaviourContextWithFSM : BehaviourContext, StatesMachine companion object { + @JvmName("invokeWithMutableList") + operator fun invoke( + behaviourContext: BehaviourContext, + handlers: MutableList>, + statesManager: StatesManager + ) = DefaultBehaviourContextWithFSM(behaviourContext, statesManager, handlers.toMutableList()) operator fun invoke( behaviourContext: BehaviourContext, handlers: List>, statesManager: StatesManager - ) = DefaultBehaviourContextWithFSM(behaviourContext, statesManager, handlers) + ) = invoke(behaviourContext, handlers.toMutableList(), statesManager) } } + +/** + * Add NON STRICT [handler] to list of available in future [BehaviourContextWithFSM]. Non strict means that + * for input [State] will be used [KClass.isInstance] and any inheritor of [kClass] will pass this requirement + * + * @see BehaviourWithFSMStateHandlerHolder + * @see BehaviourContextWithFSM.add + */ +@Suppress("MemberVisibilityCanBePrivate") +inline fun BehaviourContextWithFSM.onStateOrSubstate(handler: BehaviourWithFSMStateHandler) = add(I::class, strict = false, handler) + +/** + * Add STRICT [handler] to list of available in future [BehaviourContextWithFSM]. Strict means that + * for input [State] will be used [State]::class == [kClass] and any [State] with exactly the same type will pass + * requirements + * + * @see BehaviourWithFSMStateHandlerHolder + * @see BehaviourContextWithFSM.addStrict + */ +@Suppress("MemberVisibilityCanBePrivate") +inline fun BehaviourContextWithFSM.strictlyOn(handler: BehaviourWithFSMStateHandler) = addStrict(I::class, handler) + /** * 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 @@ -55,7 +104,7 @@ interface BehaviourContextWithFSM : BehaviourContext, StatesMachine( private val behaviourContext: BehaviourContext, private val statesManager: StatesManager, - private val handlers: List> + private val handlers: MutableList> ) : BehaviourContext by behaviourContext, BehaviourContextWithFSM { private val updatesFlows = mutableMapOf>() private fun getContextUpdatesFlow(context: Any) = updatesFlows.getOrPut(context) { @@ -68,6 +117,10 @@ class DefaultBehaviourContextWithFSM( handlers ) + override fun add(kClass: KClass, strict: Boolean, handler: BehaviourWithFSMStateHandler) { + handlers.add(BehaviourWithFSMStateHandlerHolder(kClass, strict, handler)) + } + override fun start(scope: CoroutineScope): Job = scope.launchSafelyWithoutExceptions { val statePerformer: suspend (T) -> Unit = { state: T -> val newState = launchStateHandling(state, getContextUpdatesFlow(state.context), handlers) @@ -106,9 +159,9 @@ class DefaultBehaviourContextWithFSM( onBufferOverflow: BufferOverflow, upstreamUpdatesFlow: Flow?, updatesFilter: BehaviourContextAndTypeReceiver? - ): BehaviourContextWithFSM = BehaviourContextWithFSM( + ): DefaultBehaviourContextWithFSM = BehaviourContextWithFSM( behaviourContext.copy(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, updatesFilter), - handlers, + handlers.toMutableList(), statesManager ) } diff --git a/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSMBuilder.kt b/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSMBuilder.kt index 7723b81c70..2e5cfe4615 100644 --- a/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSMBuilder.kt +++ b/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSMBuilder.kt @@ -14,70 +14,8 @@ import kotlinx.coroutines.* import kotlinx.coroutines.flow.Flow import kotlin.reflect.KClass -class BehaviourContextWithFSMBuilder internal constructor( - private val resultBehaviourContext: BehaviourContextWithFSM, - private val handlers: MutableList> -) : BehaviourContextWithFSM by resultBehaviourContext { - internal constructor( - baseBehaviourContext: BehaviourContext, - statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), - handlers: MutableList> = mutableListOf() - ) : this(DefaultBehaviourContextWithFSM(baseBehaviourContext, statesManager, handlers), handlers) - - /** - * Add NON STRICT [handler] to list of available in future [BehaviourContextWithFSM]. Non strict means that - * for input [State] will be used [KClass.isInstance] and any inheritor of [kClass] will pass this requirement - * - * @see BehaviourWithFSMStateHandlerHolder - * @see onStateOrSubstate - */ - fun add(kClass: KClass, handler: BehaviourWithFSMStateHandler) { - handlers.add(BehaviourWithFSMStateHandlerHolder(kClass, false, handler)) - } - - /** - * Add STRICT [handler] to list of available in future [BehaviourContextWithFSM]. Strict means that - * for input [State] will be used [State]::class == [kClass] and any [State] with exactly the same type will pass - * requirements - * - * @see BehaviourWithFSMStateHandlerHolder - * @see strictlyOn - */ - fun addStrict(kClass: KClass, handler: BehaviourWithFSMStateHandler) { - handlers.add(BehaviourWithFSMStateHandlerHolder(kClass, true, handler)) - } - - - /** - * Add NON STRICT [handler] to list of available in future [BehaviourContextWithFSM]. Non strict means that - * for input [State] will be used [KClass.isInstance] and any inheritor of [kClass] will pass this requirement - * - * @see BehaviourWithFSMStateHandlerHolder - * @see BehaviourContextWithFSMBuilder.add - */ - @Suppress("MemberVisibilityCanBePrivate") - inline fun onStateOrSubstate(handler: BehaviourWithFSMStateHandler) { - add(I::class, handler) - } - - /** - * Add STRICT [handler] to list of available in future [BehaviourContextWithFSM]. Strict means that - * for input [State] will be used [State]::class == [kClass] and any [State] with exactly the same type will pass - * requirements - * - * @see BehaviourWithFSMStateHandlerHolder - * @see BehaviourContextWithFSMBuilder.addStrict - */ - @Suppress("MemberVisibilityCanBePrivate") - inline fun strictlyOn(handler: BehaviourWithFSMStateHandler) { - addStrict(I::class, handler) - } - - /** - * Returns completed [resultBehaviourContext], [handlers] and [statesManager] - */ - internal fun build() = resultBehaviourContext -} +@Deprecated("Will be removed soon") +typealias BehaviourContextWithFSMBuilder = BehaviourContextWithFSM /** * Creates [BehaviourContextWithFSM] via creating of [DefaultBehaviourContext] with [this] as [TelegramBot], @@ -95,16 +33,16 @@ suspend fun TelegramBot.buildBehaviourWithFSM( defaultExceptionsHandler: ExceptionHandler? = null, statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), presetHandlers: MutableList> = mutableListOf(), - block: CustomBehaviourContextReceiver, Unit> -): BehaviourContextWithFSM = BehaviourContextWithFSMBuilder( + block: CustomBehaviourContextReceiver, Unit> +): BehaviourContextWithFSM = BehaviourContextWithFSM( DefaultBehaviourContext( this, defaultExceptionsHandler ?.let { scope + ContextSafelyExceptionHandler(it) } ?: scope, upstreamUpdatesFlow = upstreamUpdatesFlow ), - statesManager, - presetHandlers -).apply { block() }.build() + presetHandlers, + statesManager +).apply { block() } /** * Use [buildBehaviourWithFSM] to create [BehaviourContextWithFSM] and launch getting of updates @@ -117,7 +55,7 @@ suspend fun TelegramBot.buildBehaviourWithFSMAndStartLongPolling( defaultExceptionsHandler: ExceptionHandler? = null, statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), presetHandlers: MutableList> = mutableListOf(), - block: CustomBehaviourContextReceiver, Unit> + block: CustomBehaviourContextReceiver, Unit> ): Pair, Job> = buildBehaviourWithFSM( upstreamUpdatesFlow, scope, @@ -147,8 +85,8 @@ suspend fun TelegramBot.buildBehaviourWithFSMAndStartLongPolling( * @see BehaviourContext * @see BehaviourContextWithFSM * @see longPolling - * @see BehaviourContextWithFSMBuilder.strictlyOn - * @see BehaviourContextWithFSMBuilder.onStateOrSubstate + * @see BehaviourContextWithFSM.strictlyOn + * @see BehaviourContextWithFSM.onStateOrSubstate */ @PreviewFeature suspend fun TelegramBot.buildBehaviourWithFSM( @@ -157,16 +95,16 @@ suspend fun TelegramBot.buildBehaviourWithFSM( defaultExceptionsHandler: ExceptionHandler? = null, statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), presetHandlers: MutableList> = mutableListOf(), - block: CustomBehaviourContextReceiver, Unit> -): BehaviourContextWithFSM = BehaviourContextWithFSMBuilder( + block: CustomBehaviourContextReceiver, Unit> +): BehaviourContextWithFSM = BehaviourContextWithFSM( DefaultBehaviourContext( this, defaultExceptionsHandler ?.let { scope + ContextSafelyExceptionHandler(it) } ?: scope, upstreamUpdatesFlow = flowUpdatesFilter.allUpdatesFlow ), - statesManager, - presetHandlers -).apply { block() }.build() + presetHandlers, + statesManager +).apply { block() } /** * Use [buildBehaviourWithFSM] to create [BehaviourContextWithFSM] and launch getting of updates @@ -176,8 +114,8 @@ suspend fun TelegramBot.buildBehaviourWithFSM( * @see buildBehaviourWithFSMAndStartLongPolling * @see BehaviourContext * @see longPolling - * @see BehaviourContextWithFSMBuilder.strictlyOn - * @see BehaviourContextWithFSMBuilder.onStateOrSubstate + * @see BehaviourContextWithFSM.strictlyOn + * @see BehaviourContextWithFSM.onStateOrSubstate */ @PreviewFeature suspend fun TelegramBot.buildBehaviourWithFSMAndStartLongPolling( @@ -185,7 +123,7 @@ suspend fun TelegramBot.buildBehaviourWithFSMAndStartLongPolling( defaultExceptionsHandler: ExceptionHandler? = null, statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), presetHandlers: MutableList> = mutableListOf(), - block: CustomBehaviourContextReceiver, Unit> + block: CustomBehaviourContextReceiver, Unit> ) = FlowsUpdatesFilter().let { buildBehaviourWithFSM( it, diff --git a/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/TelegramBotWithFSM.kt b/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/TelegramBotWithFSM.kt index edfc67de48..91e5d64e43 100644 --- a/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/TelegramBotWithFSM.kt +++ b/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/TelegramBotWithFSM.kt @@ -39,7 +39,7 @@ suspend fun telegramBotWithBehaviourAndFSM( statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), presetHandlers: MutableList> = mutableListOf(), testServer: Boolean = false, - block: CustomBehaviourContextReceiver, Unit> + block: CustomBehaviourContextReceiver, Unit> ): TelegramBot = telegramBot( token, apiUrl, @@ -76,7 +76,7 @@ suspend fun telegramBotWithBehaviourAndFSMAndStartLongPolling( statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), presetHandlers: MutableList> = mutableListOf(), testServer: Boolean = false, - block: CustomBehaviourContextReceiver, Unit> + block: CustomBehaviourContextReceiver, Unit> ): Pair { return telegramBot( token,