add chat event triggers and expectations in steps

This commit is contained in:
InsanusMokrassar 2021-01-07 17:24:58 +06:00
parent 94745ef373
commit 965b8c3c50
4 changed files with 274 additions and 1 deletions

View File

@ -1,3 +1,5 @@
@file:Suppress("unused")
package dev.inmo.tgbotapi.extensions.steps.expectations
import dev.inmo.tgbotapi.extensions.steps.Scenario
@ -30,13 +32,14 @@ private suspend inline fun <reified T : MessageContent> Scenario.waitContent(
count: Int = 1,
initRequest: Request<*>? = null,
noinline errorFactory: NullableRequestBuilder<*> = { null },
noinline filter: (suspend (ContentMessage<T>) -> T?)? = null
noinline filter: ContentMessageToContentMapper<T>? = null
) : List<T> = waitContentMessage<T>(
count,
initRequest,
errorFactory
) {
if (content is T) {
@Suppress("UNCHECKED_CAST")
val message = (this as ContentMessage<T>)
if (filter == null) {
message.content

View File

@ -0,0 +1,144 @@
@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.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 kotlinx.coroutines.flow.toList
typealias EventMessageToEventMapper<T> = suspend ChatEventMessage<T>.() -> T?
private suspend fun <O> Scenario.waitEventMessages(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
mapper: suspend ChatEventMessage<ChatEvent>.() -> O?
): List<O> = expectFlow(
initRequest,
count,
errorFactory
) {
it.asMessageUpdate() ?.data ?.asChatEventMessage() ?.mapper()
}.toList().toList()
private suspend inline fun <reified T : ChatEvent> Scenario.waitEvents(
count: Int = 1,
initRequest: Request<*>? = null,
noinline errorFactory: NullableRequestBuilder<*> = { null },
noinline filter: EventMessageToEventMapper<T>? = null
) : List<T> = waitEventMessages<T>(
count,
initRequest,
errorFactory
) {
if (chatEvent is T) {
@Suppress("UNCHECKED_CAST")
val message = (this as ChatEventMessage<T>)
if (filter == null) {
message.chatEvent
} else {
filter(message)
}
} else {
null
}
}
suspend fun Scenario.waitChannelEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<ChannelEvent>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun Scenario.waitChatEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<ChatEvent>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun Scenario.waitCommonEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<CommonEvent>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun Scenario.waitGroupEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<GroupEvent>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun Scenario.waitSupergroupEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<SupergroupEvent>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun Scenario.waitChannelChatCreatedEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<ChannelChatCreated>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun Scenario.waitDeleteChatPhotoEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<DeleteChatPhoto>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun Scenario.waitGroupChatCreatedEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<GroupChatCreated>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun Scenario.waitLeftChatMemberEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<LeftChatMember>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun Scenario.waitNewChatPhotoEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<NewChatPhoto>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun Scenario.waitNewChatMembersEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<NewChatMembers>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun Scenario.waitNewChatTitleEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<NewChatTitle>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun Scenario.waitPinnedMessageEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<PinnedMessage>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun Scenario.waitProximityAlertTriggeredEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<ProximityAlertTriggered>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun Scenario.waitSupergroupChatCreatedEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<SupergroupChatCreated>? = null
) = waitEvents(count, initRequest, errorFactory, filter)

View File

@ -0,0 +1,125 @@
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.ChatEvents.*
import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.*
import dev.inmo.tgbotapi.types.message.abstracts.ChatEventMessage
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.*
import dev.inmo.tgbotapi.types.message.content.abstracts.*
import dev.inmo.tgbotapi.types.message.content.media.*
import dev.inmo.tgbotapi.types.message.payments.InvoiceContent
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import kotlinx.coroutines.flow.filter
internal suspend inline fun <reified T : ChatEvent> Scenario.onEvent(
includeFilterByChatInSubScenario: Boolean = true,
noinline additionalFilter: (suspend (ChatEventMessage<T>) -> Boolean)? = null,
noinline scenarioReceiver: ScenarioAndTypeReceiver<Unit, ChatEventMessage<T>>
) = flowsUpdatesFilter.expectFlow(bot) {
it.asMessageUpdate() ?.data ?.asChatEventMessage() ?.let { message ->
if (message.chatEvent is T) {
val adaptedMessage = message as ChatEventMessage<T>
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.onChannelEvent(
includeFilterByChatInSubScenario: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<ChannelEvent>) -> Boolean)? = null,
scenarioReceiver: ScenarioAndTypeReceiver<Unit, ChatEventMessage<ChannelEvent>>
) = onEvent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)
suspend fun Scenario.onChatEvent(
includeFilterByChatInSubScenario: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<ChatEvent>) -> Boolean)? = null,
scenarioReceiver: ScenarioAndTypeReceiver<Unit, ChatEventMessage<ChatEvent>>
) = onEvent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)
suspend fun Scenario.onCommonEvent(
includeFilterByChatInSubScenario: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<CommonEvent>) -> Boolean)? = null,
scenarioReceiver: ScenarioAndTypeReceiver<Unit, ChatEventMessage<CommonEvent>>
) = onEvent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)
suspend fun Scenario.onGroupEvent(
includeFilterByChatInSubScenario: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<GroupEvent>) -> Boolean)? = null,
scenarioReceiver: ScenarioAndTypeReceiver<Unit, ChatEventMessage<GroupEvent>>
) = onEvent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)
suspend fun Scenario.onSupergroupEvent(
includeFilterByChatInSubScenario: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<SupergroupEvent>) -> Boolean)? = null,
scenarioReceiver: ScenarioAndTypeReceiver<Unit, ChatEventMessage<SupergroupEvent>>
) = onEvent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)
suspend fun Scenario.onChannelChatCreated(
includeFilterByChatInSubScenario: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<ChannelChatCreated>) -> Boolean)? = null,
scenarioReceiver: ScenarioAndTypeReceiver<Unit, ChatEventMessage<ChannelChatCreated>>
) = onEvent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)
suspend fun Scenario.onDeleteChatPhoto(
includeFilterByChatInSubScenario: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<DeleteChatPhoto>) -> Boolean)? = null,
scenarioReceiver: ScenarioAndTypeReceiver<Unit, ChatEventMessage<DeleteChatPhoto>>
) = onEvent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)
suspend fun Scenario.onGroupChatCreated(
includeFilterByChatInSubScenario: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<GroupChatCreated>) -> Boolean)? = null,
scenarioReceiver: ScenarioAndTypeReceiver<Unit, ChatEventMessage<GroupChatCreated>>
) = onEvent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)
suspend fun Scenario.onLeftChatMember(
includeFilterByChatInSubScenario: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<LeftChatMember>) -> Boolean)? = null,
scenarioReceiver: ScenarioAndTypeReceiver<Unit, ChatEventMessage<LeftChatMember>>
) = onEvent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)
suspend fun Scenario.onNewChatMembers(
includeFilterByChatInSubScenario: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<NewChatMembers>) -> Boolean)? = null,
scenarioReceiver: ScenarioAndTypeReceiver<Unit, ChatEventMessage<NewChatMembers>>
) = onEvent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)
suspend fun Scenario.onNewChatPhoto(
includeFilterByChatInSubScenario: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<NewChatPhoto>) -> Boolean)? = null,
scenarioReceiver: ScenarioAndTypeReceiver<Unit, ChatEventMessage<NewChatPhoto>>
) = onEvent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)
suspend fun Scenario.onNewChatTitle(
includeFilterByChatInSubScenario: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<NewChatTitle>) -> Boolean)? = null,
scenarioReceiver: ScenarioAndTypeReceiver<Unit, ChatEventMessage<NewChatTitle>>
) = onEvent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)
suspend fun Scenario.onPinnedMessage(
includeFilterByChatInSubScenario: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<PinnedMessage>) -> Boolean)? = null,
scenarioReceiver: ScenarioAndTypeReceiver<Unit, ChatEventMessage<PinnedMessage>>
) = onEvent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)
suspend fun Scenario.onProximityAlertTriggered(
includeFilterByChatInSubScenario: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<ProximityAlertTriggered>) -> Boolean)? = null,
scenarioReceiver: ScenarioAndTypeReceiver<Unit, ChatEventMessage<ProximityAlertTriggered>>
) = onEvent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)
suspend fun Scenario.onSupergroupChatCreated(
includeFilterByChatInSubScenario: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<SupergroupChatCreated>) -> Boolean)? = null,
scenarioReceiver: ScenarioAndTypeReceiver<Unit, ChatEventMessage<SupergroupChatCreated>>
) = onEvent(includeFilterByChatInSubScenario, additionalFilter, scenarioReceiver)

View File

@ -42,6 +42,7 @@ kotlin {
api project(":tgbotapi.core")
api project(":tgbotapi.extensions.api")
api project(":tgbotapi.extensions.utils")
api project(":tgbotapi.extensions.steps")
}
}
}