diff --git a/CHANGELOG.md b/CHANGELOG.md index 57b2144a82..77d6445d2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # TelegramBotAPI changelog +## 0.33.3 + +* `Common`: + * `Version`: + * `MicroUtils`: `0.4.32` -> `0.4.33` + * `Ktor`: `1.5.2` -> `1.5.3` +* `API`: + * Bot actions DSL (fix for [#358](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/358)) +* `Behaviour Builder`: + * Rewrite logic of `doInSubContextWithUpdatesFilter` and `doInSubContextWithFlowsUpdatesFilterSetup` extensions + * All triggers now work with `stopOnCompletion` set up to `false` + ## 0.33.2 * `Common`: diff --git a/gradle.properties b/gradle.properties index ac96ac1f7e..dff1d0b1aa 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,13 +10,13 @@ kotlin_coroutines_version=1.4.3 kotlin_serialisation_runtime_version=1.1.0 klock_version=2.0.7 uuid_version=0.2.3 -ktor_version=1.5.2 +ktor_version=1.5.3 -micro_utils_version=0.4.32 +micro_utils_version=0.4.33 javax_activation_version=1.1.1 library_group=dev.inmo -library_version=0.33.2 +library_version=0.33.3 github_release_plugin_version=2.2.12 diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/Common.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/Common.kt index 8fc47f2a24..c1c935fb26 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/Common.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/Common.kt @@ -80,6 +80,8 @@ val openPeriodPollSecondsLimit = 5 .. 600 val membersLimit = 1 .. 99999 +const val botActionActualityTime: Seconds = 5 + // Made as lazy for correct work in K/JS val telegramInlineModeGifPermittedMimeTypes by lazy { listOf( diff --git a/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/send/SendActionDSL.kt b/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/send/SendActionDSL.kt new file mode 100644 index 0000000000..13e964debe --- /dev/null +++ b/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/send/SendActionDSL.kt @@ -0,0 +1,76 @@ +package dev.inmo.tgbotapi.extensions.api.send + +import dev.inmo.micro_utils.coroutines.safely +import dev.inmo.micro_utils.coroutines.safelyWithoutExceptions +import dev.inmo.tgbotapi.bot.TelegramBot +import dev.inmo.tgbotapi.requests.send.SendAction +import dev.inmo.tgbotapi.types.* +import dev.inmo.tgbotapi.types.actions.* +import dev.inmo.tgbotapi.types.chat.abstracts.Chat +import kotlinx.coroutines.* + +private const val refreshTime: MilliSeconds = (botActionActualityTime - 1) * 1000L +typealias TelegramBotActionCallback = suspend TelegramBot.() -> T + +suspend fun TelegramBot.withAction( + actionRequest: SendAction, + block: TelegramBotActionCallback +): T { + val botActionJob = supervisorScope { + launch { + while (isActive) { + delay(refreshTime) + safelyWithoutExceptions { + execute(actionRequest) + } + } + } + } + return try { + safely { block() } + } finally { + botActionJob.cancel() + } +} + +suspend fun TelegramBot.withAction( + chatId: ChatId, + action: BotAction, + block: TelegramBotActionCallback +) = withAction( + SendAction(chatId, action), + block +) + +suspend fun TelegramBot.withAction( + chat: Chat, + action: BotAction, + block: TelegramBotActionCallback +) = withAction( + chat.id, + action, + block +) + +suspend fun TelegramBot.withTypingAction(chatId: ChatId, block: TelegramBotActionCallback) = withAction(chatId, TypingAction, block) +suspend fun TelegramBot.withUploadPhotoAction(chatId: ChatId, block: TelegramBotActionCallback) = withAction(chatId, UploadPhotoAction, block) +suspend fun TelegramBot.withRecordVideoAction(chatId: ChatId, block: TelegramBotActionCallback) = withAction(chatId, RecordVideoAction, block) +suspend fun TelegramBot.withUploadVideoAction(chatId: ChatId, block: TelegramBotActionCallback) = withAction(chatId, UploadVideoAction, block) +suspend fun TelegramBot.withRecordAudioAction(chatId: ChatId, block: TelegramBotActionCallback) = withAction(chatId, RecordAudioAction, block) +suspend fun TelegramBot.withUploadAudioAction(chatId: ChatId, block: TelegramBotActionCallback) = withAction(chatId, UploadAudioAction, block) +suspend fun TelegramBot.withUploadDocumentAction(chatId: ChatId, block: TelegramBotActionCallback) = withAction(chatId, UploadDocumentAction, block) +suspend fun TelegramBot.withFindLocationAction(chatId: ChatId, block: TelegramBotActionCallback) = withAction(chatId, FindLocationAction, block) +suspend fun TelegramBot.withRecordVideoNoteAction(chatId: ChatId, block: TelegramBotActionCallback) = withAction(chatId, RecordVideoNoteAction, block) +suspend fun TelegramBot.withUploadVideoNoteAction(chatId: ChatId, block: TelegramBotActionCallback) = withAction(chatId, UploadVideoNoteAction, block) + + +suspend fun TelegramBot.withTypingAction(chat: Chat, block: TelegramBotActionCallback) = withAction(chat, TypingAction, block) +suspend fun TelegramBot.withUploadPhotoAction(chat: Chat, block: TelegramBotActionCallback) = withAction(chat, UploadPhotoAction, block) +suspend fun TelegramBot.withRecordVideoAction(chat: Chat, block: TelegramBotActionCallback) = withAction(chat, RecordVideoAction, block) +suspend fun TelegramBot.withUploadVideoAction(chat: Chat, block: TelegramBotActionCallback) = withAction(chat, UploadVideoAction, block) +suspend fun TelegramBot.withRecordAudioAction(chat: Chat, block: TelegramBotActionCallback) = withAction(chat, RecordAudioAction, block) +suspend fun TelegramBot.withUploadAudioAction(chat: Chat, block: TelegramBotActionCallback) = withAction(chat, UploadAudioAction, block) +suspend fun TelegramBot.withUploadDocumentAction(chat: Chat, block: TelegramBotActionCallback) = withAction(chat, UploadDocumentAction, block) +suspend fun TelegramBot.withFindLocationAction(chat: Chat, block: TelegramBotActionCallback) = withAction(chat, FindLocationAction, block) +suspend fun TelegramBot.withRecordVideoNoteAction(chat: Chat, block: TelegramBotActionCallback) = withAction(chat, RecordVideoNoteAction, block) +suspend fun TelegramBot.withUploadVideoNoteAction(chat: Chat, block: TelegramBotActionCallback) = withAction(chat, UploadVideoNoteAction, block) diff --git a/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext.kt b/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext.kt index 109ef09513..f4d91975fb 100644 --- a/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext.kt +++ b/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext.kt @@ -1,6 +1,7 @@ package dev.inmo.tgbotapi.extensions.behaviour_builder import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions +import dev.inmo.micro_utils.coroutines.weakLaunch import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.types.update.abstracts.Update import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter @@ -36,14 +37,15 @@ suspend fun BehaviourContext.doInSubContextWithFlowsUpdatesFilterSetup( newFlowsUpdatesFilterSetUp: BehaviourContextAndTypeReceiver?, stopOnCompletion: Boolean = true, behaviourContextReceiver: BehaviourContextReceiver -) = copy( - flowsUpdatesFilter = FlowsUpdatesFilter(), - scope = CoroutineScope(scope.coroutineContext + SupervisorJob()) -).run { +): T = supervisorScope { + val newContext = copy( + flowsUpdatesFilter = FlowsUpdatesFilter(), + scope = this + ) newFlowsUpdatesFilterSetUp ?.let { - it.apply { invoke(this@run, this@doInSubContextWithFlowsUpdatesFilterSetup.flowsUpdatesFilter) } + it.apply { invoke(newContext, this@doInSubContextWithFlowsUpdatesFilterSetup.flowsUpdatesFilter) } } - behaviourContextReceiver().also { if (stopOnCompletion) stop() } + newContext.behaviourContextReceiver().also { if (stopOnCompletion) stop() } } /** @@ -54,13 +56,17 @@ suspend fun BehaviourContext.doInSubContextWithUpdatesFilter( updatesFilter: BehaviourContextAndTypeReceiver?, stopOnCompletion: Boolean = true, behaviourContextReceiver: BehaviourContextReceiver -) = doInSubContextWithFlowsUpdatesFilterSetup( +): T = doInSubContextWithFlowsUpdatesFilterSetup( newFlowsUpdatesFilterSetUp = updatesFilter ?.let { { oldOne -> - oldOne.allUpdatesFlow.filter { updatesFilter(it) }.subscribeSafelyWithoutExceptions(this, asUpdateReceiver) + weakLaunch { + oldOne.allUpdatesFlow.filter { updatesFilter(it) }.subscribeSafelyWithoutExceptions(this, asUpdateReceiver) + } } } ?: { oldOne -> - oldOne.allUpdatesFlow.subscribeSafelyWithoutExceptions(this, asUpdateReceiver) + weakLaunch { + oldOne.allUpdatesFlow.subscribeSafelyWithoutExceptions(this, asUpdateReceiver) + } }, stopOnCompletion, behaviourContextReceiver diff --git a/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/CallbackQueryTriggers.kt b/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/CallbackQueryTriggers.kt index 8e0f9724f8..1c604ec76f 100644 --- a/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/CallbackQueryTriggers.kt +++ b/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/CallbackQueryTriggers.kt @@ -26,7 +26,8 @@ internal suspend inline fun BehaviourContext.onCallb { it.sourceChat() ?.id ?.chatId == triggerQuery.user.id.chatId } } else { null - } + }, + stopOnCompletion = false ) { scenarioReceiver(triggerQuery) } diff --git a/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/ChatMemberUpdatedTriggers.kt b/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/ChatMemberUpdatedTriggers.kt index 1156af2648..66676f7c6e 100644 --- a/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/ChatMemberUpdatedTriggers.kt +++ b/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/ChatMemberUpdatedTriggers.kt @@ -24,7 +24,8 @@ internal suspend inline fun BehaviourConte { it.sourceChat() ?.id ?.chatId == triggerChatMemberUpdated.chat.id.chatId } } else { null - } + }, + stopOnCompletion = false ) { scenarioReceiver(triggerChatMemberUpdated) } diff --git a/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/ContentTriggers.kt b/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/ContentTriggers.kt index 493b58f0ee..b649fe7f90 100644 --- a/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/ContentTriggers.kt +++ b/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/ContentTriggers.kt @@ -50,7 +50,8 @@ internal suspend inline fun BehaviourContext.onCont { it.sourceChat() ?.id ?.chatId == triggerMessage.chat.id.chatId } } else { null - } + }, + stopOnCompletion = false ) { scenarioReceiver(triggerMessage) } diff --git a/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/EventTriggers.kt b/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/EventTriggers.kt index 6940426c78..62144e443c 100644 --- a/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/EventTriggers.kt +++ b/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/EventTriggers.kt @@ -29,7 +29,8 @@ internal suspend inline fun BehaviourContext.onEvent( doInSubContextWithUpdatesFilter( updatesFilter = if (includeFilterByChatInBehaviourSubContext) { { it.sourceChat() ?.id ?.chatId == triggerMessage.chat.id.chatId } - } else null + } else null, + stopOnCompletion = false ) { scenarioReceiver(triggerMessage) } diff --git a/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/InlineQueryTriggers.kt b/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/InlineQueryTriggers.kt index 317943df52..bc55ed4afd 100644 --- a/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/InlineQueryTriggers.kt +++ b/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/InlineQueryTriggers.kt @@ -27,7 +27,8 @@ internal suspend inline fun BehaviourContext.onInlineQ { it.sourceChat() ?.id ?.chatId == triggerQuery.from.id.chatId } } else { null - } + }, + stopOnCompletion = false ) { scenarioReceiver(triggerQuery) } diff --git a/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/MediaGroupTriggers.kt b/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/MediaGroupTriggers.kt index 31a9b9d11c..b49865f902 100644 --- a/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/MediaGroupTriggers.kt +++ b/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/MediaGroupTriggers.kt @@ -32,7 +32,8 @@ internal suspend inline fun BehaviourContext.bui doInSubContextWithUpdatesFilter( updatesFilter = if (includeFilterByChatInBehaviourSubContext) { { it.sourceChat() ?.id ?.chatId == mediaGroupChat.id.chatId } - } else null + } else null, + stopOnCompletion = false ) { scenarioReceiver(mediaGroup) } diff --git a/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/PassportTriggers.kt b/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/PassportTriggers.kt index e954526b0e..92efd5f9cd 100644 --- a/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/PassportTriggers.kt +++ b/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/PassportTriggers.kt @@ -25,7 +25,8 @@ suspend inline fun BehaviourContext.onPas doInSubContextWithUpdatesFilter( updatesFilter = if (includeFilterByChatInBehaviourSubContext) { { it.sourceChat() ?.id ?.chatId == triggerMessage.chat.id.chatId } - } else null + } else null, + stopOnCompletion = false ) { scenarioReceiver(triggerMessage) }