From c70f0b65dd1d0f9e98822ca54084bc232ebb0636 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Thu, 7 Jan 2021 12:45:30 +0600 Subject: [PATCH] add content triggers and update command --- .../triggers_handling/CommandHandling.kt | 38 ++-- .../triggers_handling/ContentTriggers.kt | 176 ++++++++++++++++++ 2 files changed, 194 insertions(+), 20 deletions(-) create mode 100644 tgbotapi.extensions.steps/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/steps/triggers_handling/ContentTriggers.kt diff --git a/tgbotapi.extensions.steps/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/steps/triggers_handling/CommandHandling.kt b/tgbotapi.extensions.steps/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/steps/triggers_handling/CommandHandling.kt index ed12c1daa9..06c49f1b51 100644 --- a/tgbotapi.extensions.steps/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/steps/triggers_handling/CommandHandling.kt +++ b/tgbotapi.extensions.steps/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/steps/triggers_handling/CommandHandling.kt @@ -1,5 +1,6 @@ package dev.inmo.tgbotapi.extensions.steps.triggers_handling +import dev.inmo.micro_utils.coroutines.safelyWithoutExceptions import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions import dev.inmo.tgbotapi.CommonAbstracts.textSources import dev.inmo.tgbotapi.extensions.steps.* @@ -7,31 +8,28 @@ import dev.inmo.tgbotapi.extensions.steps.expectations.expectFlow import dev.inmo.tgbotapi.extensions.utils.* import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage import dev.inmo.tgbotapi.types.message.content.TextContent +import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.filter suspend fun Scenario.command( commandRegex: Regex, requireOnlyCommandInMessage: Boolean = true, + includeFilterByChatInSubScenario: Boolean = true, scenarioReceiver: ScenarioAndTypeReceiver> -) { - flowsUpdatesFilter.expectFlow(bot) { - it.asMessageUpdate() ?.data ?.asContentMessage() ?.let { message -> - message.content.asTextContent() ?.let { - val textSources = it.textSources - val sizeRequirement = if (requireOnlyCommandInMessage) { - textSources.size == 1 - } else { - true - } - if (sizeRequirement && textSources.any { commandRegex.matches(it.asBotCommandTextSource() ?.command ?: return@any false) }) { - message as ContentMessage - } else { - null - } - } +): Job = onText( + includeFilterByChatInSubScenario, + { message -> + val content = message.content + val textSources = content.textSources + val sizeRequirement = if (requireOnlyCommandInMessage) { + textSources.size == 1 + } else { + true } - }.subscribeSafelyWithoutExceptions(scope) { - scenarioReceiver(it) - } -} + sizeRequirement && textSources.any { commandRegex.matches(it.asBotCommandTextSource() ?.command ?: return@any false) } + }, + scenarioReceiver +) diff --git a/tgbotapi.extensions.steps/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/steps/triggers_handling/ContentTriggers.kt b/tgbotapi.extensions.steps/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/steps/triggers_handling/ContentTriggers.kt new file mode 100644 index 0000000000..a789c28686 --- /dev/null +++ b/tgbotapi.extensions.steps/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/steps/triggers_handling/ContentTriggers.kt @@ -0,0 +1,176 @@ +package dev.inmo.tgbotapi.extensions.steps.triggers_handling + + +import dev.inmo.micro_utils.coroutines.safelyWithoutExceptions +import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions +import dev.inmo.tgbotapi.extensions.steps.Scenario +import dev.inmo.tgbotapi.extensions.steps.ScenarioAndTypeReceiver +import dev.inmo.tgbotapi.extensions.steps.expectations.expectFlow +import dev.inmo.tgbotapi.extensions.utils.* +import dev.inmo.tgbotapi.types.files.abstracts.TelegramMediaFile +import dev.inmo.tgbotapi.types.message.content.TextContent +import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage +import dev.inmo.tgbotapi.types.message.content.ContactContent +import dev.inmo.tgbotapi.types.message.content.abstracts.MessageContent +import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter +import kotlinx.coroutines.flow.filter +import dev.inmo.tgbotapi.types.message.content.DiceContent +import dev.inmo.tgbotapi.types.message.content.GameContent +import dev.inmo.tgbotapi.types.message.content.LocationContent +import dev.inmo.tgbotapi.types.message.content.PollContent +import dev.inmo.tgbotapi.types.message.content.VenueContent +import dev.inmo.tgbotapi.types.message.content.abstracts.AudioMediaGroupContent +import dev.inmo.tgbotapi.types.message.content.abstracts.DocumentMediaGroupContent +import dev.inmo.tgbotapi.types.message.content.abstracts.MediaCollectionContent +import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent +import dev.inmo.tgbotapi.types.message.content.abstracts.MediaGroupContent +import dev.inmo.tgbotapi.types.message.content.abstracts.VisualMediaGroupContent +import dev.inmo.tgbotapi.types.message.content.media.AnimationContent +import dev.inmo.tgbotapi.types.message.content.media.AudioContent +import dev.inmo.tgbotapi.types.message.content.media.DocumentContent +import dev.inmo.tgbotapi.types.message.content.media.PhotoContent +import dev.inmo.tgbotapi.types.message.content.media.StickerContent +import dev.inmo.tgbotapi.types.message.content.media.VideoContent +import dev.inmo.tgbotapi.types.message.content.media.VideoNoteContent +import dev.inmo.tgbotapi.types.message.content.media.VoiceContent +import dev.inmo.tgbotapi.types.message.payments.InvoiceContent + + +internal suspend inline fun Scenario.onContent( + includeFilterByChatInSubScenario: Boolean = true, + noinline additionalFilter: (suspend (ContentMessage) -> Boolean)? = null, + noinline scenarioReceiver: ScenarioAndTypeReceiver> +) = flowsUpdatesFilter.expectFlow(bot) { + it.asMessageUpdate() ?.data ?.asContentMessage() ?.let { message -> + if (message.content is T) { + val adaptedMessage = message as ContentMessage + if (additionalFilter == null || additionalFilter(adaptedMessage)) adaptedMessage else null + } else { + null + } + } +}.subscribeSafelyWithoutExceptions(scope) { triggerMessage -> + val (jobToCancel, scenario) = if (includeFilterByChatInSubScenario) { + val subFilter = FlowsUpdatesFilter() + val subScenario = copy(flowsUpdatesFilter = subFilter) + + flowsUpdatesFilter.allUpdatesFlow.filter { + it.asMessageUpdate() ?.data ?.let { it.chat.id.chatId == triggerMessage.chat.id.chatId } == true + }.subscribeSafelyWithoutExceptions(scope, subFilter.asUpdateReceiver) to subScenario + } else { + null to this + } + safelyWithoutExceptions { scenario.scenarioReceiver(triggerMessage) } + jobToCancel ?.cancel() +} + +suspend fun Scenario.onContact( + includeFilterByChatInSubScenario: Boolean = true, + additionalFilter: (suspend (ContentMessage) -> Boolean)? = null, + scenarioReceiver: ScenarioAndTypeReceiver> +) = onContent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver) +suspend fun Scenario.onDice( + includeFilterByChatInSubScenario: Boolean = true, + additionalFilter: (suspend (ContentMessage) -> Boolean)? = null, + scenarioReceiver: ScenarioAndTypeReceiver> +) = onContent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver) +suspend fun Scenario.onGame( + includeFilterByChatInSubScenario: Boolean = true, + additionalFilter: (suspend (ContentMessage) -> Boolean)? = null, + scenarioReceiver: ScenarioAndTypeReceiver> +) = onContent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver) +suspend fun Scenario.onLocation( + includeFilterByChatInSubScenario: Boolean = true, + additionalFilter: (suspend (ContentMessage) -> Boolean)? = null, + scenarioReceiver: ScenarioAndTypeReceiver> +) = onContent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver) +suspend fun Scenario.onPoll( + includeFilterByChatInSubScenario: Boolean = true, + additionalFilter: (suspend (ContentMessage) -> Boolean)? = null, + scenarioReceiver: ScenarioAndTypeReceiver> +) = onContent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver) +suspend fun Scenario.onText( + includeFilterByChatInSubScenario: Boolean = true, + additionalFilter: (suspend (ContentMessage) -> Boolean)? = null, + scenarioReceiver: ScenarioAndTypeReceiver> +) = onContent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver) +suspend fun Scenario.onVenue( + includeFilterByChatInSubScenario: Boolean = true, + additionalFilter: (suspend (ContentMessage) -> Boolean)? = null, + scenarioReceiver: ScenarioAndTypeReceiver> +) = onContent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver) +suspend fun Scenario.onAudioMediaGroup( + includeFilterByChatInSubScenario: Boolean = true, + additionalFilter: (suspend (ContentMessage) -> Boolean)? = null, + scenarioReceiver: ScenarioAndTypeReceiver> +) = onContent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver) +suspend fun Scenario.onDocumentMediaGroup( + includeFilterByChatInSubScenario: Boolean = true, + additionalFilter: (suspend (ContentMessage) -> Boolean)? = null, + scenarioReceiver: ScenarioAndTypeReceiver> +) = onContent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver) +suspend fun Scenario.onMediaCollection( + includeFilterByChatInSubScenario: Boolean = true, + additionalFilter: (suspend (ContentMessage>) -> Boolean)? = null, + scenarioReceiver: ScenarioAndTypeReceiver>> +) = onContent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver) +suspend fun Scenario.onMedia( + includeFilterByChatInSubScenario: Boolean = true, + additionalFilter: (suspend (ContentMessage) -> Boolean)? = null, + scenarioReceiver: ScenarioAndTypeReceiver> +) = onContent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver) +suspend fun Scenario.onMediaGroup( + includeFilterByChatInSubScenario: Boolean = true, + additionalFilter: (suspend (ContentMessage) -> Boolean)? = null, + scenarioReceiver: ScenarioAndTypeReceiver> +) = onContent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver) +suspend fun Scenario.onVisualMediaGroup( + includeFilterByChatInSubScenario: Boolean = true, + additionalFilter: (suspend (ContentMessage) -> Boolean)? = null, + scenarioReceiver: ScenarioAndTypeReceiver> +) = onContent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver) +suspend fun Scenario.onAnimation( + includeFilterByChatInSubScenario: Boolean = true, + additionalFilter: (suspend (ContentMessage) -> Boolean)? = null, + scenarioReceiver: ScenarioAndTypeReceiver> +) = onContent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver) +suspend fun Scenario.onAudio( + includeFilterByChatInSubScenario: Boolean = true, + additionalFilter: (suspend (ContentMessage) -> Boolean)? = null, + scenarioReceiver: ScenarioAndTypeReceiver> +) = onContent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver) +suspend fun Scenario.onDocument( + includeFilterByChatInSubScenario: Boolean = true, + additionalFilter: (suspend (ContentMessage) -> Boolean)? = null, + scenarioReceiver: ScenarioAndTypeReceiver> +) = onContent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver) +suspend fun Scenario.onPhoto( + includeFilterByChatInSubScenario: Boolean = true, + additionalFilter: (suspend (ContentMessage) -> Boolean)? = null, + scenarioReceiver: ScenarioAndTypeReceiver> +) = onContent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver) +suspend fun Scenario.onSticker( + includeFilterByChatInSubScenario: Boolean = true, + additionalFilter: (suspend (ContentMessage) -> Boolean)? = null, + scenarioReceiver: ScenarioAndTypeReceiver> +) = onContent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver) +suspend fun Scenario.onVideo( + includeFilterByChatInSubScenario: Boolean = true, + additionalFilter: (suspend (ContentMessage) -> Boolean)? = null, + scenarioReceiver: ScenarioAndTypeReceiver> +) = onContent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver) +suspend fun Scenario.onVideoNote( + includeFilterByChatInSubScenario: Boolean = true, + additionalFilter: (suspend (ContentMessage) -> Boolean)? = null, + scenarioReceiver: ScenarioAndTypeReceiver> +) = onContent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver) +suspend fun Scenario.onVoice( + includeFilterByChatInSubScenario: Boolean = true, + additionalFilter: (suspend (ContentMessage) -> Boolean)? = null, + scenarioReceiver: ScenarioAndTypeReceiver> +) = onContent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver) +suspend fun Scenario.onInvoice( + includeFilterByChatInSubScenario: Boolean = true, + additionalFilter: (suspend (ContentMessage) -> Boolean)? = null, + scenarioReceiver: ScenarioAndTypeReceiver> +) = onContent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)