mirror of
https://github.com/InsanusMokrassar/TelegramBotAPI.git
synced 2024-11-25 03:28:44 +00:00
fixes in behaviour builders
This commit is contained in:
parent
bd32dbb3ab
commit
d55d8fa000
@ -50,6 +50,15 @@ __All the `tgbotapi.extensions.*` packages have been removed__
|
|||||||
* New typealias `FileUrl` (represents `FileId` but declare that they are the same)
|
* New typealias `FileUrl` (represents `FileId` but declare that they are the same)
|
||||||
* `BehaviourBuilder`:
|
* `BehaviourBuilder`:
|
||||||
* `SimpleFilter` now is a `fun interface` instead of just callback (fix of [#546](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/546))
|
* `SimpleFilter` now is a `fun interface` instead of just callback (fix of [#546](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/546))
|
||||||
|
* New extension `BehaviourContext#createSubContext`. It uses separated `AccumulatorFlow` and will retrieve updates by itself
|
||||||
|
* New extension `BehaviourContext#doInContext`
|
||||||
|
* `BehaviourContextWithFSM`:
|
||||||
|
* `launchStateHandling` lost its parameter `contextUpdatesFlow: Flow`
|
||||||
|
* `handleState` of `BehaviourContextWithFSM` now will get/create sub context for the state and launch handling in it
|
||||||
|
* `BehaviourWithFSMStateHandler` now extends `StatesHandler`
|
||||||
|
* `BehaviourWithFSMStateHandlerHolder` now extends `CheckableHandlerHolder` and `BehaviourWithFSMStateHandler`
|
||||||
|
* Function `checkHandleable` of `BehaviourWithFSMStateHandlerHolder` now is `suspend`
|
||||||
|
*
|
||||||
|
|
||||||
## 0.38.23
|
## 0.38.23
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ klock_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.2
|
micro_utils_version=0.10.3
|
||||||
|
|
||||||
javax_activation_version=1.1.1
|
javax_activation_version=1.1.1
|
||||||
|
|
||||||
|
@ -5,12 +5,10 @@ import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
|||||||
import dev.inmo.micro_utils.fsm.common.*
|
import dev.inmo.micro_utils.fsm.common.*
|
||||||
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.micro_utils.coroutines.accumulatorFlow
|
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.handlers_registrar.TriggersHolder
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.handlers_registrar.TriggersHolder
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.channels.BufferOverflow
|
import kotlinx.coroutines.channels.BufferOverflow
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlin.jvm.JvmName
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -23,18 +21,6 @@ import kotlin.reflect.KClass
|
|||||||
interface BehaviourContextWithFSM<T : State> : BehaviourContext, StatesMachine<T> {
|
interface BehaviourContextWithFSM<T : State> : BehaviourContext, StatesMachine<T> {
|
||||||
suspend fun start() = start(this)
|
suspend fun start() = start(this)
|
||||||
|
|
||||||
suspend fun launchStateHandling(
|
|
||||||
state: T,
|
|
||||||
contextUpdatesFlow: Flow<Update>,
|
|
||||||
handlers: List<BehaviourWithFSMStateHandlerHolder<*, T>>
|
|
||||||
): T? {
|
|
||||||
return handlers.firstOrNull { it.checkHandleable(state) } ?.run {
|
|
||||||
doInSubContext(updatesUpstreamFlow = contextUpdatesFlow) {
|
|
||||||
handleState(state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add NON STRICT [handler] to list of available in future [BehaviourContextWithFSM]. Non strict means that
|
* 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
|
* for input [State] will be used [KClass.isInstance] and any inheritor of [kClass] will pass this requirement
|
||||||
@ -104,19 +90,22 @@ class DefaultBehaviourContextWithFSM<T : State>(
|
|||||||
private val statesManager: StatesManager<T>,
|
private val statesManager: StatesManager<T>,
|
||||||
private val handlers: List<BehaviourWithFSMStateHandlerHolder<*, T>>
|
private val handlers: List<BehaviourWithFSMStateHandlerHolder<*, T>>
|
||||||
) : BehaviourContext by behaviourContext, BehaviourContextWithFSM<T> {
|
) : BehaviourContext by behaviourContext, BehaviourContextWithFSM<T> {
|
||||||
private val updatesFlows = mutableMapOf<Any, Flow<Update>>()
|
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
|
||||||
|
|
||||||
private fun getContextUpdatesFlow(context: Any) = updatesFlows.getOrPut(context) {
|
private suspend fun getSubContext(context: Any) = updatesFlows.getOrPut(context) {
|
||||||
allUpdatesFlow.accumulatorFlow(scope)
|
createSubContext()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun StatesMachine<in T>.handleState(state: T): T? = launchStateHandling(
|
override suspend fun StatesMachine<in T>.handleState(state: T): T? {
|
||||||
|
return getSubContext(
|
||||||
|
state.context
|
||||||
|
).launchStateHandling(
|
||||||
state,
|
state,
|
||||||
allUpdatesFlow,
|
|
||||||
actualHandlersList
|
actualHandlersList
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override fun <I : T> add(kClass: KClass<I>, strict: Boolean, handler: BehaviourWithFSMStateHandler<I, T>) {
|
override fun <I : T> add(kClass: KClass<I>, strict: Boolean, handler: BehaviourWithFSMStateHandler<I, T>) {
|
||||||
additionalHandlers.add(BehaviourWithFSMStateHandlerHolder(kClass, strict, handler))
|
additionalHandlers.add(BehaviourWithFSMStateHandlerHolder(kClass, strict, handler))
|
||||||
@ -125,7 +114,7 @@ class DefaultBehaviourContextWithFSM<T : State>(
|
|||||||
|
|
||||||
override fun start(scope: CoroutineScope): Job = scope.launchSafelyWithoutExceptions {
|
override fun start(scope: CoroutineScope): Job = scope.launchSafelyWithoutExceptions {
|
||||||
val statePerformer: suspend (T) -> Unit = { state: T ->
|
val statePerformer: suspend (T) -> Unit = { state: T ->
|
||||||
val newState = launchStateHandling(state, getContextUpdatesFlow(state.context), actualHandlersList)
|
val newState = getSubContext(state.context).launchStateHandling(state, actualHandlersList)
|
||||||
if (newState != null) {
|
if (newState != null) {
|
||||||
statesManager.update(state, newState)
|
statesManager.update(state, newState)
|
||||||
} else {
|
} else {
|
||||||
|
@ -2,6 +2,12 @@ package dev.inmo.tgbotapi.extensions.behaviour_builder
|
|||||||
|
|
||||||
import dev.inmo.micro_utils.fsm.common.*
|
import dev.inmo.micro_utils.fsm.common.*
|
||||||
|
|
||||||
fun interface BehaviourWithFSMStateHandler<I : O, O : State> {
|
fun interface BehaviourWithFSMStateHandler<I : O, O : State> : StatesHandler<I, O> {
|
||||||
suspend fun BehaviourContextWithFSM<in O>.handleState(state: I): O?
|
suspend fun BehaviourContextWithFSM<in O>.handleState(state: I): O?
|
||||||
|
|
||||||
|
override suspend fun StatesMachine<in O>.handleState(state: I): O? = if (this is BehaviourContextWithFSM) {
|
||||||
|
handleState(state)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,6 @@
|
|||||||
package dev.inmo.tgbotapi.extensions.behaviour_builder
|
package dev.inmo.tgbotapi.extensions.behaviour_builder
|
||||||
|
|
||||||
import dev.inmo.micro_utils.coroutines.LinkedSupervisorScope
|
|
||||||
import dev.inmo.micro_utils.coroutines.weakLaunch
|
|
||||||
import dev.inmo.micro_utils.fsm.common.*
|
import dev.inmo.micro_utils.fsm.common.*
|
||||||
import dev.inmo.tgbotapi.types.update.abstracts.Update
|
|
||||||
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.coroutines.flow.collect
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -23,24 +17,28 @@ class BehaviourWithFSMStateHandlerHolder<I : O, O : State>(
|
|||||||
private val inputKlass: KClass<I>,
|
private val inputKlass: KClass<I>,
|
||||||
private val strict: Boolean = false,
|
private val strict: Boolean = false,
|
||||||
private val delegateTo: BehaviourWithFSMStateHandler<I, O>
|
private val delegateTo: BehaviourWithFSMStateHandler<I, O>
|
||||||
) {
|
) : CheckableHandlerHolder<O, O>, BehaviourWithFSMStateHandler<O, O> {
|
||||||
/**
|
/**
|
||||||
* Check ability of [delegateTo] to handle this [state]
|
* Check ability of [delegateTo] to handle this [state]
|
||||||
*
|
*
|
||||||
* @return When [state]::class exactly equals to [inputKlass] will always return true. Otherwise when [strict]
|
* @return When [state]::class exactly equals to [inputKlass] will always return true. Otherwise when [strict]
|
||||||
* mode is disabled, will be used [KClass.isInstance] of [inputKlass] for checking
|
* mode is disabled, will be used [KClass.isInstance] of [inputKlass] for checking
|
||||||
*/
|
*/
|
||||||
fun checkHandleable(state: O): Boolean = state::class == inputKlass || (!strict && inputKlass.isInstance(state))
|
override suspend fun checkHandleable(state: O): Boolean = state::class == inputKlass || (!strict && inputKlass.isInstance(state))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handling of state :)
|
* Handling of state :)
|
||||||
*/
|
*/
|
||||||
suspend fun BehaviourContextWithFSM<in O>.handleState(
|
override suspend fun BehaviourContextWithFSM<in O>.handleState(state: O): O? = with(delegateTo) {
|
||||||
state: O
|
|
||||||
): O? = with(delegateTo) {
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
handleState(state as I)
|
handleState(state as I)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun StatesMachine<in O>.handleState(state: O): O? = if (this is BehaviourContextWithFSM) {
|
||||||
|
handleState(state)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified I : O, O : State> BehaviourWithFSMStateHandlerHolder(
|
inline fun <reified I : O, O : State> BehaviourWithFSMStateHandlerHolder(
|
||||||
|
@ -84,7 +84,7 @@ class DefaultBehaviourContext(
|
|||||||
} else {
|
} else {
|
||||||
it
|
it
|
||||||
}
|
}
|
||||||
}
|
}.accumulatorFlow(scope)
|
||||||
override val asUpdateReceiver: UpdateReceiver<Update> = additionalUpdatesSharedFlow::emit
|
override val asUpdateReceiver: UpdateReceiver<Update> = additionalUpdatesSharedFlow::emit
|
||||||
|
|
||||||
override fun copy(
|
override fun copy(
|
||||||
@ -113,6 +113,35 @@ 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)
|
||||||
|
|
||||||
|
suspend fun <BC : BehaviourContext> BC.createSubContext(
|
||||||
|
scope: CoroutineScope = LinkedSupervisorScope(),
|
||||||
|
triggersHolder: TriggersHolder = this.triggersHolder,
|
||||||
|
updatesUpstreamFlow: Flow<Update> = allUpdatesFlow.accumulatorFlow(scope),
|
||||||
|
updatesFilter: CustomBehaviourContextAndTypeReceiver<BC, Boolean, Update>? = null,
|
||||||
|
) = copy(
|
||||||
|
scope = scope,
|
||||||
|
updatesFilter = updatesFilter ?.let { _ ->
|
||||||
|
{
|
||||||
|
(this as? BC) ?.run {
|
||||||
|
updatesFilter(it)
|
||||||
|
} ?: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
upstreamUpdatesFlow = updatesUpstreamFlow,
|
||||||
|
triggersHolder = triggersHolder
|
||||||
|
) as BC
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new one [BehaviourContext], adding subsequent [FlowsUpdatesFilter] in case [updatesFilter] is provided and
|
||||||
|
* [CoroutineScope] as new [BehaviourContext.scope]
|
||||||
|
*/
|
||||||
|
suspend fun <T, BC : BehaviourContext> BC.doInContext(
|
||||||
|
stopOnCompletion: Boolean = true,
|
||||||
|
behaviourContextReceiver: CustomBehaviourContextReceiver<BC, T>
|
||||||
|
): T {
|
||||||
|
return behaviourContextReceiver().also { if (stopOnCompletion) stop() }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new one [BehaviourContext], adding subsequent [FlowsUpdatesFilter] in case [updatesFilter] is provided and
|
* Creates new one [BehaviourContext], adding subsequent [FlowsUpdatesFilter] in case [updatesFilter] is provided and
|
||||||
* [CoroutineScope] as new [BehaviourContext.scope]
|
* [CoroutineScope] as new [BehaviourContext.scope]
|
||||||
@ -125,21 +154,15 @@ suspend fun <T, BC : BehaviourContext> BC.doInSubContextWithUpdatesFilter(
|
|||||||
triggersHolder: TriggersHolder = this.triggersHolder,
|
triggersHolder: TriggersHolder = this.triggersHolder,
|
||||||
behaviourContextReceiver: CustomBehaviourContextReceiver<BC, T>
|
behaviourContextReceiver: CustomBehaviourContextReceiver<BC, T>
|
||||||
): T {
|
): T {
|
||||||
val newContext = copy(
|
return createSubContext(
|
||||||
scope = scope,
|
scope,
|
||||||
updatesFilter = updatesFilter ?.let { _ ->
|
triggersHolder,
|
||||||
{
|
updatesUpstreamFlow,
|
||||||
(this as? BC) ?.run {
|
updatesFilter
|
||||||
updatesFilter(it)
|
).doInContext(
|
||||||
} ?: true
|
stopOnCompletion,
|
||||||
}
|
behaviourContextReceiver
|
||||||
},
|
)
|
||||||
upstreamUpdatesFlow = updatesUpstreamFlow,
|
|
||||||
triggersHolder = triggersHolder
|
|
||||||
) as BC
|
|
||||||
return withContext(currentCoroutineContext()) {
|
|
||||||
newContext.behaviourContextReceiver().also { if (stopOnCompletion) newContext.stop() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun <T> BehaviourContext.doInSubContext(
|
suspend fun <T> BehaviourContext.doInSubContext(
|
||||||
|
Loading…
Reference in New Issue
Block a user