1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2024-11-22 16:23:48 +00:00
This commit is contained in:
InsanusMokrassar 2021-10-15 17:13:03 +06:00
parent 19d221fc29
commit c5ff0dbc54
3 changed files with 92 additions and 76 deletions

View File

@ -1,7 +1,6 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder package dev.inmo.tgbotapi.extensions.behaviour_builder
import dev.inmo.micro_utils.coroutines.ContextSafelyExceptionHandler import dev.inmo.micro_utils.coroutines.*
import dev.inmo.micro_utils.coroutines.ExceptionHandler
import dev.inmo.micro_utils.fsm.common.* import dev.inmo.micro_utils.fsm.common.*
import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManager import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManager
import dev.inmo.micro_utils.fsm.common.managers.InMemoryDefaultStatesManagerRepo import dev.inmo.micro_utils.fsm.common.managers.InMemoryDefaultStatesManagerRepo
@ -48,6 +47,32 @@ class BehaviourContextWithFSMBuilder internal constructor(
handlers.add(BehaviourWithFSMStateHandlerHolder(kClass, true, handler)) 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 : State> onStateOrSubstate(handler: BehaviourWithFSMStateHandler<I>) {
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 : State> strictlyOn(handler: BehaviourWithFSMStateHandler<I>) {
addStrict(I::class, handler)
}
/** /**
* Returns completed [resultBehaviourContext], [handlers] and [statesManager] * Returns completed [resultBehaviourContext], [handlers] and [statesManager]
*/ */
@ -55,61 +80,52 @@ class BehaviourContextWithFSMBuilder internal constructor(
} }
/** /**
* Add NON STRICT [handler] to list of available in future [BehaviourContextWithFSM]. Non strict means that * Creates [BehaviourContextWithFSM] via creating of [DefaultBehaviourContext] with [this] as [TelegramBot],
* for input [State] will be used [KClass.isInstance] and any inheritor of [kClass] will pass this requirement * [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.
* *
* @see BehaviourWithFSMStateHandlerHolder * !!! WARNING !!! This method WILL NOT call [BehaviourContextWithFSM.start] of result object and WILL NOT
* @see BehaviourContextWithFSMBuilder.add * start any updates retrieving. See [buildBehaviourWithFSMAndStartLongPolling] or
*/ * [telegramBotWithBehaviourAndFSMAndStartLongPolling] in case you wish to start [longPolling] automatically
inline fun <reified I : State> BehaviourContextWithFSMBuilder.onStateOrSubstate(handler: BehaviourWithFSMStateHandler<I>) {
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
*/
inline fun <reified I : State> BehaviourContextWithFSMBuilder.strictlyOn(handler: BehaviourWithFSMStateHandler<I>) {
addStrict(I::class, handler)
}
/**
* Use this factory to create and organize behaviour of your bot with attention to FSM logic. This factory WILL NOT
* start any incoming updates handling of FSM handling, you must start it by yourself
*
* @param upstreamUpdatesFlow Will be used in [BehaviourContextWithFSMBuilder.build] to put it in new [BehaviourContextWithFSM]
* @param scope This [CoroutineScope] will be used in [BehaviourContextWithFSMBuilder.build] to put it in new [BehaviourContextWithFSM]
*/ */
suspend fun TelegramBot.buildBehaviourWithFSM( suspend fun TelegramBot.buildBehaviourWithFSM(
upstreamUpdatesFlow: Flow<Update>? = null, upstreamUpdatesFlow: Flow<Update>? = null,
scope: CoroutineScope = defaultCoroutineScopeProvider(), scope: CoroutineScope = defaultCoroutineScopeProvider(),
statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
handlersPreset: MutableList<BehaviourWithFSMStateHandlerHolder<*>> = mutableListOf(),
block: CustomBehaviourContextReceiver<BehaviourContextWithFSMBuilder, Unit>
) = BehaviourContextWithFSMBuilder(
DefaultBehaviourContext(this, scope, upstreamUpdatesFlow = upstreamUpdatesFlow),
statesManager,
handlersPreset
).apply { block() }.build()
/**
* Use this factory to create and organize behaviour of your bot with attention to FSM logic. This factory will start
* listening of updates by [longPolling]
*
* @param upstreamUpdatesFlow Will be used in [BehaviourContextWithFSMBuilder.build] to put it in new [BehaviourContextWithFSM]
* @param scope This [CoroutineScope] will be used in [BehaviourContextWithFSMBuilder.build] to put it in new [BehaviourContextWithFSM]
*/
suspend fun TelegramBot.buildBehaviourWithFSMAndLongPolling(
upstreamUpdatesFlow: Flow<Update>? = null,
scope: CoroutineScope = defaultCoroutineScopeProvider(),
statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: MutableList<BehaviourWithFSMStateHandlerHolder<*>> = mutableListOf(), presetHandlers: MutableList<BehaviourWithFSMStateHandlerHolder<*>> = mutableListOf(),
block: CustomBehaviourContextReceiver<BehaviourContextWithFSMBuilder, Unit> block: CustomBehaviourContextReceiver<BehaviourContextWithFSMBuilder, Unit>
) = buildBehaviourWithFSM(upstreamUpdatesFlow, scope, statesManager, presetHandlers, block).run { ): BehaviourContextWithFSM = BehaviourContextWithFSMBuilder(
DefaultBehaviourContext(
this,
defaultExceptionsHandler ?.let { scope + ContextSafelyExceptionHandler(it) } ?: scope,
upstreamUpdatesFlow = upstreamUpdatesFlow
),
statesManager,
presetHandlers
).apply { block() }.build()
/**
* 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
*/
suspend fun TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
upstreamUpdatesFlow: Flow<Update>? = null,
scope: CoroutineScope = defaultCoroutineScopeProvider(),
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: MutableList<BehaviourWithFSMStateHandlerHolder<*>> = mutableListOf(),
block: CustomBehaviourContextReceiver<BehaviourContextWithFSMBuilder, Unit>
): Pair<BehaviourContextWithFSM, Job> = buildBehaviourWithFSM(
upstreamUpdatesFlow,
scope,
defaultExceptionsHandler,
statesManager,
presetHandlers,
block
).run {
this to scope.launch { this to scope.launch {
start() start()
longPolling(flowsUpdatesFilter, scope = scope) longPolling(flowsUpdatesFilter, scope = scope)
@ -117,16 +133,22 @@ suspend fun TelegramBot.buildBehaviourWithFSMAndLongPolling(
} }
/** /**
* 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]. * 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 * !!! WARNING !!! This method WILL NOT call [BehaviourContextWithFSM.start] of result object and WILL NOT
* [startGettingOfUpdatesByLongPolling] (or just [longPolling]) or tools for work with webhooks * start any updates retrieving. See [buildBehaviourWithFSMAndStartLongPolling] or
* [telegramBotWithBehaviourAndFSMAndStartLongPolling] in case you wish to start [longPolling] automatically
* *
* @see BehaviourContext * @see BehaviourContext
* @see BehaviourContextWithFSM * @see BehaviourContextWithFSM
* @see longPolling * @see longPolling
* @see strictlyOn * @see BehaviourContextWithFSMBuilder.strictlyOn
* @see onStateOrSubstate * @see BehaviourContextWithFSMBuilder.onStateOrSubstate
*/ */
@PreviewFeature @PreviewFeature
suspend fun TelegramBot.buildBehaviourWithFSM( suspend fun TelegramBot.buildBehaviourWithFSM(
@ -137,34 +159,28 @@ suspend fun TelegramBot.buildBehaviourWithFSM(
presetHandlers: MutableList<BehaviourWithFSMStateHandlerHolder<*>> = mutableListOf(), presetHandlers: MutableList<BehaviourWithFSMStateHandlerHolder<*>> = mutableListOf(),
block: CustomBehaviourContextReceiver<BehaviourContextWithFSMBuilder, Unit> block: CustomBehaviourContextReceiver<BehaviourContextWithFSMBuilder, Unit>
): BehaviourContextWithFSM = BehaviourContextWithFSMBuilder( ): BehaviourContextWithFSM = BehaviourContextWithFSMBuilder(
BehaviourContext( DefaultBehaviourContext(
this, this,
scope.let { defaultExceptionsHandler ?.let { scope + ContextSafelyExceptionHandler(it) } ?: scope,
if (defaultExceptionsHandler == null) { upstreamUpdatesFlow = flowUpdatesFilter.allUpdatesFlow
it
} else {
it + ContextSafelyExceptionHandler(defaultExceptionsHandler)
}
},
flowUpdatesFilter
), ),
statesManager, statesManager,
presetHandlers presetHandlers
).apply { block() }.build() ).apply { block() }.build()
/** /**
* Use this method to build bot behaviour with FSM and run it via long polling. In case you wish to use * Use [buildBehaviourWithFSM] to create [BehaviourContextWithFSM] and launch getting of updates
* [FlowsUpdatesFilter] of result [BehaviourContextWithFSM] for additional manipulations, you must provide external * using [longPolling]. For [longPolling] will be used result [BehaviourContextWithFSM] for both parameters
* [FlowsUpdatesFilter] in other [buildBehaviourWithFSM] function. * flowsUpdatesFilter and scope
* *
* @see buildBehaviourWithFSM * @see buildBehaviourWithFSMAndStartLongPolling
* @see BehaviourContext * @see BehaviourContext
* @see longPolling * @see longPolling
* @see strictlyOn * @see BehaviourContextWithFSMBuilder.strictlyOn
* @see onStateOrSubstate * @see BehaviourContextWithFSMBuilder.onStateOrSubstate
*/ */
@PreviewFeature @PreviewFeature
suspend fun TelegramBot.buildBehaviourWithFSM( suspend fun TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
scope: CoroutineScope = defaultCoroutineScopeProvider(), scope: CoroutineScope = defaultCoroutineScopeProvider(),
defaultExceptionsHandler: ExceptionHandler<Unit>? = null, defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),

View File

@ -16,7 +16,7 @@ import kotlin.coroutines.coroutineContext
/** /**
* Create bot using [telegramBot] and start listening for updates using [buildBehaviour]. * Create bot using [telegramBot] and start listening for updates using [buildBehaviourWithFSM].
* Use this method in case you wish to make some additional actions with [flowsUpdatesFilter]. * 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 * **WARNING** This method WILL NOT launch any listening of updates. Use something like
@ -43,8 +43,8 @@ suspend fun telegramBotWithBehaviourAndFSM(
apiUrl, apiUrl,
builder builder
).apply { ).apply {
buildBehaviourWithFSM( buildBehaviourWithFSMAndStartLongPolling(
flowsUpdatesFilter, flowsUpdatesFilter.allUpdatesFlow,
scope ?: CoroutineScope(coroutineContext), scope ?: CoroutineScope(coroutineContext),
defaultExceptionsHandler, defaultExceptionsHandler,
statesManager, statesManager,
@ -67,7 +67,7 @@ suspend fun telegramBotWithBehaviourAndFSM(
* @see [buildBehaviour] * @see [buildBehaviour]
* @see startGettingOfUpdatesByLongPolling * @see startGettingOfUpdatesByLongPolling
*/ */
suspend fun telegramBotWithBehaviourAndFSM( suspend fun telegramBotWithBehaviourAndFSMAndStartLongPolling(
token: String, token: String,
scope: CoroutineScope? = null, scope: CoroutineScope? = null,
apiUrl: String = telegramBotAPIDefaultUrl, apiUrl: String = telegramBotAPIDefaultUrl,
@ -82,7 +82,7 @@ suspend fun telegramBotWithBehaviourAndFSM(
apiUrl, apiUrl,
builder builder
).let { ).let {
it to it.buildBehaviourWithFSM ( it to it.buildBehaviourWithFSMAndStartLongPolling (
scope ?: CoroutineScope(coroutineContext), scope ?: CoroutineScope(coroutineContext),
defaultExceptionsHandler, defaultExceptionsHandler,
statesManager, statesManager,

View File

@ -67,8 +67,8 @@ interface BehaviourContext : FlowsUpdatesFilter, TelegramBot, CoroutineScope {
class DefaultBehaviourContext( class DefaultBehaviourContext(
override val bot: TelegramBot, override val bot: TelegramBot,
override val scope: CoroutineScope, override val scope: CoroutineScope,
private val broadcastChannelsSize: Int = 100, broadcastChannelsSize: Int = 100,
private val onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND, onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND,
private val upstreamUpdatesFlow: Flow<Update>? = null, private val upstreamUpdatesFlow: Flow<Update>? = null,
private val updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>? = null private val updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>? = null
) : AbstractFlowsUpdatesFilter(), TelegramBot by bot, CoroutineScope by scope, BehaviourContext { ) : AbstractFlowsUpdatesFilter(), TelegramBot by bot, CoroutineScope by scope, BehaviourContext {