package com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.updates.retrieving import com.github.insanusmokrassar.TelegramBotAPI.bot.RequestsExecutor import com.github.insanusmokrassar.TelegramBotAPI.bot.exceptions.RequestException import com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.updates.convertWithMediaGroupUpdates import com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.updates.lastUpdateIdentifier import com.github.insanusmokrassar.TelegramBotAPI.requests.GetUpdates import com.github.insanusmokrassar.TelegramBotAPI.types.* import com.github.insanusmokrassar.TelegramBotAPI.types.update.* import com.github.insanusmokrassar.TelegramBotAPI.types.update.MediaGroupUpdates.* import com.github.insanusmokrassar.TelegramBotAPI.types.update.abstracts.Update import com.github.insanusmokrassar.TelegramBotAPI.updateshandlers.* import com.github.insanusmokrassar.TelegramBotAPI.utils.PreviewFeature import com.github.insanusmokrassar.TelegramBotAPI.utils.handleSafely import kotlinx.coroutines.* fun RequestsExecutor.startGettingOfUpdatesByLongPolling( timeoutSeconds: Seconds = 30, scope: CoroutineScope = CoroutineScope(Dispatchers.Default), exceptionsHandler: (suspend (Exception) -> Unit)? = null, allowedUpdates: List? = null, updatesReceiver: UpdateReceiver ): Job = scope.launch { var lastUpdateIdentifier: UpdateIdentifier? = null while (isActive) { handleSafely( { e -> exceptionsHandler ?.invoke(e) if (e is RequestException) { delay(1000L) } } ) { val updates = execute( GetUpdates( offset = lastUpdateIdentifier?.plus(1), timeout = timeoutSeconds, allowed_updates = allowedUpdates ) ).let { originalUpdates -> val converted = originalUpdates.convertWithMediaGroupUpdates() /** * Dirty hack for cases when the media group was retrieved not fully: * * We are throw out the last media group and will reretrieve it again in the next get updates * and it will guarantee that it is full */ if (originalUpdates.size == getUpdatesLimit.last && converted.last() is SentMediaGroupUpdate) { converted - converted.last() } else { converted } } handleSafely { for (update in updates) { updatesReceiver(update) lastUpdateIdentifier = update.lastUpdateIdentifier() } } } } } /** * This method will create a new one [FlowsUpdatesFilter]. This method could be unsafe due to the fact that it will start * getting updates IMMEDIATELY. That means that your bot will be able to skip some of them until you will call * [kotlinx.coroutines.flow.Flow.collect] on one of [FlowsUpdatesFilter] flows. To avoid it, you can pass * [flowUpdatesPreset] lambda - it will be called BEFORE starting updates getting */ @FlowPreview @PreviewFeature @Suppress("unused") fun RequestsExecutor.startGettingFlowsUpdatesByLongPolling( timeoutSeconds: Seconds = 30, scope: CoroutineScope = CoroutineScope(Dispatchers.Default), exceptionsHandler: (suspend (Exception) -> Unit)? = null, flowsUpdatesFilterUpdatesKeeperCount: Int = 64, flowUpdatesPreset: FlowsUpdatesFilter.() -> Unit = {} ): FlowsUpdatesFilter = FlowsUpdatesFilter(flowsUpdatesFilterUpdatesKeeperCount).apply { flowUpdatesPreset() startGettingOfUpdatesByLongPolling(timeoutSeconds, scope, exceptionsHandler, allowedUpdates, asUpdateReceiver) } fun RequestsExecutor.startGettingOfUpdatesByLongPolling( updatesFilter: UpdatesFilter, timeoutSeconds: Seconds = 30, exceptionsHandler: (suspend (Exception) -> Unit)? = null, scope: CoroutineScope = CoroutineScope(Dispatchers.Default) ): Job = startGettingOfUpdatesByLongPolling( timeoutSeconds, scope, exceptionsHandler, updatesFilter.allowedUpdates, updatesFilter.asUpdateReceiver ) fun RequestsExecutor.startGettingOfUpdatesByLongPolling( messageCallback: UpdateReceiver? = null, messageMediaGroupCallback: UpdateReceiver? = null, editedMessageCallback: UpdateReceiver? = null, editedMessageMediaGroupCallback: UpdateReceiver? = null, channelPostCallback: UpdateReceiver? = null, channelPostMediaGroupCallback: UpdateReceiver? = null, editedChannelPostCallback: UpdateReceiver? = null, editedChannelPostMediaGroupCallback: UpdateReceiver? = null, chosenInlineResultCallback: UpdateReceiver? = null, inlineQueryCallback: UpdateReceiver? = null, callbackQueryCallback: UpdateReceiver? = null, shippingQueryCallback: UpdateReceiver? = null, preCheckoutQueryCallback: UpdateReceiver? = null, pollCallback: UpdateReceiver? = null, pollAnswerCallback: UpdateReceiver? = null, timeoutSeconds: Seconds = 30, exceptionsHandler: (suspend (Exception) -> Unit)? = null, scope: CoroutineScope = GlobalScope ): Job { return startGettingOfUpdatesByLongPolling( SimpleUpdatesFilter( messageCallback, messageMediaGroupCallback, editedMessageCallback, editedMessageMediaGroupCallback, channelPostCallback, channelPostMediaGroupCallback, editedChannelPostCallback, editedChannelPostMediaGroupCallback, chosenInlineResultCallback, inlineQueryCallback, callbackQueryCallback, shippingQueryCallback, preCheckoutQueryCallback, pollCallback, pollAnswerCallback ), timeoutSeconds, exceptionsHandler, scope ) } @Suppress("unused") fun RequestsExecutor.startGettingOfUpdatesByLongPolling( messageCallback: UpdateReceiver? = null, mediaGroupCallback: UpdateReceiver? = null, editedMessageCallback: UpdateReceiver? = null, channelPostCallback: UpdateReceiver? = null, editedChannelPostCallback: UpdateReceiver? = null, chosenInlineResultCallback: UpdateReceiver? = null, inlineQueryCallback: UpdateReceiver? = null, callbackQueryCallback: UpdateReceiver? = null, shippingQueryCallback: UpdateReceiver? = null, preCheckoutQueryCallback: UpdateReceiver? = null, pollCallback: UpdateReceiver? = null, pollAnswerCallback: UpdateReceiver? = null, timeoutSeconds: Seconds = 30, exceptionsHandler: (suspend (Exception) -> Unit)? = null, scope: CoroutineScope = CoroutineScope(Dispatchers.Default) ): Job = startGettingOfUpdatesByLongPolling( messageCallback = messageCallback, messageMediaGroupCallback = mediaGroupCallback, editedMessageCallback = editedMessageCallback, editedMessageMediaGroupCallback = mediaGroupCallback, channelPostCallback = channelPostCallback, channelPostMediaGroupCallback = mediaGroupCallback, editedChannelPostCallback = editedChannelPostCallback, editedChannelPostMediaGroupCallback = mediaGroupCallback, chosenInlineResultCallback = chosenInlineResultCallback, inlineQueryCallback = inlineQueryCallback, callbackQueryCallback = callbackQueryCallback, shippingQueryCallback = shippingQueryCallback, preCheckoutQueryCallback = preCheckoutQueryCallback, pollCallback = pollCallback, pollAnswerCallback = pollAnswerCallback, timeoutSeconds = timeoutSeconds, exceptionsHandler = exceptionsHandler, scope = scope )