1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2024-11-26 03:58:44 +00:00

add callback query expectations and triggers

This commit is contained in:
InsanusMokrassar 2021-01-07 17:57:08 +06:00
parent 965b8c3c50
commit 3125c2fc1b
5 changed files with 230 additions and 2 deletions

View File

@ -0,0 +1,107 @@
@file:Suppress("unused")
package dev.inmo.tgbotapi.extensions.steps.expectations
import dev.inmo.tgbotapi.extensions.steps.Scenario
import dev.inmo.tgbotapi.extensions.utils.*
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.types.CallbackQuery.*
import dev.inmo.tgbotapi.types.message.ChatEvents.*
import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.*
import dev.inmo.tgbotapi.types.message.abstracts.ChatEventMessage
import dev.inmo.tgbotapi.types.message.content.ContactContent
import dev.inmo.tgbotapi.types.update.CallbackQueryUpdate
import kotlinx.coroutines.flow.toList
typealias CallbackQueryMapper<T> = T.() -> T?
private suspend fun <O> Scenario.waitCallbackQueries(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
mapper: suspend CallbackQuery.() -> O?
): List<O> = expectFlow(
initRequest,
count,
errorFactory
) {
it.asCallbackQueryUpdate() ?.data ?.mapper()
}.toList().toList()
private suspend inline fun <reified T : CallbackQuery> Scenario.waitEvents(
count: Int = 1,
initRequest: Request<*>? = null,
noinline errorFactory: NullableRequestBuilder<*> = { null },
noinline filter: CallbackQueryMapper<T>? = null
) : List<T> = waitCallbackQueries<T>(
count,
initRequest,
errorFactory
) {
if (this is T) {
if (filter == null) {
this
} else {
filter(this)
}
} else {
null
}
}
suspend fun Scenario.waitDataCallbackQuery(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: CallbackQueryMapper<DataCallbackQuery>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun Scenario.waitGameShortNameCallbackQuery(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: CallbackQueryMapper<GameShortNameCallbackQuery>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun Scenario.waitInlineMessageIdCallbackQuery(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: CallbackQueryMapper<InlineMessageIdCallbackQuery>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun Scenario.waitInlineMessageIdDataCallbackQuery(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: CallbackQueryMapper<InlineMessageIdDataCallbackQuery>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun Scenario.waitInlineMessageIdGameShortNameCallbackQuery(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: CallbackQueryMapper<InlineMessageIdGameShortNameCallbackQuery>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun Scenario.waitMessageCallbackQuery(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: CallbackQueryMapper<MessageCallbackQuery>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun Scenario.waitMessageDataCallbackQuery(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: CallbackQueryMapper<MessageDataCallbackQuery>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun Scenario.waitMessageGameShortNameCallbackQuery(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: CallbackQueryMapper<MessageGameShortNameCallbackQuery>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun Scenario.waitUnknownCallbackQuery(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: CallbackQueryMapper<UnknownCallbackQueryType>? = null
) = waitEvents(count, initRequest, errorFactory, filter)

View File

@ -0,0 +1,91 @@
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.extensions.utils.extensions.sourceChat
import dev.inmo.tgbotapi.types.CallbackQuery.*
import dev.inmo.tgbotapi.types.message.ChatEvents.*
import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.*
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import kotlinx.coroutines.flow.filter
internal suspend inline fun <reified T : CallbackQuery> Scenario.onCallbackQuery(
includeFilterByChatInSubScenario: Boolean = true,
noinline additionalFilter: (suspend (T) -> Boolean)? = null,
noinline scenarioReceiver: ScenarioAndTypeReceiver<Unit, T>
) = flowsUpdatesFilter.expectFlow(bot) {
it.asCallbackQueryUpdate() ?.data ?.let { query ->
if (query is T) {
if (additionalFilter == null || additionalFilter(query)) query else null
} else {
null
}
}
}.subscribeSafelyWithoutExceptions(scope) { triggerQuery ->
val (jobToCancel, scenario) = if (includeFilterByChatInSubScenario) {
val subFilter = FlowsUpdatesFilter()
val subScenario = copy(flowsUpdatesFilter = subFilter)
flowsUpdatesFilter.allUpdatesFlow.filter {
val chat = it.sourceChat() ?: return@filter false
chat.id.chatId == triggerQuery.user.id.chatId
}.subscribeSafelyWithoutExceptions(scope, subFilter.asUpdateReceiver) to subScenario
} else {
null to this
}
safelyWithoutExceptions { scenario.scenarioReceiver(triggerQuery) }
jobToCancel ?.cancel()
}
suspend fun Scenario.onDataCallbackQuery(
includeFilterByChatInSubScenario: Boolean = true,
additionalFilter: (suspend (DataCallbackQuery) -> Boolean)? = null,
scenarioReceiver: ScenarioAndTypeReceiver<Unit, DataCallbackQuery>
) = onCallbackQuery(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)
suspend fun Scenario.onGameShortNameCallbackQuery(
includeFilterByChatInSubScenario: Boolean = true,
additionalFilter: (suspend (GameShortNameCallbackQuery) -> Boolean)? = null,
scenarioReceiver: ScenarioAndTypeReceiver<Unit, GameShortNameCallbackQuery>
) = onCallbackQuery(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)
suspend fun Scenario.onInlineMessageIdCallbackQuery(
includeFilterByChatInSubScenario: Boolean = true,
additionalFilter: (suspend (InlineMessageIdCallbackQuery) -> Boolean)? = null,
scenarioReceiver: ScenarioAndTypeReceiver<Unit, InlineMessageIdCallbackQuery>
) = onCallbackQuery(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)
suspend fun Scenario.onInlineMessageIdDataCallbackQuery(
includeFilterByChatInSubScenario: Boolean = true,
additionalFilter: (suspend (InlineMessageIdDataCallbackQuery) -> Boolean)? = null,
scenarioReceiver: ScenarioAndTypeReceiver<Unit, InlineMessageIdDataCallbackQuery>
) = onCallbackQuery(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)
suspend fun Scenario.onInlineMessageIdGameShortNameCallbackQuery(
includeFilterByChatInSubScenario: Boolean = true,
additionalFilter: (suspend (InlineMessageIdGameShortNameCallbackQuery) -> Boolean)? = null,
scenarioReceiver: ScenarioAndTypeReceiver<Unit, InlineMessageIdGameShortNameCallbackQuery>
) = onCallbackQuery(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)
suspend fun Scenario.onMessageCallbackQuery(
includeFilterByChatInSubScenario: Boolean = true,
additionalFilter: (suspend (MessageCallbackQuery) -> Boolean)? = null,
scenarioReceiver: ScenarioAndTypeReceiver<Unit, MessageCallbackQuery>
) = onCallbackQuery(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)
suspend fun Scenario.onMessageDataCallbackQuery(
includeFilterByChatInSubScenario: Boolean = true,
additionalFilter: (suspend (MessageDataCallbackQuery) -> Boolean)? = null,
scenarioReceiver: ScenarioAndTypeReceiver<Unit, MessageDataCallbackQuery>
) = onCallbackQuery(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)
suspend fun Scenario.onMessageGameShortNameCallbackQuery(
includeFilterByChatInSubScenario: Boolean = true,
additionalFilter: (suspend (MessageGameShortNameCallbackQuery) -> Boolean)? = null,
scenarioReceiver: ScenarioAndTypeReceiver<Unit, MessageGameShortNameCallbackQuery>
) = onCallbackQuery(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)
suspend fun Scenario.onUnknownCallbackQueryType(
includeFilterByChatInSubScenario: Boolean = true,
additionalFilter: (suspend (UnknownCallbackQueryType) -> Boolean)? = null,
scenarioReceiver: ScenarioAndTypeReceiver<Unit, UnknownCallbackQueryType>
) = onCallbackQuery(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)

View File

@ -8,6 +8,7 @@ import dev.inmo.tgbotapi.extensions.steps.ScenarioAndTypeReceiver
import dev.inmo.tgbotapi.extensions.steps.expectations.expectFlow
import dev.inmo.tgbotapi.extensions.utils.asContentMessage
import dev.inmo.tgbotapi.extensions.utils.asMessageUpdate
import dev.inmo.tgbotapi.extensions.utils.extensions.sourceChat
import dev.inmo.tgbotapi.types.files.abstracts.TelegramMediaFile
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.*
@ -37,7 +38,8 @@ internal suspend inline fun <reified T : MessageContent> Scenario.onContent(
val subScenario = copy(flowsUpdatesFilter = subFilter)
flowsUpdatesFilter.allUpdatesFlow.filter {
it.asMessageUpdate() ?.data ?.let { it.chat.id.chatId == triggerMessage.chat.id.chatId } == true
val chat = it.sourceChat() ?: return@filter false
chat.id.chatId == triggerMessage.chat.id.chatId
}.subscribeSafelyWithoutExceptions(scope, subFilter.asUpdateReceiver) to subScenario
} else {
null to this

View File

@ -7,6 +7,7 @@ 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.extensions.utils.extensions.sourceChat
import dev.inmo.tgbotapi.types.files.abstracts.TelegramMediaFile
import dev.inmo.tgbotapi.types.message.ChatEvents.*
import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.*
@ -38,7 +39,8 @@ internal suspend inline fun <reified T : ChatEvent> Scenario.onEvent(
val subScenario = copy(flowsUpdatesFilter = subFilter)
flowsUpdatesFilter.allUpdatesFlow.filter {
it.asMessageUpdate() ?.data ?.let { it.chat.id.chatId == triggerMessage.chat.id.chatId } == true
val chat = it.sourceChat() ?: return@filter false
chat.id.chatId == triggerMessage.chat.id.chatId
}.subscribeSafelyWithoutExceptions(scope, subFilter.asUpdateReceiver) to subScenario
} else {
null to this

View File

@ -0,0 +1,26 @@
package dev.inmo.tgbotapi.extensions.utils.extensions
import dev.inmo.tgbotapi.extensions.utils.shortcuts.chat
import dev.inmo.tgbotapi.types.chat.abstracts.Chat
import dev.inmo.tgbotapi.types.update.*
import dev.inmo.tgbotapi.types.update.MediaGroupUpdates.*
import dev.inmo.tgbotapi.types.update.abstracts.BaseMessageUpdate
import dev.inmo.tgbotapi.types.update.abstracts.Update
import dev.inmo.tgbotapi.utils.PreviewFeature
@PreviewFeature
fun Update.sourceChat(): Chat? = when (this) {
is MediaGroupUpdate -> when (this) {
is SentMediaGroupUpdate -> data.chat
is EditMediaGroupUpdate -> data.chat
else -> null
}
is BaseMessageUpdate -> data.chat
is InlineQueryUpdate -> data.from
is ChosenInlineResultUpdate -> data.user
is CallbackQueryUpdate -> data.user
is PreCheckoutQueryUpdate -> data.user
is PollAnswerUpdate -> data.user
is ShippingQueryUpdate -> data.user
else -> null
}