1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2024-11-22 16:23:48 +00:00

Merge pull request #579 from InsanusMokrassar/0.38.19

0.38.19
This commit is contained in:
InsanusMokrassar 2022-05-03 14:36:22 +06:00 committed by GitHub
commit ba4c153659
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 110 additions and 93 deletions

View File

@ -1,5 +1,13 @@
# TelegramBotAPI changelog # TelegramBotAPI changelog
## 0.38.19
* `BehaviourBuilder`:
* Hotfixes
* `BehaviourBuilder FSM`:
* `BehaviourContextWithFSMBuilder` deprecated in favor to `BehaviourContextWithFSM`
* Now it is possible to define additional handlers in subcontexts of `BehaviourBuilderWithFSM`
## 0.38.18 ## 0.38.18
* `Core`: * `Core`:

View File

@ -20,6 +20,6 @@ javax_activation_version=1.1.1
dokka_version=1.6.10 dokka_version=1.6.10
library_group=dev.inmo library_group=dev.inmo
library_version=0.38.18 library_version=0.38.19
github_release_plugin_version=2.3.7 github_release_plugin_version=2.3.7

View File

@ -9,6 +9,8 @@ import dev.inmo.micro_utils.coroutines.accumulatorFlow
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
/** /**
* Interface which combine [BehaviourContext] and [StatesMachine]. Subcontext of triggers and states contexts must have * Interface which combine [BehaviourContext] and [StatesMachine]. Subcontext of triggers and states contexts must have
@ -30,6 +32,25 @@ interface BehaviourContextWithFSM<T : State> : BehaviourContext, StatesMachine<T
} }
} }
/**
* 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 : T> add(kClass: KClass<I>, strict: Boolean = false, handler: BehaviourWithFSMStateHandler<I, T>)
/**
* 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 : T> addStrict(kClass: KClass<I>, handler: BehaviourWithFSMStateHandler<I, T>) = add(kClass, strict = true, handler)
override fun copy( override fun copy(
bot: TelegramBot, bot: TelegramBot,
scope: CoroutineScope, scope: CoroutineScope,
@ -48,6 +69,28 @@ interface BehaviourContextWithFSM<T : State> : BehaviourContext, StatesMachine<T
} }
} }
/**
* 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 <reified I : O, O: State> BehaviourContextWithFSM<O>.onStateOrSubstate(handler: BehaviourWithFSMStateHandler<I, O>) = 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 <reified I : O, O: State> BehaviourContextWithFSM<O>.strictlyOn(handler: BehaviourWithFSMStateHandler<I, O>) = addStrict(I::class, handler)
/** /**
* 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
@ -58,6 +101,9 @@ class DefaultBehaviourContextWithFSM<T : State>(
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, Flow<Update>>()
private val additionalHandlers = mutableListOf<BehaviourWithFSMStateHandlerHolder<*, T>>()
private var actualHandlersList = additionalHandlers + handlers
private fun getContextUpdatesFlow(context: Any) = updatesFlows.getOrPut(context) { private fun getContextUpdatesFlow(context: Any) = updatesFlows.getOrPut(context) {
allUpdatesFlow.accumulatorFlow(scope) allUpdatesFlow.accumulatorFlow(scope)
} }
@ -65,12 +111,17 @@ class DefaultBehaviourContextWithFSM<T : State>(
override suspend fun StatesMachine<in T>.handleState(state: T): T? = launchStateHandling( override suspend fun StatesMachine<in T>.handleState(state: T): T? = launchStateHandling(
state, state,
allUpdatesFlow, allUpdatesFlow,
handlers actualHandlersList
) )
override fun <I : T> add(kClass: KClass<I>, strict: Boolean, handler: BehaviourWithFSMStateHandler<I, T>) {
additionalHandlers.add(BehaviourWithFSMStateHandlerHolder(kClass, strict, handler))
actualHandlersList = additionalHandlers + handlers
}
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), handlers) val newState = launchStateHandling(state, getContextUpdatesFlow(state.context), actualHandlersList)
if (newState != null) { if (newState != null) {
statesManager.update(state, newState) statesManager.update(state, newState)
} else { } else {
@ -94,6 +145,26 @@ class DefaultBehaviourContextWithFSM<T : State>(
launch { statePerformer(it) } launch { statePerformer(it) }
} }
} }
/**
* 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 <reified I : T> onStateOrSubstate(handler: BehaviourWithFSMStateHandler<I, T>) = 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 <reified I : T> strictlyOn(handler: BehaviourWithFSMStateHandler<I, T>) = addStrict(I::class, handler)
override suspend fun startChain(state: T) { override suspend fun startChain(state: T) {
statesManager.startChain(state) statesManager.startChain(state)
@ -106,7 +177,7 @@ class DefaultBehaviourContextWithFSM<T : State>(
onBufferOverflow: BufferOverflow, onBufferOverflow: BufferOverflow,
upstreamUpdatesFlow: Flow<Update>?, upstreamUpdatesFlow: Flow<Update>?,
updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>? updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>?
): BehaviourContextWithFSM<T> = BehaviourContextWithFSM( ): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM(
behaviourContext.copy(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, updatesFilter), behaviourContext.copy(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, updatesFilter),
handlers, handlers,
statesManager statesManager

View File

@ -14,70 +14,8 @@ import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlin.reflect.KClass import kotlin.reflect.KClass
class BehaviourContextWithFSMBuilder<T : State> internal constructor( @Deprecated("Will be removed soon")
private val resultBehaviourContext: BehaviourContextWithFSM<T>, typealias BehaviourContextWithFSMBuilder<T> = BehaviourContextWithFSM<T>
private val handlers: MutableList<BehaviourWithFSMStateHandlerHolder<*, T>>
) : BehaviourContextWithFSM<T> by resultBehaviourContext {
internal constructor(
baseBehaviourContext: BehaviourContext,
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
handlers: MutableList<BehaviourWithFSMStateHandlerHolder<*, T>> = 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 : T> add(kClass: KClass<I>, handler: BehaviourWithFSMStateHandler<I, T>) {
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 : T> addStrict(kClass: KClass<I>, handler: BehaviourWithFSMStateHandler<I, T>) {
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 <reified I : T> onStateOrSubstate(handler: BehaviourWithFSMStateHandler<I, T>) {
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 <reified I : T> strictlyOn(handler: BehaviourWithFSMStateHandler<I, T>) {
addStrict(I::class, handler)
}
/**
* Returns completed [resultBehaviourContext], [handlers] and [statesManager]
*/
internal fun build() = resultBehaviourContext
}
/** /**
* Creates [BehaviourContextWithFSM] via creating of [DefaultBehaviourContext] with [this] as [TelegramBot], * Creates [BehaviourContextWithFSM] via creating of [DefaultBehaviourContext] with [this] as [TelegramBot],
@ -94,17 +32,17 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
scope: CoroutineScope = defaultCoroutineScopeProvider(), scope: CoroutineScope = defaultCoroutineScopeProvider(),
defaultExceptionsHandler: ExceptionHandler<Unit>? = null, defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: MutableList<BehaviourWithFSMStateHandlerHolder<*, T>> = mutableListOf(), presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
block: CustomBehaviourContextReceiver<BehaviourContextWithFSMBuilder<T>, Unit> block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): BehaviourContextWithFSM<T> = BehaviourContextWithFSMBuilder( ): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM(
DefaultBehaviourContext( DefaultBehaviourContext(
this, this,
defaultExceptionsHandler ?.let { scope + ContextSafelyExceptionHandler(it) } ?: scope, defaultExceptionsHandler ?.let { scope + ContextSafelyExceptionHandler(it) } ?: scope,
upstreamUpdatesFlow = upstreamUpdatesFlow upstreamUpdatesFlow = upstreamUpdatesFlow
), ),
statesManager, presetHandlers,
presetHandlers statesManager
).apply { block() }.build() ).apply { block() }
/** /**
* Use [buildBehaviourWithFSM] to create [BehaviourContextWithFSM] and launch getting of updates * Use [buildBehaviourWithFSM] to create [BehaviourContextWithFSM] and launch getting of updates
@ -116,9 +54,9 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
scope: CoroutineScope = defaultCoroutineScopeProvider(), scope: CoroutineScope = defaultCoroutineScopeProvider(),
defaultExceptionsHandler: ExceptionHandler<Unit>? = null, defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: MutableList<BehaviourWithFSMStateHandlerHolder<*, T>> = mutableListOf(), presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
block: CustomBehaviourContextReceiver<BehaviourContextWithFSMBuilder<T>, Unit> block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): Pair<BehaviourContextWithFSM<T>, Job> = buildBehaviourWithFSM( ): Pair<DefaultBehaviourContextWithFSM<T>, Job> = buildBehaviourWithFSM(
upstreamUpdatesFlow, upstreamUpdatesFlow,
scope, scope,
defaultExceptionsHandler, defaultExceptionsHandler,
@ -147,8 +85,8 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
* @see BehaviourContext * @see BehaviourContext
* @see BehaviourContextWithFSM * @see BehaviourContextWithFSM
* @see longPolling * @see longPolling
* @see BehaviourContextWithFSMBuilder.strictlyOn * @see BehaviourContextWithFSM.strictlyOn
* @see BehaviourContextWithFSMBuilder.onStateOrSubstate * @see BehaviourContextWithFSM.onStateOrSubstate
*/ */
@PreviewFeature @PreviewFeature
suspend fun <T : State> TelegramBot.buildBehaviourWithFSM( suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
@ -156,17 +94,17 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
scope: CoroutineScope = defaultCoroutineScopeProvider(), scope: CoroutineScope = defaultCoroutineScopeProvider(),
defaultExceptionsHandler: ExceptionHandler<Unit>? = null, defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: MutableList<BehaviourWithFSMStateHandlerHolder<*, T>> = mutableListOf(), presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
block: CustomBehaviourContextReceiver<BehaviourContextWithFSMBuilder<T>, Unit> block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): BehaviourContextWithFSM<T> = BehaviourContextWithFSMBuilder( ): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM(
DefaultBehaviourContext( DefaultBehaviourContext(
this, this,
defaultExceptionsHandler ?.let { scope + ContextSafelyExceptionHandler(it) } ?: scope, defaultExceptionsHandler ?.let { scope + ContextSafelyExceptionHandler(it) } ?: scope,
upstreamUpdatesFlow = flowUpdatesFilter.allUpdatesFlow upstreamUpdatesFlow = flowUpdatesFilter.allUpdatesFlow
), ),
statesManager, presetHandlers,
presetHandlers statesManager
).apply { block() }.build() ).apply { block() }
/** /**
* Use [buildBehaviourWithFSM] to create [BehaviourContextWithFSM] and launch getting of updates * Use [buildBehaviourWithFSM] to create [BehaviourContextWithFSM] and launch getting of updates
@ -176,16 +114,16 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
* @see buildBehaviourWithFSMAndStartLongPolling * @see buildBehaviourWithFSMAndStartLongPolling
* @see BehaviourContext * @see BehaviourContext
* @see longPolling * @see longPolling
* @see BehaviourContextWithFSMBuilder.strictlyOn * @see BehaviourContextWithFSM.strictlyOn
* @see BehaviourContextWithFSMBuilder.onStateOrSubstate * @see BehaviourContextWithFSM.onStateOrSubstate
*/ */
@PreviewFeature @PreviewFeature
suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling( suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
scope: CoroutineScope = defaultCoroutineScopeProvider(), scope: CoroutineScope = defaultCoroutineScopeProvider(),
defaultExceptionsHandler: ExceptionHandler<Unit>? = null, defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: MutableList<BehaviourWithFSMStateHandlerHolder<*, T>> = mutableListOf(), presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
block: CustomBehaviourContextReceiver<BehaviourContextWithFSMBuilder<T>, Unit> block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
) = FlowsUpdatesFilter().let { ) = FlowsUpdatesFilter().let {
buildBehaviourWithFSM( buildBehaviourWithFSM(
it, it,

View File

@ -37,9 +37,9 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSM(
builder: KtorRequestsExecutorBuilder.() -> Unit = {}, builder: KtorRequestsExecutorBuilder.() -> Unit = {},
defaultExceptionsHandler: ExceptionHandler<Unit>? = null, defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: MutableList<BehaviourWithFSMStateHandlerHolder<*, T>> = mutableListOf(), presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
testServer: Boolean = false, testServer: Boolean = false,
block: CustomBehaviourContextReceiver<BehaviourContextWithFSMBuilder<T>, Unit> block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): TelegramBot = telegramBot( ): TelegramBot = telegramBot(
token, token,
apiUrl, apiUrl,
@ -74,9 +74,9 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSMAndStartLongPolling(
builder: KtorRequestsExecutorBuilder.() -> Unit = {}, builder: KtorRequestsExecutorBuilder.() -> Unit = {},
defaultExceptionsHandler: ExceptionHandler<Unit>? = null, defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: MutableList<BehaviourWithFSMStateHandlerHolder<*, T>> = mutableListOf(), presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
testServer: Boolean = false, testServer: Boolean = false,
block: CustomBehaviourContextReceiver<BehaviourContextWithFSMBuilder<T>, Unit> block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): Pair<TelegramBot, Job> { ): Pair<TelegramBot, Job> {
return telegramBot( return telegramBot(
token, token,