tgbotapi/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/updates/retrieving/LongPolling.kt

228 lines
8.8 KiB
Kotlin
Raw Normal View History

package dev.inmo.tgbotapi.extensions.utils.updates.retrieving
2020-05-13 17:22:35 +00:00
2021-02-08 13:35:32 +00:00
import dev.inmo.micro_utils.coroutines.*
2020-10-04 11:06:30 +00:00
import dev.inmo.tgbotapi.bot.RequestsExecutor
2021-01-09 16:10:38 +00:00
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.bot.exceptions.*
2020-10-04 11:06:30 +00:00
import dev.inmo.tgbotapi.extensions.utils.updates.convertWithMediaGroupUpdates
import dev.inmo.tgbotapi.extensions.utils.updates.lastUpdateIdentifier
import dev.inmo.tgbotapi.requests.GetUpdates
import dev.inmo.tgbotapi.requests.webhook.DeleteWebhook
2020-10-04 11:06:30 +00:00
import dev.inmo.tgbotapi.types.*
2022-11-07 20:27:38 +00:00
import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
import dev.inmo.tgbotapi.types.message.content.MediaGroupContent
2020-10-04 11:06:30 +00:00
import dev.inmo.tgbotapi.types.update.*
2022-11-07 20:27:38 +00:00
import dev.inmo.tgbotapi.types.update.abstracts.BaseSentMessageUpdate
2020-10-04 11:06:30 +00:00
import dev.inmo.tgbotapi.types.update.abstracts.Update
import dev.inmo.tgbotapi.updateshandlers.*
import io.ktor.client.plugins.HttpRequestTimeoutException
2022-04-04 07:16:18 +00:00
import io.ktor.utils.io.CancellationException
2020-05-13 17:22:35 +00:00
import kotlinx.coroutines.*
2021-02-08 13:35:32 +00:00
import kotlinx.coroutines.flow.*
2020-05-13 17:22:35 +00:00
2021-06-27 19:00:20 +00:00
fun TelegramBot.longPollingFlow(
2020-05-13 17:22:35 +00:00
timeoutSeconds: Seconds = 30,
2020-05-14 07:11:46 +00:00
exceptionsHandler: (ExceptionHandler<Unit>)? = null,
allowedUpdates: List<String>? = ALL_UPDATES_LIST,
autoDisableWebhooks: Boolean = true
2021-06-27 19:00:20 +00:00
): Flow<Update> = channelFlow {
if (autoDisableWebhooks) {
runCatchingSafely {
execute(DeleteWebhook())
}
}
2020-05-13 17:22:35 +00:00
var lastUpdateIdentifier: UpdateIdentifier? = null
while (isActive) {
safely(
2020-05-13 17:22:35 +00:00
{ e ->
exceptionsHandler ?.invoke(e)
if (e is RequestException) {
delay(1000L)
}
2021-06-30 07:57:20 +00:00
if (e is GetUpdatesConflict && (exceptionsHandler == null || exceptionsHandler == defaultSafelyExceptionHandler)) {
println("Warning!!! Other bot with the same bot token requests updates with getUpdate in parallel")
}
2020-05-13 17:22:35 +00:00
}
) {
val updates = execute(
GetUpdates(
offset = lastUpdateIdentifier?.plus(1),
timeout = timeoutSeconds,
allowed_updates = allowedUpdates
)
).let { originalUpdates ->
val converted = originalUpdates.convertWithMediaGroupUpdates()
2021-06-27 19:00:20 +00:00
/**
* 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
*/
2020-05-13 17:22:35 +00:00
/**
* 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
*/
2022-11-07 20:27:38 +00:00
if (
originalUpdates.size == getUpdatesLimit.last
2022-11-07 20:50:22 +00:00
&& ((converted.last() as? BaseSentMessageUpdate) ?.data as? CommonMessage<*>) ?.content is MediaGroupContent<*>
2022-11-07 20:27:38 +00:00
) {
2020-05-13 17:22:35 +00:00
converted - converted.last()
} else {
converted
}
}
2021-06-27 19:00:20 +00:00
safelyWithResult {
2020-05-13 17:22:35 +00:00
for (update in updates) {
2021-06-27 19:00:20 +00:00
send(update)
2020-05-13 17:22:35 +00:00
2022-11-07 20:27:38 +00:00
lastUpdateIdentifier = update.updateId
2020-05-13 17:22:35 +00:00
}
2021-06-27 19:00:20 +00:00
}.onFailure {
cancel(it as? CancellationException ?: return@onFailure)
2020-05-13 17:22:35 +00:00
}
}
}
}
2021-06-27 19:00:20 +00:00
fun TelegramBot.startGettingOfUpdatesByLongPolling(
timeoutSeconds: Seconds = 30,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
exceptionsHandler: (ExceptionHandler<Unit>)? = null,
allowedUpdates: List<String>? = ALL_UPDATES_LIST,
autoDisableWebhooks: Boolean = true,
2021-06-27 19:00:20 +00:00
updatesReceiver: UpdateReceiver<Update>
): Job = longPollingFlow(timeoutSeconds, exceptionsHandler, allowedUpdates, autoDisableWebhooks).subscribeSafely(
2021-06-27 19:00:20 +00:00
scope,
exceptionsHandler ?: defaultSafelyExceptionHandler,
updatesReceiver
)
2021-02-08 13:35:32 +00:00
/**
* @return [kotlinx.coroutines.flow.Flow] which will emit updates to the collector while they will be accumulated. Works
2022-04-04 07:16:18 +00:00
* the same as [longPollingFlow], but it will cancel the flow after the first one [HttpRequestTimeoutException]
2021-02-08 13:35:32 +00:00
*/
fun TelegramBot.createAccumulatedUpdatesRetrieverFlow(
avoidInlineQueries: Boolean = false,
avoidCallbackQueries: Boolean = false,
exceptionsHandler: ExceptionHandler<Unit>? = null,
allowedUpdates: List<String>? = ALL_UPDATES_LIST,
autoDisableWebhooks: Boolean = true,
2022-04-04 07:16:18 +00:00
): Flow<Update> = longPollingFlow(
timeoutSeconds = 0,
exceptionsHandler = {
when {
it is HttpRequestTimeoutException ||
(it is CommonBotException && it.cause is HttpRequestTimeoutException) -> throw CancellationException("Cancel due to absence of new updates")
else -> exceptionsHandler ?.invoke(it)
2022-04-04 07:16:18 +00:00
}
},
allowedUpdates = allowedUpdates,
autoDisableWebhooks = autoDisableWebhooks
2022-04-04 07:16:18 +00:00
).filter {
!(it is InlineQueryUpdate && avoidInlineQueries || it is CallbackQueryUpdate && avoidCallbackQueries)
}
fun TelegramBot.retrieveAccumulatedUpdates(
avoidInlineQueries: Boolean = false,
avoidCallbackQueries: Boolean = false,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
exceptionsHandler: (ExceptionHandler<Unit>)? = null,
allowedUpdates: List<String>? = ALL_UPDATES_LIST,
autoDisableWebhooks: Boolean = true,
2022-04-04 07:16:18 +00:00
updatesReceiver: UpdateReceiver<Update>
): Job = createAccumulatedUpdatesRetrieverFlow(
avoidInlineQueries,
avoidCallbackQueries,
exceptionsHandler,
allowedUpdates,
autoDisableWebhooks
2022-04-04 07:16:18 +00:00
).subscribeSafelyWithoutExceptions(
scope.LinkedSupervisorScope()
) {
updatesReceiver(it)
2021-02-08 13:35:32 +00:00
}
fun TelegramBot.retrieveAccumulatedUpdates(
flowsUpdatesFilter: FlowsUpdatesFilter,
avoidInlineQueries: Boolean = false,
avoidCallbackQueries: Boolean = false,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
autoDisableWebhooks: Boolean = true,
2021-02-08 13:35:32 +00:00
exceptionsHandler: ExceptionHandler<Unit>? = null
2022-04-04 07:16:18 +00:00
) = retrieveAccumulatedUpdates(
avoidInlineQueries,
avoidCallbackQueries,
scope,
exceptionsHandler,
flowsUpdatesFilter.allowedUpdates,
autoDisableWebhooks,
2022-04-04 07:16:18 +00:00
flowsUpdatesFilter.asUpdateReceiver
)
suspend fun TelegramBot.flushAccumulatedUpdates(
avoidInlineQueries: Boolean = false,
avoidCallbackQueries: Boolean = false,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
allowedUpdates: List<String>? = ALL_UPDATES_LIST,
2022-04-04 07:16:18 +00:00
exceptionsHandler: ExceptionHandler<Unit>? = null,
autoDisableWebhooks: Boolean = true,
2022-04-04 07:16:18 +00:00
updatesReceiver: UpdateReceiver<Update> = {}
) = retrieveAccumulatedUpdates(
avoidInlineQueries,
avoidCallbackQueries,
scope,
exceptionsHandler,
allowedUpdates,
autoDisableWebhooks,
2022-04-04 07:16:18 +00:00
updatesReceiver
).join()
2021-02-08 13:35:32 +00:00
2021-01-09 16:10:38 +00:00
/**
* Will [startGettingOfUpdatesByLongPolling] using incoming [flowsUpdatesFilter]. It is assumed that you ALREADY CONFIGURE
* all updates receivers, because this method will trigger getting of updates and.
*/
fun TelegramBot.longPolling(
2022-05-22 16:36:56 +00:00
updatesFilter: UpdatesFilter,
2021-01-09 16:10:38 +00:00
timeoutSeconds: Seconds = 30,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
autoDisableWebhooks: Boolean = true,
2021-01-09 16:10:38 +00:00
exceptionsHandler: ExceptionHandler<Unit>? = null
2022-05-22 16:36:56 +00:00
): Job = updatesFilter.run {
startGettingOfUpdatesByLongPolling(timeoutSeconds, scope, exceptionsHandler, allowedUpdates, autoDisableWebhooks, asUpdateReceiver)
2021-01-09 16:10:38 +00:00
}
/**
* 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.
*/
@Suppress("unused")
fun TelegramBot.longPolling(
timeoutSeconds: Seconds = 30,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
exceptionsHandler: ExceptionHandler<Unit>? = null,
flowsUpdatesFilterUpdatesKeeperCount: Int = 100,
autoDisableWebhooks: Boolean = true,
2021-01-09 16:10:38 +00:00
flowUpdatesPreset: FlowsUpdatesFilter.() -> Unit
): Job = longPolling(FlowsUpdatesFilter(flowsUpdatesFilterUpdatesKeeperCount).apply(flowUpdatesPreset), timeoutSeconds, scope, autoDisableWebhooks, exceptionsHandler)
2021-01-09 16:10:38 +00:00
2020-05-13 17:22:35 +00:00
fun RequestsExecutor.startGettingOfUpdatesByLongPolling(
updatesFilter: UpdatesFilter,
timeoutSeconds: Seconds = 30,
2020-05-14 18:28:16 +00:00
exceptionsHandler: ExceptionHandler<Unit>? = null,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
autoDisableWebhooks: Boolean = true,
2020-05-13 17:22:35 +00:00
): Job = startGettingOfUpdatesByLongPolling(
timeoutSeconds,
scope,
exceptionsHandler,
updatesFilter.allowedUpdates,
autoDisableWebhooks,
2020-05-13 17:22:35 +00:00
updatesFilter.asUpdateReceiver
)