diff --git a/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/utils/UpdatesHandling.kt b/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/utils/UpdatesHandling.kt index 1c660b8ad2..c0ad2cd264 100644 --- a/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/utils/UpdatesHandling.kt +++ b/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/utils/UpdatesHandling.kt @@ -1,7 +1,8 @@ package dev.inmo.tgbotapi.extensions.api.utils +import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions import dev.inmo.tgbotapi.extensions.api.InternalUtils.convertWithMediaGroupUpdates -import dev.inmo.tgbotapi.types.message.abstracts.PossiblySentViaBotCommonMessage +import dev.inmo.tgbotapi.types.message.abstracts.PossiblyMediaGroupMessage import dev.inmo.tgbotapi.types.update.abstracts.BaseMessageUpdate import dev.inmo.tgbotapi.types.update.abstracts.Update import dev.inmo.tgbotapi.updateshandlers.UpdateReceiver @@ -28,26 +29,18 @@ fun CoroutineScope.updateHandlerWithMediaGroupsAdaptation( ) launch { - launch { + launchSafelyWithoutExceptions { for (update in updatesChannel) { - val dataAsPossiblySentViaBotCommonMessage = update.data as? PossiblySentViaBotCommonMessage<*> - - if (dataAsPossiblySentViaBotCommonMessage == null) { - output(update) - continue + val data = update.data + when { + data is PossiblyMediaGroupMessage<*> && data.mediaGroupId != null -> { + mediaGroupChannel.send("${data.mediaGroupId}${update::class.simpleName}" to update as BaseMessageUpdate) + } + else -> output(update) } - - val mediaGroupId = dataAsPossiblySentViaBotCommonMessage.mediaGroupId - - if (mediaGroupId == null) { - output(update) - continue - } - - mediaGroupChannel.send("${mediaGroupId}${update::class.simpleName}" to update as BaseMessageUpdate) } } - launch { + launchSafelyWithoutExceptions { for ((_, mediaGroup) in mediaGroupAccumulatedChannel) { mediaGroup.convertWithMediaGroupUpdates().forEach { output(it) diff --git a/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSMBuilder.kt b/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSMBuilder.kt index d3452123f6..03750af9b8 100644 --- a/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSMBuilder.kt +++ b/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSMBuilder.kt @@ -8,6 +8,7 @@ import dev.inmo.micro_utils.fsm.common.utils.StateHandlingErrorHandler import dev.inmo.micro_utils.fsm.common.utils.defaultStateHandlingErrorHandler import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.longPolling +import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.updateHandlerWithMediaGroupsAdaptation import dev.inmo.tgbotapi.types.Seconds import dev.inmo.tgbotapi.types.update.abstracts.Update import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter @@ -47,6 +48,10 @@ suspend fun TelegramBot.buildBehaviourWithFSM( * 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 + * + * @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null + * in case you wish to enable classic way of updates handling, but in that mode some media group messages can be + * retrieved in different updates */ suspend fun TelegramBot.buildBehaviourWithFSMAndStartLongPolling( upstreamUpdatesFlow: Flow? = null, @@ -117,6 +122,10 @@ suspend fun TelegramBot.buildBehaviourWithFSM( * using [longPolling]. For [longPolling] will be used result [BehaviourContextWithFSM] for both parameters * flowsUpdatesFilter and scope * + * @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null + * in case you wish to enable classic way of updates handling, but in that mode some media group messages can be + * retrieved in different updates + * * @see buildBehaviourWithFSMAndStartLongPolling * @see BehaviourContext * @see longPolling diff --git a/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/TelegramBotWithFSM.kt b/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/TelegramBotWithFSM.kt index b4dd08dca6..85a5f0d402 100644 --- a/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/TelegramBotWithFSM.kt +++ b/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/TelegramBotWithFSM.kt @@ -11,6 +11,7 @@ import dev.inmo.tgbotapi.bot.ktor.KtorRequestsExecutorBuilder import dev.inmo.tgbotapi.bot.ktor.telegramBot import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling +import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.updateHandlerWithMediaGroupsAdaptation import dev.inmo.tgbotapi.types.Seconds import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter import dev.inmo.tgbotapi.utils.telegramBotAPIDefaultUrl @@ -26,6 +27,10 @@ import kotlin.coroutines.coroutineContext * **WARNING** This method WILL NOT launch any listening of updates. Use something like * [startGettingOfUpdatesByLongPolling] or tools for work with webhooks * + * @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null + * in case you wish to enable classic way of updates handling, but in that mode some media group messages can be + * retrieved in different updates + * * @return Created bot which has been used to create [BehaviourContext] via [buildBehaviourWithFSM] * * @see [BehaviourContext] @@ -73,6 +78,10 @@ suspend fun telegramBotWithBehaviourAndFSM( * Create bot using [telegramBot] and start listening for updates using [buildBehaviourWithFSMAndStartLongPolling]. This * method will launch updates retrieving via long polling inside of [buildBehaviourWithFSMAndStartLongPolling] * + * @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null + * in case you wish to enable classic way of updates handling, but in that mode some media group messages can be + * retrieved in different updates + * * @return Pair of [TelegramBot] and [Job]. This [Job] can be used to stop listening updates in your [block] you passed * here * diff --git a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourBuilders.kt b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourBuilders.kt index 9ad2c878c1..1990b89eba 100644 --- a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourBuilders.kt +++ b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourBuilders.kt @@ -5,6 +5,7 @@ import dev.inmo.micro_utils.coroutines.ExceptionHandler import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.longPolling import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling +import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.updateHandlerWithMediaGroupsAdaptation import dev.inmo.tgbotapi.types.Seconds import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter import kotlinx.coroutines.* @@ -47,6 +48,10 @@ suspend fun TelegramBot.buildBehaviour( * Use this method to build bot behaviour and run it via long polling. In case you wish to get [FlowsUpdatesFilter] for * additional manipulations, you must provide external [FlowsUpdatesFilter] in other [buildBehaviour] function. * + * @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null + * in case you wish to enable classic way of updates handling, but in that mode some media group messages can be + * retrieved in different updates + * * @see buildBehaviour * @see BehaviourContext * @see startGettingOfUpdatesByLongPolling diff --git a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/TelegramBot.kt b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/TelegramBot.kt index 0abbf6cefe..a72e8df932 100644 --- a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/TelegramBot.kt +++ b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/TelegramBot.kt @@ -5,6 +5,7 @@ import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.bot.ktor.KtorRequestsExecutorBuilder import dev.inmo.tgbotapi.bot.ktor.telegramBot import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling +import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.updateHandlerWithMediaGroupsAdaptation import dev.inmo.tgbotapi.types.Seconds import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter import dev.inmo.tgbotapi.utils.telegramBotAPIDefaultUrl @@ -53,6 +54,10 @@ suspend fun telegramBotWithBehaviour( * * **WARNING** This method WILL launch updates listening inside of calling [buildBehaviourWithLongPolling] * + * @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null + * in case you wish to enable classic way of updates handling, but in that mode some media group messages can be + * retrieved in different updates + * * @return Pair of [TelegramBot] and [Job]. This [Job] can be used to stop listening updates in your [block] you passed * here * diff --git a/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/updates/UpdatesUtils.kt b/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/updates/UpdatesUtils.kt index a0c36c0c35..83757480d8 100644 --- a/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/updates/UpdatesUtils.kt +++ b/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/updates/UpdatesUtils.kt @@ -27,7 +27,8 @@ fun List.lastUpdateIdentifier(): UpdateIdentifier? { } /** - * Will convert incoming list of updates to list with [MediaGroupUpdate]s + * Will convert incoming list of [Update]s to list with [Update]s, which include [dev.inmo.tgbotapi.types.message.abstracts.ContentMessage]s + * with [dev.inmo.tgbotapi.types.message.content.MediaGroupContent] */ fun List.convertWithMediaGroupUpdates(): List { val resultUpdates = mutableListOf() @@ -67,4 +68,5 @@ fun List.convertWithMediaGroupUpdates(): List { * * @throws IllegalStateException */ +@Deprecated("Redundant", ReplaceWith("this")) fun BaseEditMessageUpdate.toEditMediaGroupUpdate() = this diff --git a/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/updates/retrieving/LongPolling.kt b/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/updates/retrieving/LongPolling.kt index 9c5fec7946..a891eed163 100644 --- a/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/updates/retrieving/LongPolling.kt +++ b/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/updates/retrieving/LongPolling.kt @@ -21,7 +21,8 @@ import kotlinx.coroutines.flow.* /** * @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null - * in case you wish to enable classic way of updates handling + * in case you wish to enable classic way of updates handling, but in that mode some media group messages can be + * retrieved in different updates */ fun TelegramBot.longPollingFlow( timeoutSeconds: Seconds = 30, @@ -129,6 +130,11 @@ fun TelegramBot.longPollingFlow( } } +/** + * @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null + * in case you wish to enable classic way of updates handling, but in that mode some media group messages can be + * retrieved in different updates + */ fun TelegramBot.startGettingOfUpdatesByLongPolling( timeoutSeconds: Seconds = 30, scope: CoroutineScope = CoroutineScope(Dispatchers.Default), @@ -152,6 +158,10 @@ fun TelegramBot.startGettingOfUpdatesByLongPolling( ) /** + * @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null + * in case you wish to enable classic way of updates handling, but in that mode some media group messages can be + * retrieved in different updates + * * @return [kotlinx.coroutines.flow.Flow] which will emit updates to the collector while they will be accumulated. Works * the same as [longPollingFlow], but it will cancel the flow after the first one [HttpRequestTimeoutException] */ @@ -179,6 +189,11 @@ fun TelegramBot.createAccumulatedUpdatesRetrieverFlow( !(it is InlineQueryUpdate && avoidInlineQueries || it is CallbackQueryUpdate && avoidCallbackQueries) } +/** + * @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null + * in case you wish to enable classic way of updates handling, but in that mode some media group messages can be + * retrieved in different updates + */ fun TelegramBot.retrieveAccumulatedUpdates( avoidInlineQueries: Boolean = false, avoidCallbackQueries: Boolean = false, @@ -201,6 +216,11 @@ fun TelegramBot.retrieveAccumulatedUpdates( updatesReceiver(it) } +/** + * @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null + * in case you wish to enable classic way of updates handling, but in that mode some media group messages can be + * retrieved in different updates + */ fun TelegramBot.retrieveAccumulatedUpdates( flowsUpdatesFilter: FlowsUpdatesFilter, avoidInlineQueries: Boolean = false, @@ -220,6 +240,11 @@ fun TelegramBot.retrieveAccumulatedUpdates( flowsUpdatesFilter.asUpdateReceiver ) +/** + * @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null + * in case you wish to enable classic way of updates handling, but in that mode some media group messages can be + * retrieved in different updates + */ suspend fun TelegramBot.flushAccumulatedUpdates( avoidInlineQueries: Boolean = false, avoidCallbackQueries: Boolean = false, @@ -241,8 +266,12 @@ suspend fun TelegramBot.flushAccumulatedUpdates( ).join() /** - * Will [startGettingOfUpdatesByLongPolling] using incoming [flowsUpdatesFilter]. It is assumed that you ALREADY CONFIGURE + * Will [startGettingOfUpdatesByLongPolling] using incoming [updatesFilter]. It is assumed that you ALREADY CONFIGURE * all updates receivers, because this method will trigger getting of updates and. + * + * @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null + * in case you wish to enable classic way of updates handling, but in that mode some media group messages can be + * retrieved in different updates */ fun TelegramBot.longPolling( updatesFilter: UpdatesFilter, @@ -269,6 +298,10 @@ fun TelegramBot.longPolling( * Will enable [longPolling] by creating [FlowsUpdatesFilter] with [flowsUpdatesFilterUpdatesKeeperCount] as an argument * and applied [flowUpdatesPreset]. It is assumed that you WILL CONFIGURE all updates receivers in [flowUpdatesPreset], * because of after [flowUpdatesPreset] method calling will be triggered getting of updates. + * + * @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null + * in case you wish to enable classic way of updates handling, but in that mode some media group messages can be + * retrieved in different updates */ @Suppress("unused") fun TelegramBot.longPolling( @@ -282,6 +315,11 @@ fun TelegramBot.longPolling( flowUpdatesPreset: FlowsUpdatesFilter.() -> Unit ): Job = longPolling(FlowsUpdatesFilter(flowsUpdatesFilterUpdatesKeeperCount).apply(flowUpdatesPreset), timeoutSeconds, scope, autoDisableWebhooks, autoSkipTimeoutExceptions, mediaGroupsDebounceTimeMillis, exceptionsHandler) +/** + * @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null + * in case you wish to enable classic way of updates handling, but in that mode some media group messages can be + * retrieved in different updates + */ fun RequestsExecutor.startGettingOfUpdatesByLongPolling( updatesFilter: UpdatesFilter, timeoutSeconds: Seconds = 30, diff --git a/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/updates/retrieving/MediaGroupsIncluder.kt b/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/updates/retrieving/MediaGroupsIncluder.kt index a5d490d4b5..b2c4df5376 100644 --- a/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/updates/retrieving/MediaGroupsIncluder.kt +++ b/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/updates/retrieving/MediaGroupsIncluder.kt @@ -1,6 +1,5 @@ package dev.inmo.tgbotapi.extensions.utils.updates.retrieving -import dev.inmo.micro_utils.coroutines.launchSafely import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions import dev.inmo.tgbotapi.extensions.utils.updates.convertWithMediaGroupUpdates import dev.inmo.tgbotapi.types.message.abstracts.PossiblyMediaGroupMessage @@ -12,7 +11,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.launch - /** * Create [UpdateReceiver] object which will correctly accumulate updates and send into output updates which INCLUDE * [dev.inmo.tgbotapi.types.update.MediaGroupUpdates.MediaGroupUpdate]s. diff --git a/tgbotapi.utils/src/jvmMain/kotlin/dev/inmo/tgbotapi/extensions/utils/updates/retrieving/Webhook.kt b/tgbotapi.utils/src/jvmMain/kotlin/dev/inmo/tgbotapi/extensions/utils/updates/retrieving/Webhook.kt index a675fa27e3..c7ab3b32de 100644 --- a/tgbotapi.utils/src/jvmMain/kotlin/dev/inmo/tgbotapi/extensions/utils/updates/retrieving/Webhook.kt +++ b/tgbotapi.utils/src/jvmMain/kotlin/dev/inmo/tgbotapi/extensions/utils/updates/retrieving/Webhook.kt @@ -26,6 +26,9 @@ import java.util.concurrent.Executors * @param [scope] Will be used for mapping of media groups * @param [exceptionsHandler] Pass this parameter to set custom exception handler for getting updates * @param [block] Some receiver block like [dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter] + * @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null + * in case you wish to enable classic way of updates handling, but in that mode some media group messages can be + * retrieved in different updates * * @see dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter * @see UpdatesFilter @@ -57,6 +60,11 @@ fun Route.includeWebhookHandlingInRoute( } } +/** + * @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null + * in case you wish to enable classic way of updates handling, but in that mode some media group messages can be + * retrieved in different updates + */ fun Route.includeWebhookHandlingInRouteWithFlows( scope: CoroutineScope, exceptionsHandler: ExceptionHandler? = null, @@ -76,6 +84,9 @@ fun Route.includeWebhookHandlingInRouteWithFlows( * @param listenRoute address to listen by bot. If null - will be set up in root of host * @param scope Scope which will be used for * @param privateKeyConfig If configured - server will be created with [sslConnector]. [connector] will be used otherwise + * @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null + * in case you wish to enable classic way of updates handling, but in that mode some media group messages can be + * retrieved in different updates * * @see dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter * @see UpdatesFilter @@ -132,6 +143,9 @@ fun startListenWebhooks( * @param listenPort port which will be listen by bot * @param listenRoute address to listen by bot * @param scope Scope which will be used for + * @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null + * in case you wish to enable classic way of updates handling, but in that mode some media group messages can be + * retrieved in different updates * * @see dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter * @see UpdatesFilter