This commit is contained in:
InsanusMokrassar 2023-02-06 13:28:38 +06:00
parent f81d28dd5f
commit e3acdf1802
6 changed files with 136 additions and 55 deletions

View File

@ -8,6 +8,10 @@
* `ChatPermissions` now is interface and have two main realizations: `ChatPermissions.Granular` and
`ChatPermissions.Common`
* `RestrictedChatMember` now implements `ChatPermissions` too
* `API`:
* Now it is possible to pass all long polling parameters in all places used it
* `Issues`:
* Fix of [#697](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/697)
## 5.0.2

View File

@ -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.types.Seconds
import dev.inmo.tgbotapi.types.update.abstracts.Update
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import kotlinx.coroutines.*
@ -54,6 +55,9 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
timeoutSeconds: Seconds = 30,
autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true,
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): Pair<DefaultBehaviourContextWithFSM<T>, Job> = buildBehaviourWithFSM(
upstreamUpdatesFlow,
@ -66,7 +70,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
).run {
this to scope.launch {
start()
longPolling(flowsUpdatesFilter, scope = scope)
longPolling(flowsUpdatesFilter, timeoutSeconds, scope, autoDisableWebhooks, autoSkipTimeoutExceptions, defaultExceptionsHandler)
}
}
@ -124,6 +128,9 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
timeoutSeconds: Seconds = 30,
autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true,
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
) = FlowsUpdatesFilter().let {
buildBehaviourWithFSM(
@ -138,7 +145,11 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
start()
longPolling(
flowsUpdatesFilter,
scope = scope
timeoutSeconds,
scope,
autoDisableWebhooks,
autoSkipTimeoutExceptions,
defaultExceptionsHandler
)
}
}

View File

@ -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.types.Seconds
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import dev.inmo.tgbotapi.utils.telegramBotAPIDefaultUrl
import kotlinx.coroutines.CoroutineScope
@ -42,6 +43,9 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSM(
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
testServer: Boolean = false,
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
timeoutSeconds: Seconds = 30,
autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true,
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): TelegramBot = telegramBot(
token,
@ -56,6 +60,9 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSM(
statesManager,
presetHandlers,
onStateHandlingErrorHandler,
timeoutSeconds,
autoDisableWebhooks,
autoSkipTimeoutExceptions,
block
)
}
@ -81,6 +88,9 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSMAndStartLongPolling(
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
testServer: Boolean = false,
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
timeoutSeconds: Seconds = 30,
autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true,
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): Pair<TelegramBot, Job> {
return telegramBot(
@ -95,6 +105,9 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSMAndStartLongPolling(
statesManager,
presetHandlers,
onStateHandlingErrorHandler,
timeoutSeconds,
autoDisableWebhooks,
autoSkipTimeoutExceptions,
block
)
}

View File

@ -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.types.Seconds
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import kotlinx.coroutines.*
@ -53,6 +54,9 @@ suspend fun TelegramBot.buildBehaviour(
suspend fun TelegramBot.buildBehaviourWithLongPolling(
scope: CoroutineScope = defaultCoroutineScopeProvider(),
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
timeoutSeconds: Seconds = 30,
autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true,
block: BehaviourContextReceiver<Unit>
): Job {
val behaviourContext = buildBehaviour(
@ -62,6 +66,9 @@ suspend fun TelegramBot.buildBehaviourWithLongPolling(
)
return longPolling(
behaviourContext,
scope = behaviourContext
scope = behaviourContext,
timeoutSeconds = timeoutSeconds,
autoDisableWebhooks = autoDisableWebhooks,
autoSkipTimeoutExceptions = autoSkipTimeoutExceptions
)
}

View File

@ -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.types.Seconds
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import dev.inmo.tgbotapi.utils.telegramBotAPIDefaultUrl
import kotlinx.coroutines.*
@ -66,6 +67,9 @@ suspend fun telegramBotWithBehaviourAndLongPolling(
builder: KtorRequestsExecutorBuilder.() -> Unit = {},
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
testServer: Boolean = false,
timeoutSeconds: Seconds = 30,
autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true,
block: BehaviourContextReceiver<Unit>
): Pair<TelegramBot, Job> {
return telegramBot(
@ -77,6 +81,9 @@ suspend fun telegramBotWithBehaviourAndLongPolling(
it to it.buildBehaviourWithLongPolling(
scope ?: CoroutineScope(coroutineContext),
defaultExceptionsHandler,
timeoutSeconds,
autoDisableWebhooks,
autoSkipTimeoutExceptions,
block
)
}

View File

@ -5,7 +5,6 @@ import dev.inmo.tgbotapi.bot.RequestsExecutor
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.bot.exceptions.*
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
import dev.inmo.tgbotapi.types.*
@ -24,7 +23,8 @@ fun TelegramBot.longPollingFlow(
timeoutSeconds: Seconds = 30,
exceptionsHandler: (ExceptionHandler<Unit>)? = null,
allowedUpdates: List<String>? = ALL_UPDATES_LIST,
autoDisableWebhooks: Boolean = true
autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true
): Flow<Update> = channelFlow {
if (autoDisableWebhooks) {
runCatchingSafely {
@ -32,58 +32,77 @@ fun TelegramBot.longPollingFlow(
}
}
val contextSafelyExceptionHandler = coroutineContext[ContextSafelyExceptionHandlerKey]
val contextToWork = if (contextSafelyExceptionHandler == null || !autoSkipTimeoutExceptions) {
coroutineContext
} else {
coroutineContext + ContextSafelyExceptionHandler { e ->
if (e is HttpRequestTimeoutException || (e is CommonBotException && e.cause is HttpRequestTimeoutException)) {
return@ContextSafelyExceptionHandler
} else {
contextSafelyExceptionHandler.handler(e)
}
}
}
var lastUpdateIdentifier: UpdateIdentifier? = null
while (isActive) {
safely(
{ e ->
exceptionsHandler ?.invoke(e)
if (e is RequestException) {
delay(1000L)
withContext(contextToWork) {
while (isActive) {
safely(
{ e ->
val isHttpRequestTimeoutException = e is HttpRequestTimeoutException || (e is CommonBotException && e.cause is HttpRequestTimeoutException)
if (isHttpRequestTimeoutException && autoSkipTimeoutExceptions) {
return@safely
}
exceptionsHandler ?.invoke(e)
if (e is RequestException) {
delay(1000L)
}
if (e is GetUpdatesConflict && (exceptionsHandler == null || exceptionsHandler == defaultSafelyExceptionHandler)) {
println("Warning!!! Other bot with the same bot token requests updates with getUpdate in parallel")
}
}
if (e is GetUpdatesConflict && (exceptionsHandler == null || exceptionsHandler == defaultSafelyExceptionHandler)) {
println("Warning!!! Other bot with the same bot token requests updates with getUpdate in parallel")
) {
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
*/
/**
* 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() as? BaseSentMessageUpdate) ?.data as? CommonMessage<*>) ?.content is MediaGroupContent<*>
) {
converted - converted.last()
} else {
converted
}
}
}
) {
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
*/
/**
* 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() as? BaseSentMessageUpdate) ?.data as? CommonMessage<*>) ?.content is MediaGroupContent<*>
) {
converted - converted.last()
} else {
converted
}
}
safelyWithResult {
for (update in updates) {
send(update)
safelyWithResult {
for (update in updates) {
send(update)
lastUpdateIdentifier = update.updateId
lastUpdateIdentifier = update.updateId
}
}.onFailure {
cancel(it as? CancellationException ?: return@onFailure)
}
}.onFailure {
cancel(it as? CancellationException ?: return@onFailure)
}
}
}
@ -95,8 +114,15 @@ fun TelegramBot.startGettingOfUpdatesByLongPolling(
exceptionsHandler: (ExceptionHandler<Unit>)? = null,
allowedUpdates: List<String>? = ALL_UPDATES_LIST,
autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true,
updatesReceiver: UpdateReceiver<Update>
): Job = longPollingFlow(timeoutSeconds, exceptionsHandler, allowedUpdates, autoDisableWebhooks).subscribeSafely(
): Job = longPollingFlow(
timeoutSeconds = timeoutSeconds,
exceptionsHandler = exceptionsHandler,
allowedUpdates = allowedUpdates,
autoDisableWebhooks = autoDisableWebhooks,
autoSkipTimeoutExceptions = autoSkipTimeoutExceptions
).subscribeSafely(
scope,
exceptionsHandler ?: defaultSafelyExceptionHandler,
updatesReceiver
@ -111,7 +137,7 @@ fun TelegramBot.createAccumulatedUpdatesRetrieverFlow(
avoidCallbackQueries: Boolean = false,
exceptionsHandler: ExceptionHandler<Unit>? = null,
allowedUpdates: List<String>? = ALL_UPDATES_LIST,
autoDisableWebhooks: Boolean = true,
autoDisableWebhooks: Boolean = true
): Flow<Update> = longPollingFlow(
timeoutSeconds = 0,
exceptionsHandler = {
@ -122,7 +148,8 @@ fun TelegramBot.createAccumulatedUpdatesRetrieverFlow(
}
},
allowedUpdates = allowedUpdates,
autoDisableWebhooks = autoDisableWebhooks
autoDisableWebhooks = autoDisableWebhooks,
autoSkipTimeoutExceptions = false
).filter {
!(it is InlineQueryUpdate && avoidInlineQueries || it is CallbackQueryUpdate && avoidCallbackQueries)
}
@ -191,9 +218,18 @@ fun TelegramBot.longPolling(
timeoutSeconds: Seconds = 30,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true,
exceptionsHandler: ExceptionHandler<Unit>? = null
): Job = updatesFilter.run {
startGettingOfUpdatesByLongPolling(timeoutSeconds, scope, exceptionsHandler, allowedUpdates, autoDisableWebhooks, asUpdateReceiver)
startGettingOfUpdatesByLongPolling(
timeoutSeconds = timeoutSeconds,
scope = scope,
exceptionsHandler = exceptionsHandler,
allowedUpdates = allowedUpdates,
autoDisableWebhooks = autoDisableWebhooks,
autoSkipTimeoutExceptions = autoSkipTimeoutExceptions,
updatesReceiver = asUpdateReceiver
)
}
/**
@ -208,8 +244,9 @@ fun TelegramBot.longPolling(
exceptionsHandler: ExceptionHandler<Unit>? = null,
flowsUpdatesFilterUpdatesKeeperCount: Int = 100,
autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true,
flowUpdatesPreset: FlowsUpdatesFilter.() -> Unit
): Job = longPolling(FlowsUpdatesFilter(flowsUpdatesFilterUpdatesKeeperCount).apply(flowUpdatesPreset), timeoutSeconds, scope, autoDisableWebhooks, exceptionsHandler)
): Job = longPolling(FlowsUpdatesFilter(flowsUpdatesFilterUpdatesKeeperCount).apply(flowUpdatesPreset), timeoutSeconds, scope, autoDisableWebhooks, autoSkipTimeoutExceptions, exceptionsHandler)
fun RequestsExecutor.startGettingOfUpdatesByLongPolling(
updatesFilter: UpdatesFilter,
@ -217,11 +254,13 @@ fun RequestsExecutor.startGettingOfUpdatesByLongPolling(
exceptionsHandler: ExceptionHandler<Unit>? = null,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true,
): Job = startGettingOfUpdatesByLongPolling(
timeoutSeconds,
scope,
exceptionsHandler,
updatesFilter.allowedUpdates,
autoDisableWebhooks,
autoSkipTimeoutExceptions,
updatesFilter.asUpdateReceiver
)