@file:Suppress("NOTHING_TO_INLINE") package dev.inmo.tgbotapi.extensions.behaviour_builder import dev.inmo.micro_utils.coroutines.* import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.handlers_registrar.TriggersHolder import dev.inmo.tgbotapi.types.update.abstracts.Update import dev.inmo.tgbotapi.updateshandlers.* import kotlinx.coroutines.* import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.* typealias CustomBehaviourContextReceiver = suspend BC.() -> T typealias BehaviourContextReceiver = CustomBehaviourContextReceiver typealias CustomBehaviourContextAndTypeReceiver = suspend BC.(I) -> T typealias BehaviourContextAndTypeReceiver = CustomBehaviourContextAndTypeReceiver typealias CustomBehaviourContextAndTwoTypesReceiver = suspend BC.(I1, I2) -> T typealias BehaviourContextAndTwoTypesReceiver = CustomBehaviourContextAndTwoTypesReceiver inline fun BehaviourContextReceiver(noinline block: BehaviourContextReceiver) = block inline fun CustomBehaviourContextReceiver(noinline block: CustomBehaviourContextReceiver) = block inline fun BehaviourContextAndTypeReceiver(noinline block: BehaviourContextAndTypeReceiver) = block inline fun BehaviourContextAndTwoTypesReceiver(noinline block: BehaviourContextAndTwoTypesReceiver) = block internal inline fun CustomBehaviourContextAndTwoTypesReceiver.toOneType( i1: I1, ): CustomBehaviourContextAndTypeReceiver = { invoke(this, i1, it) } /** * This class contains all necessary tools for work with bots and especially [buildBehaviour] * * @see DefaultBehaviourContext */ interface BehaviourContext : FlowsUpdatesFilter, TelegramBot, CoroutineScope { val bot: TelegramBot get() = this /** * Will be used for creating of some subscriptions inside of methods, updates listening and different other things * in context of working with [CoroutineScope] and coroutines. */ val scope: CoroutineScope get() = this /** * This parameter will be used to subscribe on different types of update */ val flowsUpdatesFilter: FlowsUpdatesFilter get() = this val triggersHolder: TriggersHolder fun copy( bot: TelegramBot = this.bot, scope: CoroutineScope = this.scope, broadcastChannelsSize: Int = 100, onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND, upstreamUpdatesFlow: Flow? = null, triggersHolder: TriggersHolder = TriggersHolder() ): BehaviourContext } class DefaultBehaviourContext( override val bot: TelegramBot, override val scope: CoroutineScope, broadcastChannelsSize: Int = 100, onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND, private val upstreamUpdatesFlow: Flow? = null, override val triggersHolder: TriggersHolder = TriggersHolder() ) : AbstractFlowsUpdatesFilter(), TelegramBot by bot, CoroutineScope by scope, BehaviourContext { private val additionalUpdatesSharedFlow = MutableSharedFlow(0, broadcastChannelsSize, onBufferOverflow) override val allUpdatesFlow: Flow = (additionalUpdatesSharedFlow.asSharedFlow()).let { if (upstreamUpdatesFlow != null) { var lastHandledUpdate = -1L (it + upstreamUpdatesFlow).filter { (it.updateId > lastHandledUpdate).also { passed -> if (passed) { lastHandledUpdate = it.updateId } } } } else { it } }.accumulatorFlow(scope) override val asUpdateReceiver: UpdateReceiver = additionalUpdatesSharedFlow::emit override fun copy( bot: TelegramBot, scope: CoroutineScope, broadcastChannelsSize: Int, onBufferOverflow: BufferOverflow, upstreamUpdatesFlow: Flow?, triggersHolder: TriggersHolder ): DefaultBehaviourContext = DefaultBehaviourContext(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, triggersHolder) } fun BehaviourContext( bot: TelegramBot, scope: CoroutineScope, flowsUpdatesFilter: FlowsUpdatesFilter = FlowsUpdatesFilter(), triggersHolder: TriggersHolder = TriggersHolder(), ) = DefaultBehaviourContext(bot, scope, upstreamUpdatesFlow = flowsUpdatesFilter.allUpdatesFlow, triggersHolder = triggersHolder) inline fun BehaviourContext( bot: TelegramBot, scope: CoroutineScope, flowsUpdatesFilter: FlowsUpdatesFilter = FlowsUpdatesFilter(), triggersHolder: TriggersHolder = TriggersHolder(), crossinline block: BehaviourContext.() -> T ) = DefaultBehaviourContext(bot, scope, upstreamUpdatesFlow = flowsUpdatesFilter.allUpdatesFlow, triggersHolder = triggersHolder).run(block) /** * Creates new [BehaviourContext] using its [BehaviourContext.copy] method * * @param updatesFilter This param will not be used anymore */ fun BC.createSubContext( scope: CoroutineScope = LinkedSupervisorScope(), triggersHolder: TriggersHolder = this.triggersHolder, updatesUpstreamFlow: Flow = allUpdatesFlow, ) = copy( scope = scope, upstreamUpdatesFlow = updatesUpstreamFlow, triggersHolder = triggersHolder ) as BC /** * Launch [behaviourContextReceiver] in context of [this] as [BehaviourContext] and as [kotlin.coroutines.CoroutineContext] * * @param stopOnCompletion ___FALSE BY DEFAULT___. Will stop [this] in case if passed true */ suspend fun BC.doInContext( stopOnCompletion: Boolean = false, behaviourContextReceiver: CustomBehaviourContextReceiver ): T { return withContext(coroutineContext) { behaviourContextReceiver().also { if (stopOnCompletion) stop() } } } /** * Creates new one [BehaviourContext] using [createSubContext] and launches [behaviourContextReceiver] in a new context * using [doInContext] * * @param stopOnCompletion ___TRUE BY DEFAULT___ */ suspend fun BC.createSubContextAndDoWithUpdatesFilter( scope: CoroutineScope = LinkedSupervisorScope(), triggersHolder: TriggersHolder = this.triggersHolder, updatesUpstreamFlow: Flow = allUpdatesFlow, stopOnCompletion: Boolean = true, behaviourContextReceiver: CustomBehaviourContextReceiver ): T { return createSubContext( scope, triggersHolder, updatesUpstreamFlow ).doInContext( stopOnCompletion, behaviourContextReceiver ) } /** * This method will cancel ALL subsequent contexts, expectations and waiters */ fun BehaviourContext.stop() = scope.cancel()