commonMain.dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext.kt Maven / Gradle / Ivy
@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.UpdateId
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) {
val handledUpdates = mutableSetOf()
(it + upstreamUpdatesFlow).filter {
val passed = handledUpdates.add(it.updateId)
(passed).also { passed ->
val needToDropCount = handledUpdates.size - broadcastChannelsSize
if (needToDropCount > 0) {
handledUpdates.removeAll(
handledUpdates.take(needToDropCount).ifEmpty { return@also }
)
}
}
}
} 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()
© 2015 - 2024 Weber Informatics LLC | Privacy Policy