1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2024-06-02 07:55:25 +00:00
tgbotapi/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSMBuilder.kt

205 lines
9.0 KiB
Kotlin
Raw Normal View History

2021-10-13 08:22:01 +00:00
package dev.inmo.tgbotapi.extensions.behaviour_builder
2021-10-15 11:13:03 +00:00
import dev.inmo.micro_utils.coroutines.*
2021-10-13 08:22:01 +00:00
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
2021-10-13 08:22:01 +00:00
import dev.inmo.tgbotapi.types.update.abstracts.Update
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import dev.inmo.tgbotapi.utils.PreviewFeature
import kotlinx.coroutines.*
2021-10-13 08:22:01 +00:00
import kotlinx.coroutines.flow.Flow
import kotlin.reflect.KClass
class BehaviourContextWithFSMBuilder internal constructor(
private val resultBehaviourContext: BehaviourContextWithFSM,
private val handlers: MutableList<BehaviourWithFSMStateHandlerHolder<*>>
) : BehaviourContextWithFSM by resultBehaviourContext {
internal constructor(
baseBehaviourContext: BehaviourContext,
statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
handlers: MutableList<BehaviourWithFSMStateHandlerHolder<*>> = 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 <I : State> add(kClass: KClass<I>, handler: BehaviourWithFSMStateHandler<I>) {
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 <I : State> addStrict(kClass: KClass<I>, handler: BehaviourWithFSMStateHandler<I>) {
handlers.add(BehaviourWithFSMStateHandlerHolder(kClass, true, handler))
}
2021-10-15 11:13:03 +00:00
2021-10-13 08:22:01 +00:00
/**
2021-10-15 11:13:03 +00:00
* 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
2021-10-13 08:22:01 +00:00
*/
2021-10-15 11:13:03 +00:00
@Suppress("MemberVisibilityCanBePrivate")
inline fun <reified I : State> onStateOrSubstate(handler: BehaviourWithFSMStateHandler<I>) {
add(I::class, handler)
}
2021-10-13 08:22:01 +00:00
2021-10-15 11:13:03 +00:00
/**
* 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 <reified I : State> strictlyOn(handler: BehaviourWithFSMStateHandler<I>) {
addStrict(I::class, handler)
}
2021-10-13 08:22:01 +00:00
2021-10-15 11:13:03 +00:00
/**
* Returns completed [resultBehaviourContext], [handlers] and [statesManager]
*/
internal fun build() = resultBehaviourContext
2021-10-13 08:22:01 +00:00
}
/**
2021-10-15 11:13:03 +00:00
* Creates [BehaviourContextWithFSM] via creating of [DefaultBehaviourContext] with [this] as [TelegramBot],
* [scope] as target scope for that [DefaultBehaviourContext] and [upstreamUpdatesFlow]. Pass [statesManager]
* to customize some internal logic of states changes. Pass [presetHandlers] in case you have some list of
* [BehaviourWithFSMStateHandlerHolder] with presets.
2021-10-13 08:22:01 +00:00
*
2021-10-15 11:13:03 +00:00
* !!! WARNING !!! This method WILL NOT call [BehaviourContextWithFSM.start] of result object and WILL NOT
* start any updates retrieving. See [buildBehaviourWithFSMAndStartLongPolling] or
* [telegramBotWithBehaviourAndFSMAndStartLongPolling] in case you wish to start [longPolling] automatically
2021-10-13 08:22:01 +00:00
*/
suspend fun TelegramBot.buildBehaviourWithFSM(
upstreamUpdatesFlow: Flow<Update>? = null,
scope: CoroutineScope = defaultCoroutineScopeProvider(),
2021-10-15 11:13:03 +00:00
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
2021-10-15 11:13:03 +00:00
presetHandlers: MutableList<BehaviourWithFSMStateHandlerHolder<*>> = mutableListOf(),
2021-10-15 10:37:20 +00:00
block: CustomBehaviourContextReceiver<BehaviourContextWithFSMBuilder, Unit>
2021-10-15 11:13:03 +00:00
): BehaviourContextWithFSM = BehaviourContextWithFSMBuilder(
DefaultBehaviourContext(
this,
defaultExceptionsHandler ?.let { scope + ContextSafelyExceptionHandler(it) } ?: scope,
upstreamUpdatesFlow = upstreamUpdatesFlow
),
statesManager,
2021-10-15 11:13:03 +00:00
presetHandlers
2021-10-13 08:22:01 +00:00
).apply { block() }.build()
/**
2021-10-15 11:13:03 +00:00
* Use [buildBehaviourWithFSM] to create [BehaviourContextWithFSM] and launch getting of updates
* using [longPolling]. For [longPolling] will be used result [BehaviourContextWithFSM] for both parameters
* flowsUpdatesFilter and scope
2021-10-13 08:22:01 +00:00
*/
2021-10-15 11:13:03 +00:00
suspend fun TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
2021-10-13 08:22:01 +00:00
upstreamUpdatesFlow: Flow<Update>? = null,
scope: CoroutineScope = defaultCoroutineScopeProvider(),
2021-10-15 11:13:03 +00:00
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: MutableList<BehaviourWithFSMStateHandlerHolder<*>> = mutableListOf(),
2021-10-15 10:37:20 +00:00
block: CustomBehaviourContextReceiver<BehaviourContextWithFSMBuilder, Unit>
2021-10-15 11:13:03 +00:00
): Pair<BehaviourContextWithFSM, Job> = buildBehaviourWithFSM(
upstreamUpdatesFlow,
scope,
defaultExceptionsHandler,
statesManager,
presetHandlers,
block
).run {
2021-10-13 08:22:01 +00:00
this to scope.launch {
start()
longPolling(flowsUpdatesFilter, scope = scope)
}
}
/**
2021-10-15 11:13:03 +00:00
* Creates [BehaviourContextWithFSM] via creating of [DefaultBehaviourContext] with [this] as [TelegramBot],
* [scope] as target scope for that [DefaultBehaviourContext] and [FlowsUpdatesFilter.allUpdatesFlow] of
* [flowUpdatesFilter] as [DefaultBehaviourContext.upstreamUpdatesFlow]. Pass [statesManager]
* to customize some internal logic of states changes. Pass [presetHandlers] in case you have some list of
* [BehaviourWithFSMStateHandlerHolder] with presets.
* Use this method in case you wish to make some additional actions with [flowUpdatesFilter].
*
2021-10-15 11:13:03 +00:00
* !!! WARNING !!! This method WILL NOT call [BehaviourContextWithFSM.start] of result object and WILL NOT
* start any updates retrieving. See [buildBehaviourWithFSMAndStartLongPolling] or
* [telegramBotWithBehaviourAndFSMAndStartLongPolling] in case you wish to start [longPolling] automatically
*
* @see BehaviourContext
* @see BehaviourContextWithFSM
* @see longPolling
2021-10-15 11:13:03 +00:00
* @see BehaviourContextWithFSMBuilder.strictlyOn
* @see BehaviourContextWithFSMBuilder.onStateOrSubstate
*/
@PreviewFeature
suspend fun TelegramBot.buildBehaviourWithFSM(
flowUpdatesFilter: FlowsUpdatesFilter,
scope: CoroutineScope = defaultCoroutineScopeProvider(),
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: MutableList<BehaviourWithFSMStateHandlerHolder<*>> = mutableListOf(),
block: CustomBehaviourContextReceiver<BehaviourContextWithFSMBuilder, Unit>
): BehaviourContextWithFSM = BehaviourContextWithFSMBuilder(
2021-10-15 11:13:03 +00:00
DefaultBehaviourContext(
this,
2021-10-15 11:13:03 +00:00
defaultExceptionsHandler ?.let { scope + ContextSafelyExceptionHandler(it) } ?: scope,
upstreamUpdatesFlow = flowUpdatesFilter.allUpdatesFlow
),
statesManager,
presetHandlers
).apply { block() }.build()
/**
2021-10-15 11:13:03 +00:00
* Use [buildBehaviourWithFSM] to create [BehaviourContextWithFSM] and launch getting of updates
* using [longPolling]. For [longPolling] will be used result [BehaviourContextWithFSM] for both parameters
* flowsUpdatesFilter and scope
*
2021-10-15 11:13:03 +00:00
* @see buildBehaviourWithFSMAndStartLongPolling
* @see BehaviourContext
* @see longPolling
2021-10-15 11:13:03 +00:00
* @see BehaviourContextWithFSMBuilder.strictlyOn
* @see BehaviourContextWithFSMBuilder.onStateOrSubstate
*/
@PreviewFeature
2021-10-15 11:13:03 +00:00
suspend fun TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
scope: CoroutineScope = defaultCoroutineScopeProvider(),
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: MutableList<BehaviourWithFSMStateHandlerHolder<*>> = mutableListOf(),
2021-10-15 10:37:20 +00:00
block: CustomBehaviourContextReceiver<BehaviourContextWithFSMBuilder, Unit>
) = FlowsUpdatesFilter().let {
buildBehaviourWithFSM(
it,
scope,
defaultExceptionsHandler,
statesManager,
presetHandlers,
block
).run {
start()
longPolling(
flowsUpdatesFilter,
scope = scope
)
2021-10-13 08:22:01 +00:00
}
}