improving work of behaviour_builder

This commit is contained in:
InsanusMokrassar 2021-01-19 18:50:45 +06:00
parent 30a4a7bd8b
commit fc71e028c4
11 changed files with 234 additions and 142 deletions

View File

@ -8,8 +8,17 @@
* `Version`:
* `MicroUtils`: `0.4.16` -> `0.4.19`
* `Core`:
* **BC** Now `MediaGroupMessage` have a generic type related to `MediaGroupContent`
* **BREAKING CHANGE** Now `MediaGroupMessage` have a generic type related to `MediaGroupContent`
* Methods and types related to `MediaGroupMessage` have been modified according to their meanings
* **Important Change** `FlowsUpdatesFilter` now is an interface. Old class has been renamed to
`DefaultFlowsUpdatesFilter` and factory method `FlowsUpdatesFilter` has been added
* `Behaviour Builder`:
* Trigger and expectation extensions for `MessageContent` (`onContentMessage` and `waitContentMessage`)
* `onMediaGroup` has been replaced
* `waitMediaGroup` has been added
* `onVisualMediaGroup` now is just an alternative to `onVisualGallery`
* `command` and `onCommand` expectations has been added for commands `String` variant
* New extensions `BehaviourContext#oneOf`, `BehaviourContext#parallel` and `Deferred<T>#withAction`
## 0.31.0

View File

@ -7,15 +7,47 @@ import dev.inmo.tgbotapi.types.update.abstracts.UnknownUpdate
import dev.inmo.tgbotapi.types.update.abstracts.Update
import kotlinx.coroutines.flow.*
@Suppress("EXPERIMENTAL_API_USAGE", "unused")
class FlowsUpdatesFilter(
interface FlowsUpdatesFilter : UpdatesFilter {
override val allowedUpdates: List<String>
get() = ALL_UPDATES_LIST
val allUpdatesFlow: Flow<Update>
val allUpdatesWithoutMediaGroupsGroupingFlow: Flow<Update>
val messageFlow: Flow<MessageUpdate>
val messageMediaGroupFlow: Flow<MessageMediaGroupUpdate>
val editedMessageFlow: Flow<EditMessageUpdate>
val editedMessageMediaGroupFlow: Flow<EditMessageMediaGroupUpdate>
val channelPostFlow: Flow<ChannelPostUpdate>
val channelPostMediaGroupFlow: Flow<ChannelPostMediaGroupUpdate>
val editedChannelPostFlow: Flow<EditChannelPostUpdate>
val editedChannelPostMediaGroupFlow: Flow<EditChannelPostMediaGroupUpdate>
val chosenInlineResultFlow: Flow<ChosenInlineResultUpdate>
val inlineQueryFlow: Flow<InlineQueryUpdate>
val callbackQueryFlow: Flow<CallbackQueryUpdate>
val shippingQueryFlow: Flow<ShippingQueryUpdate>
val preCheckoutQueryFlow: Flow<PreCheckoutQueryUpdate>
val pollFlow: Flow<PollUpdate>
val pollAnswerFlow: Flow<PollAnswerUpdate>
val unknownUpdateTypeFlow: Flow<UnknownUpdate>
}
/**
* Creates [DefaultFlowsUpdatesFilter]
*/
@Suppress("FunctionName")
fun FlowsUpdatesFilter(
broadcastChannelsSize: Int = 100
): UpdatesFilter {
) = DefaultFlowsUpdatesFilter(broadcastChannelsSize)
@Suppress("EXPERIMENTAL_API_USAGE", "unused")
class DefaultFlowsUpdatesFilter(
broadcastChannelsSize: Int = 100
): FlowsUpdatesFilter {
private val updatesSharedFlow = MutableSharedFlow<Update>(extraBufferCapacity = broadcastChannelsSize)
@Suppress("MemberVisibilityCanBePrivate")
val allUpdatesFlow: Flow<Update> = updatesSharedFlow.asSharedFlow()
override val allUpdatesFlow: Flow<Update> = updatesSharedFlow.asSharedFlow()
@Suppress("MemberVisibilityCanBePrivate")
val allUpdatesWithoutMediaGroupsGroupingFlow: Flow<Update> = updatesSharedFlow.flatMapConcat {
override val allUpdatesWithoutMediaGroupsGroupingFlow: Flow<Update> = allUpdatesFlow.flatMapConcat {
when (it) {
is SentMediaGroupUpdate -> it.origins.asFlow()
is EditMediaGroupUpdate -> flowOf(it.origin)
@ -23,26 +55,24 @@ class FlowsUpdatesFilter(
}
}
override val allowedUpdates: List<String>
get() = ALL_UPDATES_LIST
override val asUpdateReceiver: UpdateReceiver<Update> = {
updatesSharedFlow.emit(it)
}
val messageFlow: Flow<MessageUpdate> = allUpdatesFlow.filterIsInstance()
val messageMediaGroupFlow: Flow<MessageMediaGroupUpdate> = allUpdatesFlow.filterIsInstance()
val editedMessageFlow: Flow<EditMessageUpdate> = allUpdatesFlow.filterIsInstance()
val editedMessageMediaGroupFlow: Flow<EditMessageMediaGroupUpdate> = allUpdatesFlow.filterIsInstance()
val channelPostFlow: Flow<ChannelPostUpdate> = allUpdatesFlow.filterIsInstance()
val channelPostMediaGroupFlow: Flow<ChannelPostMediaGroupUpdate> = allUpdatesFlow.filterIsInstance()
val editedChannelPostFlow: Flow<EditChannelPostUpdate> = allUpdatesFlow.filterIsInstance()
val editedChannelPostMediaGroupFlow: Flow<EditChannelPostMediaGroupUpdate> = allUpdatesFlow.filterIsInstance()
val chosenInlineResultFlow: Flow<ChosenInlineResultUpdate> = allUpdatesFlow.filterIsInstance()
val inlineQueryFlow: Flow<InlineQueryUpdate> = allUpdatesFlow.filterIsInstance()
val callbackQueryFlow: Flow<CallbackQueryUpdate> = allUpdatesFlow.filterIsInstance()
val shippingQueryFlow: Flow<ShippingQueryUpdate> = allUpdatesFlow.filterIsInstance()
val preCheckoutQueryFlow: Flow<PreCheckoutQueryUpdate> = allUpdatesFlow.filterIsInstance()
val pollFlow: Flow<PollUpdate> = allUpdatesFlow.filterIsInstance()
val pollAnswerFlow: Flow<PollAnswerUpdate> = allUpdatesFlow.filterIsInstance()
val unknownUpdateTypeFlow: Flow<UnknownUpdate> = allUpdatesFlow.filterIsInstance()
override val messageFlow: Flow<MessageUpdate> = allUpdatesFlow.filterIsInstance()
override val messageMediaGroupFlow: Flow<MessageMediaGroupUpdate> = allUpdatesFlow.filterIsInstance()
override val editedMessageFlow: Flow<EditMessageUpdate> = allUpdatesFlow.filterIsInstance()
override val editedMessageMediaGroupFlow: Flow<EditMessageMediaGroupUpdate> = allUpdatesFlow.filterIsInstance()
override val channelPostFlow: Flow<ChannelPostUpdate> = allUpdatesFlow.filterIsInstance()
override val channelPostMediaGroupFlow: Flow<ChannelPostMediaGroupUpdate> = allUpdatesFlow.filterIsInstance()
override val editedChannelPostFlow: Flow<EditChannelPostUpdate> = allUpdatesFlow.filterIsInstance()
override val editedChannelPostMediaGroupFlow: Flow<EditChannelPostMediaGroupUpdate> = allUpdatesFlow.filterIsInstance()
override val chosenInlineResultFlow: Flow<ChosenInlineResultUpdate> = allUpdatesFlow.filterIsInstance()
override val inlineQueryFlow: Flow<InlineQueryUpdate> = allUpdatesFlow.filterIsInstance()
override val callbackQueryFlow: Flow<CallbackQueryUpdate> = allUpdatesFlow.filterIsInstance()
override val shippingQueryFlow: Flow<ShippingQueryUpdate> = allUpdatesFlow.filterIsInstance()
override val preCheckoutQueryFlow: Flow<PreCheckoutQueryUpdate> = allUpdatesFlow.filterIsInstance()
override val pollFlow: Flow<PollUpdate> = allUpdatesFlow.filterIsInstance()
override val pollAnswerFlow: Flow<PollAnswerUpdate> = allUpdatesFlow.filterIsInstance()
override val unknownUpdateTypeFlow: Flow<UnknownUpdate> = allUpdatesFlow.filterIsInstance()
}

View File

@ -1,9 +1,11 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.types.update.abstracts.Update
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import dev.inmo.tgbotapi.updateshandlers.UpdatesFilter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.filter
typealias BehaviourContextReceiver<T> = suspend BehaviourContext.() -> T
typealias BehaviourContextAndTypeReceiver<T, I> = suspend BehaviourContext.(I) -> T
@ -19,4 +21,48 @@ data class BehaviourContext(
val bot: TelegramBot,
val scope: CoroutineScope,
val flowsUpdatesFilter: FlowsUpdatesFilter = FlowsUpdatesFilter()
) : UpdatesFilter by flowsUpdatesFilter, TelegramBot by bot, CoroutineScope by scope
) : FlowsUpdatesFilter by flowsUpdatesFilter, TelegramBot by bot, CoroutineScope by scope
/**
* Creates new one [BehaviourContext], adding subsequent [FlowsUpdatesFilter] in case [newFlowsUpdatesFilterSetUp] is provided and
* [CoroutineScope] as new [BehaviourContext.scope]
*
* @param newFlowsUpdatesFilterSetUp As a parameter receives [FlowsUpdatesFilter] from old [this] [BehaviourContext.flowsUpdatesFilter]
*/
suspend fun <T> BehaviourContext.doInSubContextWithFlowsUpdatesFilterSetup(
newFlowsUpdatesFilterSetUp: BehaviourContextAndTypeReceiver<Unit, FlowsUpdatesFilter>?,
behaviourContextReceiver: BehaviourContextReceiver<T>
) = copy(
flowsUpdatesFilter = FlowsUpdatesFilter(),
scope = CoroutineScope(scope.coroutineContext + SupervisorJob())
).run {
newFlowsUpdatesFilterSetUp ?.let {
it.apply { invoke(this@run, this@doInSubContextWithFlowsUpdatesFilterSetup.flowsUpdatesFilter) }
}
behaviourContextReceiver().also { stop() }
}
/**
* Creates new one [BehaviourContext], adding subsequent [FlowsUpdatesFilter] in case [updatesFilter] is provided and
* [CoroutineScope] as new [BehaviourContext.scope]
*/
suspend fun <T> BehaviourContext.doInSubContextWithUpdatesFilter(
updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>?,
behaviourContextReceiver: BehaviourContextReceiver<T>
) = doInSubContextWithFlowsUpdatesFilterSetup(
newFlowsUpdatesFilterSetUp = updatesFilter ?.let {
{ oldOne ->
oldOne.allUpdatesFlow.filter { updatesFilter(it) }.subscribeSafelyWithoutExceptions(scope, asUpdateReceiver)
}
},
behaviourContextReceiver
)
suspend fun <T> BehaviourContext.doInSubContext(
behaviourContextReceiver: BehaviourContextReceiver<T>
) = doInSubContextWithFlowsUpdatesFilterSetup(newFlowsUpdatesFilterSetUp = null, behaviourContextReceiver)
/**
* This method will cancel ALL subsequent contexts, expectations and waiters
*/
fun BehaviourContext.stop() = scope.cancel()

View File

@ -0,0 +1,21 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder
import dev.inmo.micro_utils.coroutines.DeferredAction
import dev.inmo.micro_utils.coroutines.invokeFirstOf
import kotlinx.coroutines.*
suspend fun <T> BehaviourContext.parallel(
action: BehaviourContextReceiver<T>
) = async {
action()
}
inline infix fun <T, O> Deferred<T>.withAction(noinline callback: suspend (T) -> O) = DeferredAction(this, callback)
suspend fun <O> BehaviourContext.oneOf(
deferredActions: Iterable<DeferredAction<*, O>>
) = deferredActions.invokeFirstOf(scope)
suspend fun <O> BehaviourContext.oneOf(
vararg deferredActions: DeferredAction<*, O>
) = oneOf(deferredActions.toList())

View File

@ -59,6 +59,12 @@ private suspend inline fun <reified T : MessageContent> BehaviourContext.waitCon
}
}
suspend fun BehaviourContext.waitContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: CommonMessageToContentMapper<MessageContent>? = null
) = waitContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitContact(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
@ -101,14 +107,14 @@ suspend fun BehaviourContext.waitVenue(
count: Int = 1,
filter: CommonMessageToContentMapper<VenueContent>? = null
) = waitContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitAudioMediaGroup(
suspend fun BehaviourContext.waitAudioMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: CommonMessageToContentMapper<AudioMediaGroupContent>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitDocumentMediaGroup(
suspend fun BehaviourContext.waitDocumentMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
@ -119,17 +125,17 @@ suspend fun BehaviourContext.waitMedia(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
includeMediaGroups: Boolean = false,
filter: CommonMessageToContentMapper<MediaContent>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitMediaGroup(
suspend fun BehaviourContext.waitAnyMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: CommonMessageToContentMapper<MediaGroupContent>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitVisualMediaGroup(
suspend fun BehaviourContext.waitVisualMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
@ -146,21 +152,21 @@ suspend fun BehaviourContext.waitAudio(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
includeMediaGroups: Boolean = false,
filter: CommonMessageToContentMapper<AudioContent>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitDocument(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
includeMediaGroups: Boolean = false,
filter: CommonMessageToContentMapper<DocumentContent>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitPhoto(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
includeMediaGroups: Boolean = false,
filter: CommonMessageToContentMapper<PhotoContent>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitSticker(
@ -173,7 +179,7 @@ suspend fun BehaviourContext.waitVideo(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
includeMediaGroups: Boolean = false,
filter: CommonMessageToContentMapper<VideoContent>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitVideoNote(

View File

@ -12,7 +12,7 @@ import kotlinx.coroutines.flow.take
import kotlinx.coroutines.flow.toList
@PreviewFeature
internal suspend inline fun <reified T : MediaGroupContent> BehaviourContext.onMediaGroup(
internal suspend inline fun <reified T : MediaGroupContent> BehaviourContext.buildMediaGroupWaiter(
count: Int = 1,
initRequest: Request<*>? = null,
noinline errorFactory: NullableRequestBuilder<*> = { null },
@ -21,7 +21,7 @@ internal suspend inline fun <reified T : MediaGroupContent> BehaviourContext.onM
update.asSentMediaGroupUpdate() ?.data ?.let { mediaGroup ->
if (mediaGroup.all { message -> message.content is T } && (filter == null || filter(mediaGroup as List<MediaGroupMessage<T>>))) {
listOf(
mediaGroup.map { it.content as T } as List<MediaGroupMessage<T>>
mediaGroup.map { it.content as T }
)
} else {
null
@ -29,33 +29,39 @@ internal suspend inline fun <reified T : MediaGroupContent> BehaviourContext.onM
} ?: emptyList()
}.take(count).toList()
suspend fun BehaviourContext.waitMediaGroup(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: (suspend (List<MediaGroupMessage<MediaGroupContent>>) -> Boolean)? = null
) = buildMediaGroupWaiter(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitPlaylist(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: (suspend (List<MediaGroupMessage<AudioMediaGroupContent>>) -> Boolean)? = null
) = onMediaGroup(count, initRequest, errorFactory, filter)
) = buildMediaGroupWaiter(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitDocumentsGroup(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: (suspend (List<MediaGroupMessage<DocumentMediaGroupContent>>) -> Boolean)? = null
) = onMediaGroup(count, initRequest, errorFactory, filter)
) = buildMediaGroupWaiter(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitVisualGallery(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: (suspend (List<MediaGroupMessage<VisualMediaGroupContent>>) -> Boolean)? = null
) = onMediaGroup(count, initRequest, errorFactory, filter)
) = buildMediaGroupWaiter(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitPhotoGallery(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: (suspend (List<MediaGroupMessage<PhotoContent>>) -> Boolean)? = null
) = onMediaGroup(count, initRequest, errorFactory, filter)
) = buildMediaGroupWaiter(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitVideoGallery(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: (suspend (List<MediaGroupMessage<VideoContent>>) -> Boolean)? = null
) = onMediaGroup(count, initRequest, errorFactory, filter)
) = buildMediaGroupWaiter(count, initRequest, errorFactory, filter)

View File

@ -1,18 +1,12 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling
import dev.inmo.micro_utils.coroutines.safelyWithoutExceptions
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextAndTypeReceiver
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.expectFlow
import dev.inmo.tgbotapi.extensions.utils.*
import dev.inmo.tgbotapi.extensions.utils.extensions.sourceChat
import dev.inmo.tgbotapi.types.CallbackQuery.*
import dev.inmo.tgbotapi.types.message.ChatEvents.*
import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.*
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import kotlinx.coroutines.flow.filter
internal suspend inline fun <reified T : CallbackQuery> BehaviourContext.onCallbackQuery(
includeFilterByChatInBehaviourSubContext: Boolean = true,
@ -27,19 +21,15 @@ internal suspend inline fun <reified T : CallbackQuery> BehaviourContext.onCallb
}
}.let(::listOfNotNull)
}.subscribeSafelyWithoutExceptions(scope) { triggerQuery ->
val (jobToCancel, scenario) = if (includeFilterByChatInBehaviourSubContext) {
val subFilter = FlowsUpdatesFilter()
val subBehaviourContext = copy(flowsUpdatesFilter = subFilter)
flowsUpdatesFilter.allUpdatesFlow.filter {
val chat = it.sourceChat() ?: return@filter false
chat.id.chatId == triggerQuery.user.id.chatId
}.subscribeSafelyWithoutExceptions(scope, subFilter.asUpdateReceiver) to subBehaviourContext
} else {
null to this
doInSubContextWithUpdatesFilter(
updatesFilter = if (includeFilterByChatInBehaviourSubContext) {
{ it.sourceChat() ?.id ?.chatId == triggerQuery.user.id.chatId }
} else {
null
}
) {
scenarioReceiver(triggerQuery)
}
safelyWithoutExceptions { scenario.scenarioReceiver(triggerQuery) }
jobToCancel ?.cancel()
}

View File

@ -26,6 +26,12 @@ suspend fun BehaviourContext.command(
},
scenarioReceiver
)
suspend fun BehaviourContext.command(
command: String,
requireOnlyCommandInMessage: Boolean = true,
includeFilterByChatInBehaviourSubContext: Boolean = true,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, CommonMessage<TextContent>>
) = command(command.toRegex(), requireOnlyCommandInMessage, includeFilterByChatInBehaviourSubContext, scenarioReceiver)
suspend inline fun BehaviourContext.onCommand(
commandRegex: Regex,
@ -33,3 +39,10 @@ suspend inline fun BehaviourContext.onCommand(
includeFilterByChatInBehaviourSubContext: Boolean = true,
noinline scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, CommonMessage<TextContent>>
): Job = command(commandRegex, requireOnlyCommandInMessage, includeFilterByChatInBehaviourSubContext, scenarioReceiver)
suspend inline fun BehaviourContext.onCommand(
command: String,
requireOnlyCommandInMessage: Boolean = true,
includeFilterByChatInBehaviourSubContext: Boolean = true,
noinline scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, CommonMessage<TextContent>>
): Job = onCommand(command.toRegex(), requireOnlyCommandInMessage, includeFilterByChatInBehaviourSubContext, scenarioReceiver)

View File

@ -2,11 +2,8 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling
import dev.inmo.micro_utils.coroutines.safelyWithoutExceptions
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextAndTypeReceiver
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.expectFlow
import dev.inmo.tgbotapi.extensions.utils.*
import dev.inmo.tgbotapi.extensions.utils.extensions.sourceChat
@ -16,9 +13,7 @@ import dev.inmo.tgbotapi.types.message.content.*
import dev.inmo.tgbotapi.types.message.content.abstracts.*
import dev.inmo.tgbotapi.types.message.content.media.*
import dev.inmo.tgbotapi.types.message.payments.InvoiceContent
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import dev.inmo.tgbotapi.utils.PreviewFeature
import kotlinx.coroutines.flow.filter
typealias CommonMessageFilter<T> = (suspend (CommonMessage<T>) -> Boolean)
@ -50,21 +45,22 @@ internal suspend inline fun <reified T : MessageContent> BehaviourContext.onCont
}
}.let(::listOfNotNull)
}.subscribeSafelyWithoutExceptions(scope) { triggerMessage ->
val (jobToCancel, scenario) = if (includeFilterByChatInBehaviourSubContext) {
val subFilter = FlowsUpdatesFilter()
val subBehaviourContext = copy(flowsUpdatesFilter = subFilter)
flowsUpdatesFilter.allUpdatesFlow.filter {
val chat = it.sourceChat() ?: return@filter false
chat.id.chatId == triggerMessage.chat.id.chatId
}.subscribeSafelyWithoutExceptions(scope, subFilter.asUpdateReceiver) to subBehaviourContext
} else {
null to this
doInSubContextWithUpdatesFilter(
updatesFilter = if (includeFilterByChatInBehaviourSubContext) {
{ it.sourceChat() ?.id ?.chatId == triggerMessage.chat.id.chatId }
} else {
null
}
) {
scenarioReceiver(triggerMessage)
}
safelyWithoutExceptions { scenario.scenarioReceiver(triggerMessage) }
jobToCancel ?.cancel()
}
suspend fun BehaviourContext.onContentMessage(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: CommonMessageFilter<MessageContent>? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, CommonMessage<MessageContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, false, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onContact(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: CommonMessageFilter<ContactContent>? = null,
@ -105,7 +101,7 @@ suspend fun BehaviourContext.onAudioMediaGroup(
additionalFilter: CommonMessageFilter<AudioMediaGroupContent>? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, CommonMessage<AudioMediaGroupContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, true, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onDocumentMediaGroup(
suspend fun BehaviourContext.onDocumentMediaGroupContent(
includeFilterByChatInBehaviourSubContext: Boolean = true,
includeMediaGroups: Boolean = true,
additionalFilter: CommonMessageFilter<DocumentMediaGroupContent>? = null,
@ -113,7 +109,7 @@ suspend fun BehaviourContext.onDocumentMediaGroup(
) = onContent(includeFilterByChatInBehaviourSubContext, includeMediaGroups, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onMediaCollection(
includeFilterByChatInBehaviourSubContext: Boolean = true,
includeMediaGroups: Boolean = true,
includeMediaGroups: Boolean = false,
additionalFilter: (suspend (CommonMessage<MediaCollectionContent<TelegramMediaFile>>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, CommonMessage<MediaCollectionContent<TelegramMediaFile>>>
) = onContent(includeFilterByChatInBehaviourSubContext, includeMediaGroups, additionalFilter, scenarioReceiver)
@ -123,18 +119,6 @@ suspend fun BehaviourContext.onMedia(
additionalFilter: CommonMessageFilter<MediaContent>? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, CommonMessage<MediaContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, includeMediaGroups, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onMediaGroup(
includeFilterByChatInBehaviourSubContext: Boolean = true,
includeMediaGroups: Boolean = true,
additionalFilter: CommonMessageFilter<MediaGroupContent>? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, CommonMessage<MediaGroupContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, includeMediaGroups, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onVisualMediaGroup(
includeFilterByChatInBehaviourSubContext: Boolean = true,
includeMediaGroups: Boolean = true,
additionalFilter: CommonMessageFilter<VisualMediaGroupContent>? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, CommonMessage<VisualMediaGroupContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, includeMediaGroups, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onAnimation(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: CommonMessageFilter<AnimationContent>? = null,
@ -142,19 +126,19 @@ suspend fun BehaviourContext.onAnimation(
) = onContent(includeFilterByChatInBehaviourSubContext, false, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onAudio(
includeFilterByChatInBehaviourSubContext: Boolean = true,
includeMediaGroups: Boolean = true,
includeMediaGroups: Boolean = false,
additionalFilter: CommonMessageFilter<AudioContent>? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, CommonMessage<AudioContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, includeMediaGroups, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onDocument(
includeFilterByChatInBehaviourSubContext: Boolean = true,
includeMediaGroups: Boolean = true,
includeMediaGroups: Boolean = false,
additionalFilter: CommonMessageFilter<DocumentContent>? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, CommonMessage<DocumentContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, includeMediaGroups, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onPhoto(
includeFilterByChatInBehaviourSubContext: Boolean = true,
includeMediaGroups: Boolean = true,
includeMediaGroups: Boolean = false,
additionalFilter: CommonMessageFilter<PhotoContent>? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, CommonMessage<PhotoContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, includeMediaGroups, additionalFilter, scenarioReceiver)
@ -165,7 +149,7 @@ suspend fun BehaviourContext.onSticker(
) = onContent(includeFilterByChatInBehaviourSubContext, false, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onVideo(
includeFilterByChatInBehaviourSubContext: Boolean = true,
includeMediaGroups: Boolean = true,
includeMediaGroups: Boolean = false,
additionalFilter: CommonMessageFilter<VideoContent>? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, CommonMessage<VideoContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, includeMediaGroups, additionalFilter, scenarioReceiver)

View File

@ -1,21 +1,14 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling
import dev.inmo.micro_utils.coroutines.safelyWithoutExceptions
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextAndTypeReceiver
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.expectFlow
import dev.inmo.tgbotapi.extensions.utils.*
import dev.inmo.tgbotapi.extensions.utils.extensions.sourceChat
import dev.inmo.tgbotapi.types.message.ChatEvents.*
import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.*
import dev.inmo.tgbotapi.types.message.abstracts.ChatEventMessage
import dev.inmo.tgbotapi.types.message.content.*
import dev.inmo.tgbotapi.types.message.content.abstracts.*
import dev.inmo.tgbotapi.types.message.content.media.*
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import kotlinx.coroutines.flow.filter
internal suspend inline fun <reified T : ChatEvent> BehaviourContext.onEvent(
includeFilterByChatInBehaviourSubContext: Boolean = true,
@ -31,19 +24,13 @@ internal suspend inline fun <reified T : ChatEvent> BehaviourContext.onEvent(
}
}.let(::listOfNotNull)
}.subscribeSafelyWithoutExceptions(scope) { triggerMessage ->
val (jobToCancel, scenario) = if (includeFilterByChatInBehaviourSubContext) {
val subFilter = FlowsUpdatesFilter()
val subBehaviourContext = copy(flowsUpdatesFilter = subFilter)
flowsUpdatesFilter.allUpdatesFlow.filter {
val chat = it.sourceChat() ?: return@filter false
chat.id.chatId == triggerMessage.chat.id.chatId
}.subscribeSafelyWithoutExceptions(scope, subFilter.asUpdateReceiver) to subBehaviourContext
} else {
null to this
doInSubContextWithUpdatesFilter(
updatesFilter = if (includeFilterByChatInBehaviourSubContext) {
{ it.sourceChat() ?.id ?.chatId == triggerMessage.chat.id.chatId }
} else null
) {
scenarioReceiver(triggerMessage)
}
safelyWithoutExceptions { scenario.scenarioReceiver(triggerMessage) }
jobToCancel ?.cancel()
}
suspend fun BehaviourContext.onChannelEvent(

View File

@ -2,10 +2,8 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling
import dev.inmo.micro_utils.coroutines.safelyWithoutExceptions
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextAndTypeReceiver
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.expectFlow
import dev.inmo.tgbotapi.extensions.utils.*
import dev.inmo.tgbotapi.extensions.utils.extensions.sourceChat
@ -13,12 +11,10 @@ import dev.inmo.tgbotapi.extensions.utils.shortcuts.chat
import dev.inmo.tgbotapi.types.message.abstracts.MediaGroupMessage
import dev.inmo.tgbotapi.types.message.content.abstracts.*
import dev.inmo.tgbotapi.types.message.content.media.*
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import dev.inmo.tgbotapi.utils.PreviewFeature
import kotlinx.coroutines.flow.filter
@PreviewFeature
internal suspend inline fun <reified T : MediaGroupContent> BehaviourContext.onMediaGroup(
internal suspend inline fun <reified T : MediaGroupContent> BehaviourContext.buildMediaGroupTrigger(
includeFilterByChatInBehaviourSubContext: Boolean = true,
noinline additionalFilter: (suspend (List<MediaGroupMessage<T>>) -> Boolean)? = null,
noinline scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, List<MediaGroupMessage<T>>>
@ -31,45 +27,49 @@ internal suspend inline fun <reified T : MediaGroupContent> BehaviourContext.onM
}
} ?: emptyList()
}.subscribeSafelyWithoutExceptions(scope) { mediaGroup ->
val (jobToCancel, scenario) = if (includeFilterByChatInBehaviourSubContext) {
val subFilter = FlowsUpdatesFilter()
val subBehaviourContext = copy(flowsUpdatesFilter = subFilter)
val mediaGroupChat = mediaGroup.chat!!
flowsUpdatesFilter.allUpdatesFlow.filter {
val chat = it.sourceChat() ?: return@filter false
chat.id.chatId == mediaGroupChat.id.chatId
}.subscribeSafelyWithoutExceptions(scope, subFilter.asUpdateReceiver) to subBehaviourContext
} else {
null to this
val mediaGroupChat = mediaGroup.chat!!
doInSubContextWithUpdatesFilter(
updatesFilter = if (includeFilterByChatInBehaviourSubContext) {
{ it.sourceChat() ?.id ?.chatId == mediaGroupChat.id.chatId }
} else null
) {
scenarioReceiver(mediaGroup)
}
safelyWithoutExceptions { scenario.scenarioReceiver(mediaGroup) }
jobToCancel ?.cancel()
}
suspend fun BehaviourContext.onMediaGroup(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (List<MediaGroupMessage<MediaGroupContent>>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, List<MediaGroupMessage<MediaGroupContent>>>
) = buildMediaGroupTrigger(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onPlaylist(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (List<MediaGroupMessage<AudioMediaGroupContent>>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, List<MediaGroupMessage<AudioMediaGroupContent>>>
) = onMediaGroup(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
) = buildMediaGroupTrigger(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onDocumentsGroup(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (List<MediaGroupMessage<DocumentMediaGroupContent>>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, List<MediaGroupMessage<DocumentMediaGroupContent>>>
) = onMediaGroup(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
) = buildMediaGroupTrigger(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onVisualGallery(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (List<MediaGroupMessage<VisualMediaGroupContent>>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, List<MediaGroupMessage<VisualMediaGroupContent>>>
) = onMediaGroup(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
) = buildMediaGroupTrigger(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onVisualMediaGroup(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (List<MediaGroupMessage<VisualMediaGroupContent>>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, List<MediaGroupMessage<VisualMediaGroupContent>>>
) = onVisualGallery(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onPhotoGallery(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (List<MediaGroupMessage<PhotoContent>>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, List<MediaGroupMessage<PhotoContent>>>
) = onMediaGroup(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
) = buildMediaGroupTrigger(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onVideoGallery(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (List<MediaGroupMessage<VideoContent>>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, List<MediaGroupMessage<VideoContent>>>
) = onMediaGroup(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
) = buildMediaGroupTrigger(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)