diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ec5dae49a..f8fbffb0d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,43 @@ * `closePollExactAfter` * `closePollAfter` +### 0.27.11 + +* `TelegramBotAPI-extensions-utils`: + * `Flow>.flatMap` extension was added + * Extensions for `FlowUpdatesFilter` were added: + * `FlowsUpdatesFilter#allSentMessagesFlow` (combination of `messageFlow` and `channelPostFlow`) + * `FlowsUpdatesFilter#allSentMediaGroupsFlow` (combination of `messageMediaGroupFlow` and `channelPostMediaGroupFlow`) + * `FlowsUpdatesFilter#sentMessages` + * `FlowsUpdatesFilter#sentMessagesWithMediaGroups` + * `FlowsUpdatesFilter#photoMessagesWithMediaGroups` + * `FlowsUpdatesFilter#imageMessagesWithMediaGroups` + * `FlowsUpdatesFilter#videoMessagesWithMediaGroups` + * `FlowsUpdatesFilter#mediaGroupMessages` + * `FlowsUpdatesFilter#mediaGroupPhotosMessages` + * `FlowsUpdatesFilter#mediaGroupVideosMessages` + * A lot of extensions like `Flow#textMessages` were added: + * `Flow#animationMessages` + * `Flow#audioMessages` + * `Flow#contactMessages` + * `Flow#diceMessages` + * `Flow#documentMessages` + * `Flow#gameMessages` + * `Flow#invoiceMessages` + * `Flow#locationMessages` + * `Flow#photoMessages` + * `Flow#imageMessages` + * `Flow#pollMessages` + * `Flow#stickerMessages` + * `Flow#textMessages` + * `Flow#venueMessages` + * `Flow#videoMessages` + * `Flow#videoNoteMessages` + * `Flow#voiceMessages` + * `Flow#mediaGroupMessages` + * `Flow#mediaGroupPhotosMessages` + * `Flow#mediaGroupVideosMessages` + ### 0.27.10 * `TelegramBotAPI-extensions-api`: diff --git a/TelegramBotAPI-extensions-utils/src/commonMain/kotlin/com/github/insanusmokrassar/TelegramBotAPI/extensions/utils/FlowsAggregation.kt b/TelegramBotAPI-extensions-utils/src/commonMain/kotlin/com/github/insanusmokrassar/TelegramBotAPI/extensions/utils/FlowsAggregation.kt index 37333b2cec..89398c7e10 100644 --- a/TelegramBotAPI-extensions-utils/src/commonMain/kotlin/com/github/insanusmokrassar/TelegramBotAPI/extensions/utils/FlowsAggregation.kt +++ b/TelegramBotAPI-extensions-utils/src/commonMain/kotlin/com/github/insanusmokrassar/TelegramBotAPI/extensions/utils/FlowsAggregation.kt @@ -5,6 +5,9 @@ import kotlinx.coroutines.channels.BroadcastChannel import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.* +/** + * Analog of [merge] function for [Flow]s. The difference is in the usage of [BroadcastChannel] in this case + */ fun aggregateFlows( withScope: CoroutineScope, vararg flows: Flow, @@ -18,3 +21,19 @@ fun aggregateFlows( } return bc.asFlow() } + +fun Flow>.flatMap(): Flow = flow { + collect { + it.forEach { + emit(it) + } + } +} + +fun Flow.flatMap(mapper: (T) -> Iterable): Flow = flow { + collect { + mapper(it).forEach { + emit(it) + } + } +} diff --git a/TelegramBotAPI-extensions-utils/src/commonMain/kotlin/com/github/insanusmokrassar/TelegramBotAPI/extensions/utils/extensions/FlowsUpdatesFilter.kt b/TelegramBotAPI-extensions-utils/src/commonMain/kotlin/com/github/insanusmokrassar/TelegramBotAPI/extensions/utils/extensions/FlowsUpdatesFilter.kt new file mode 100644 index 0000000000..51efd9f2cb --- /dev/null +++ b/TelegramBotAPI-extensions-utils/src/commonMain/kotlin/com/github/insanusmokrassar/TelegramBotAPI/extensions/utils/extensions/FlowsUpdatesFilter.kt @@ -0,0 +1,19 @@ +package com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.extensions + +import com.github.insanusmokrassar.TelegramBotAPI.types.update.MediaGroupUpdates.SentMediaGroupUpdate +import com.github.insanusmokrassar.TelegramBotAPI.types.update.abstracts.BaseSentMessageUpdate +import com.github.insanusmokrassar.TelegramBotAPI.updateshandlers.FlowsUpdatesFilter +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.merge + +val FlowsUpdatesFilter.allSentMessagesFlow: Flow + get() = merge( + messageFlow, + channelPostFlow + ) + +val FlowsUpdatesFilter.allSentMediaGroupsFlow: Flow + get() = merge( + messageMediaGroupFlow, + channelPostMediaGroupFlow + ) diff --git a/TelegramBotAPI-extensions-utils/src/commonMain/kotlin/com/github/insanusmokrassar/TelegramBotAPI/extensions/utils/shortcuts/FlowsUpdatesFilter.kt b/TelegramBotAPI-extensions-utils/src/commonMain/kotlin/com/github/insanusmokrassar/TelegramBotAPI/extensions/utils/shortcuts/FlowsUpdatesFilter.kt index 8b02c3924f..ff7a3705f1 100644 --- a/TelegramBotAPI-extensions-utils/src/commonMain/kotlin/com/github/insanusmokrassar/TelegramBotAPI/extensions/utils/shortcuts/FlowsUpdatesFilter.kt +++ b/TelegramBotAPI-extensions-utils/src/commonMain/kotlin/com/github/insanusmokrassar/TelegramBotAPI/extensions/utils/shortcuts/FlowsUpdatesFilter.kt @@ -1,12 +1,17 @@ package com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.shortcuts import com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.aggregateFlows +import com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.flatMap import com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.updates.asContentMessagesFlow +import com.github.insanusmokrassar.TelegramBotAPI.types.message.abstracts.CommonMessage import com.github.insanusmokrassar.TelegramBotAPI.types.message.abstracts.ContentMessage import com.github.insanusmokrassar.TelegramBotAPI.types.message.content.* +import com.github.insanusmokrassar.TelegramBotAPI.types.message.content.abstracts.MediaGroupContent import com.github.insanusmokrassar.TelegramBotAPI.types.message.content.abstracts.MessageContent import com.github.insanusmokrassar.TelegramBotAPI.types.message.content.media.* import com.github.insanusmokrassar.TelegramBotAPI.types.message.payments.InvoiceContent +import com.github.insanusmokrassar.TelegramBotAPI.types.update.MediaGroupUpdates.SentMediaGroupUpdate +import com.github.insanusmokrassar.TelegramBotAPI.types.update.abstracts.BaseSentMessageUpdate import com.github.insanusmokrassar.TelegramBotAPI.updateshandlers.FlowsUpdatesFilter import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.* @@ -19,60 +24,120 @@ inline fun filterForContentMessage(): suspend (Cont } } +@Suppress("UNCHECKED_CAST") +inline fun Flow.filterContentMessages( +): Flow> = asContentMessagesFlow().mapNotNull(filterForContentMessage()) + +inline fun Flow.filterMediaGroupMessages( +): Flow>> = map { + it.data.mapNotNull { message -> + if (message.content is T) { + message as CommonMessage + } else { + null + } + } +} + /** * @param scopeToIncludeChannels This parameter is required when you want to include [textMessages] for channels too. - * In this case will be created new channel which will agregate messages from [FlowsUpdatesFilter.messageFlow] and + * In this case will be created new channel which will aggregate messages from [FlowsUpdatesFilter.messageFlow] and * [FlowsUpdatesFilter.channelPostFlow]. In case it is null will be used [Flow]s mapping */ @Suppress("UNCHECKED_CAST") inline fun FlowsUpdatesFilter.filterContentMessages( scopeToIncludeChannels: CoroutineScope? = null ): Flow> { - val filter = filterForContentMessage() - return scopeToIncludeChannels ?.let { scope -> + return (scopeToIncludeChannels ?.let { scope -> aggregateFlows( scope, - messageFlow.asContentMessagesFlow().mapNotNull(filter), - channelPostFlow.asContentMessagesFlow().mapNotNull(filter) + messageFlow, + channelPostFlow ) - } ?: messageFlow.asContentMessagesFlow().mapNotNull(filter) + } ?: messageFlow).filterContentMessages() } +/** + * @param scopeToIncludeChannels This parameter is required when you want to include [SentMediaGroupUpdate] for channels + * too. In this case will be created new channel which will aggregate messages from [FlowsUpdatesFilter.messageFlow] and + * [FlowsUpdatesFilter.channelPostFlow]. In case it is null will be used [Flow]s mapping + */ +@Suppress("UNCHECKED_CAST") +inline fun FlowsUpdatesFilter.filterMediaGroupMessages( + scopeToIncludeChannels: CoroutineScope? = null +): Flow>> { + return (scopeToIncludeChannels ?.let { scope -> + aggregateFlows( + scope, + messageMediaGroupFlow, + channelPostMediaGroupFlow + ) + } ?: messageMediaGroupFlow).filterMediaGroupMessages() +} + +fun FlowsUpdatesFilter.sentMessages( + scopeToIncludeChannels: CoroutineScope? = null +): Flow> = filterContentMessages(scopeToIncludeChannels) +fun FlowsUpdatesFilter.sentMessagesWithMediaGroups( + scopeToIncludeChannels: CoroutineScope? = null +): Flow> = merge( + sentMessages(scopeToIncludeChannels), + mediaGroupMessages(scopeToIncludeChannels).flatMap { + it.mapNotNull { it as? ContentMessage } + } +) + +fun Flow.animationMessages() = filterContentMessages() fun FlowsUpdatesFilter.animationMessages( scopeToIncludeChannels: CoroutineScope? = null ) = filterContentMessages(scopeToIncludeChannels) +fun Flow.audioMessages() = filterContentMessages() fun FlowsUpdatesFilter.audioMessages( scopeToIncludeChannels: CoroutineScope? = null ) = filterContentMessages(scopeToIncludeChannels) +fun Flow.contactMessages() = filterContentMessages() fun FlowsUpdatesFilter.contactMessages( scopeToIncludeChannels: CoroutineScope? = null ) = filterContentMessages(scopeToIncludeChannels) +fun Flow.diceMessages() = filterContentMessages() fun FlowsUpdatesFilter.diceMessages( scopeToIncludeChannels: CoroutineScope? = null ) = filterContentMessages(scopeToIncludeChannels) +fun Flow.documentMessages() = filterContentMessages() fun FlowsUpdatesFilter.documentMessages( scopeToIncludeChannels: CoroutineScope? = null ) = filterContentMessages(scopeToIncludeChannels) +fun Flow.gameMessages() = filterContentMessages() fun FlowsUpdatesFilter.gameMessages( scopeToIncludeChannels: CoroutineScope? = null ) = filterContentMessages(scopeToIncludeChannels) +fun Flow.invoiceMessages() = filterContentMessages() fun FlowsUpdatesFilter.invoiceMessages( scopeToIncludeChannels: CoroutineScope? = null ) = filterContentMessages(scopeToIncludeChannels) +fun Flow.locationMessages() = filterContentMessages() fun FlowsUpdatesFilter.locationMessages( scopeToIncludeChannels: CoroutineScope? = null ) = filterContentMessages(scopeToIncludeChannels) +fun Flow.photoMessages() = filterContentMessages() +fun Flow.imageMessages() = photoMessages() fun FlowsUpdatesFilter.photoMessages( scopeToIncludeChannels: CoroutineScope? = null ) = filterContentMessages(scopeToIncludeChannels) +fun FlowsUpdatesFilter.photoMessagesWithMediaGroups( + scopeToIncludeChannels: CoroutineScope? = null +) = merge( + filterContentMessages(scopeToIncludeChannels), + mediaGroupPhotosMessages(scopeToIncludeChannels).flatMap() +) /** * Shortcut for [photoMessages] */ @@ -80,33 +145,63 @@ fun FlowsUpdatesFilter.photoMessages( inline fun FlowsUpdatesFilter.imageMessages( scopeToIncludeChannels: CoroutineScope? = null ) = photoMessages(scopeToIncludeChannels) +fun FlowsUpdatesFilter.imageMessagesWithMediaGroups( + scopeToIncludeChannels: CoroutineScope? = null +) = photoMessagesWithMediaGroups(scopeToIncludeChannels) +fun Flow.pollMessages() = filterContentMessages() fun FlowsUpdatesFilter.pollMessages( scopeToIncludeChannels: CoroutineScope? = null ) = filterContentMessages(scopeToIncludeChannels) +fun Flow.stickerMessages() = filterContentMessages() fun FlowsUpdatesFilter.stickerMessages( scopeToIncludeChannels: CoroutineScope? = null ) = filterContentMessages(scopeToIncludeChannels) +fun Flow.textMessages() = filterContentMessages() fun FlowsUpdatesFilter.textMessages( scopeToIncludeChannels: CoroutineScope? = null ) = filterContentMessages(scopeToIncludeChannels) +fun Flow.venueMessages() = filterContentMessages() fun FlowsUpdatesFilter.venueMessages( scopeToIncludeChannels: CoroutineScope? = null ) = filterContentMessages(scopeToIncludeChannels) +fun Flow.videoMessages() = filterContentMessages() fun FlowsUpdatesFilter.videoMessages( scopeToIncludeChannels: CoroutineScope? = null ) = filterContentMessages(scopeToIncludeChannels) +fun FlowsUpdatesFilter.videoMessagesWithMediaGroups( + scopeToIncludeChannels: CoroutineScope? = null +) = merge( + filterContentMessages(scopeToIncludeChannels), + mediaGroupVideosMessages(scopeToIncludeChannels).flatMap() +) +fun Flow.videoNoteMessages() = filterContentMessages() fun FlowsUpdatesFilter.videoNoteMessages( scopeToIncludeChannels: CoroutineScope? = null ) = filterContentMessages(scopeToIncludeChannels) +fun Flow.voiceMessages() = filterContentMessages() fun FlowsUpdatesFilter.voiceMessages( scopeToIncludeChannels: CoroutineScope? = null ) = filterContentMessages(scopeToIncludeChannels) +fun Flow.mediaGroupMessages() = filterMediaGroupMessages() +fun FlowsUpdatesFilter.mediaGroupMessages( + scopeToIncludeChannels: CoroutineScope? = null +) = filterMediaGroupMessages(scopeToIncludeChannels) + +fun Flow.mediaGroupPhotosMessages() = filterMediaGroupMessages() +fun FlowsUpdatesFilter.mediaGroupPhotosMessages( + scopeToIncludeChannels: CoroutineScope? = null +) = filterMediaGroupMessages(scopeToIncludeChannels) + +fun Flow.mediaGroupVideosMessages() = filterMediaGroupMessages() +fun FlowsUpdatesFilter.mediaGroupVideosMessages( + scopeToIncludeChannels: CoroutineScope? = null +) = filterMediaGroupMessages(scopeToIncludeChannels) diff --git a/TelegramBotAPI-extensions-utils/src/commonMain/kotlin/com/github/insanusmokrassar/TelegramBotAPI/extensions/utils/updates/retrieving/LongPolling.kt b/TelegramBotAPI-extensions-utils/src/commonMain/kotlin/com/github/insanusmokrassar/TelegramBotAPI/extensions/utils/updates/retrieving/LongPolling.kt index 6fbd655048..2e77fef119 100644 --- a/TelegramBotAPI-extensions-utils/src/commonMain/kotlin/com/github/insanusmokrassar/TelegramBotAPI/extensions/utils/updates/retrieving/LongPolling.kt +++ b/TelegramBotAPI-extensions-utils/src/commonMain/kotlin/com/github/insanusmokrassar/TelegramBotAPI/extensions/utils/updates/retrieving/LongPolling.kt @@ -114,7 +114,7 @@ fun RequestsExecutor.startGettingOfUpdatesByLongPolling( pollAnswerCallback: UpdateReceiver? = null, timeoutSeconds: Seconds = 30, exceptionsHandler: ExceptionHandler? = null, - scope: CoroutineScope = GlobalScope + scope: CoroutineScope = CoroutineScope(Dispatchers.Default) ): Job { return startGettingOfUpdatesByLongPolling( SimpleUpdatesFilter( diff --git a/gradle.properties b/gradle.properties index 2dd30909ce..248839347c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,6 +9,6 @@ ktor_version=1.3.2 javax_activation_version=1.1.1 library_group=com.github.insanusmokrassar -library_version=0.27.10 +library_version=0.27.11 gradle_bintray_plugin_version=1.8.4