1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2025-09-03 07:09:23 +00:00

rename steps subproject to behaviour_builder

This commit is contained in:
2021-01-07 18:17:50 +06:00
parent b933361258
commit 8c2cffc8e3
16 changed files with 26 additions and 26 deletions

View File

@@ -0,0 +1 @@
# TelegramBotAPI Steps Extensions

View File

@@ -0,0 +1,48 @@
buildscript {
repositories {
mavenLocal()
jcenter()
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
}
}
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
}
project.version = "$library_version"
project.group = "$library_group"
apply from: "publish.gradle"
repositories {
mavenLocal()
jcenter()
mavenCentral()
maven { url "https://kotlin.bintray.com/kotlinx" }
}
kotlin {
jvm()
js(BOTH) {
browser()
nodejs()
}
sourceSets {
commonMain {
dependencies {
implementation kotlin('stdlib')
api project(":tgbotapi.core")
api project(":tgbotapi.extensions.utils")
api project(":tgbotapi.extensions.api")
}
}
}
}

View File

@@ -0,0 +1 @@
{"bintrayConfig":{"repo":"TelegramBotAPI","packageName":"${project.name}","packageVcs":"https://github.com/InsanusMokrassar/TelegramBotAPI","autoPublish":true,"overridePublish":true},"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/LICENSE"}],"mavenConfig":{"name":"Telegram Bot API Steps Extensions","description":"These extensions project contains tools for simple interaction with chats","url":"https://insanusmokrassar.github.io/TelegramBotAPI/tgbotapi.extensions.steps","vcsUrl":"https://github.com/insanusmokrassar/TelegramBotAPI.git","developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"}]}}

View File

@@ -0,0 +1,69 @@
apply plugin: 'maven-publish'
task javadocsJar(type: Jar) {
classifier = 'javadoc'
}
task sourceJar (type : Jar) {
classifier = 'sources'
}
afterEvaluate {
project.publishing.publications.all {
// rename artifacts
groupId "${project.group}"
if (it.name.contains('kotlinMultiplatform')) {
artifactId = "${project.name}"
artifact sourceJar
} else {
artifactId = "${project.name}-$name"
}
}
}
publishing {
publications.all {
artifact javadocsJar
pom {
description = "These extensions project contains tools for simple interaction with chats"
name = "Telegram Bot API Steps Extensions"
url = "https://insanusmokrassar.github.io/TelegramBotAPI/tgbotapi.extensions.steps"
scm {
developerConnection = "scm:git:[fetch=]https://github.com/insanusmokrassar/TelegramBotAPI.git[push=]https://github.com/insanusmokrassar/TelegramBotAPI.git"
url = "https://github.com/insanusmokrassar/TelegramBotAPI.git"
}
developers {
developer {
id = "InsanusMokrassar"
name = "Ovsiannikov Aleksei"
email = "ovsyannikov.alexey95@gmail.com"
}
}
licenses {
license {
name = "Apache Software License 2.0"
url = "https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/LICENSE"
}
}
}
repositories {
maven {
name = "bintray"
url = uri("https://api.bintray.com/maven/${project.hasProperty('BINTRAY_USER') ? project.property('BINTRAY_USER') : System.getenv('BINTRAY_USER')}/TelegramBotAPI/${project.name}/;publish=1;override=1")
credentials {
username = project.hasProperty('BINTRAY_USER') ? project.property('BINTRAY_USER') : System.getenv('BINTRAY_USER')
password = project.hasProperty('BINTRAY_KEY') ? project.property('BINTRAY_KEY') : System.getenv('BINTRAY_KEY')
}
}
}
}
}

View File

@@ -0,0 +1,33 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import kotlinx.coroutines.CoroutineScope
suspend fun TelegramBot.buildBehaviour(
scope: CoroutineScope,
flowUpdatesFilter: FlowsUpdatesFilter,
block: BehaviourContextReceiver<Unit>
) {
BehaviourContext(
this,
scope,
flowUpdatesFilter
).block()
}
suspend fun TelegramBot.buildBehaviour(
scope: CoroutineScope,
block: BehaviourContextReceiver<Unit>
) = FlowsUpdatesFilter().also {
buildBehaviour(
scope,
it,
block
)
startGettingOfUpdatesByLongPolling(
updatesFilter = it,
scope = scope
)
}

View File

@@ -0,0 +1,14 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import kotlinx.coroutines.CoroutineScope
typealias BehaviourContextReceiver<T> = suspend BehaviourContext.() -> T
typealias BehaviourContextAndTypeReceiver<T, I> = suspend BehaviourContext.(I) -> T
data class BehaviourContext(
val bot: TelegramBot,
val scope: CoroutineScope,
val flowsUpdatesFilter: FlowsUpdatesFilter = FlowsUpdatesFilter()
)

View File

@@ -0,0 +1,121 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder.expectations
import dev.inmo.micro_utils.coroutines.safelyWithoutExceptions
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.types.update.abstracts.Update
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import dev.inmo.tgbotapi.utils.RiskFeature
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.flow.*
private val cancelledByFilterException = CancellationException("Cancelled by filter precreatedException")
typealias RequestBuilder<T> = suspend (Update) -> Request<T>
typealias NullableRequestBuilder<T> = suspend (Update) -> Request<T>?
/**
* @param initRequest If not null, this request will be sent by [bot] before returning value
* @param count If set, result [Flow] will return [count] elements on each [Flow.collect]
* @param errorFactory If set, this factory will be used to produce requests in case when user have sent incorrect data
* @param cancelRequestFactory If set, this factory will be used to produce requests in case when it is required to say
* user that chain of scenario has been cancelled
* @param cancelTrigger When this trigger returns true, chain is cancelled
* @param filter It is main param, which will be called on each update. When it return not null, result will be returned
* as is, but when it returns null, then will be called [cancelTrigger] (if it will return true - [cancelRequestFactory]
* will be called too), [errorFactory] and then will be returned null
*/
@RiskFeature("This method is not very comfortable to use and too low-level. It is recommended to use methods which already included into library")
suspend fun <T> FlowsUpdatesFilter.expectFlow(
bot: TelegramBot,
initRequest: Request<*>? = null,
count: Int? = null,
errorFactory: NullableRequestBuilder<*> = { null },
cancelRequestFactory: NullableRequestBuilder<*> = { null },
cancelTrigger: suspend (Update) -> Boolean = { cancelRequestFactory(it) != null },
filter: suspend (Update) -> T?
): Flow<T> {
val flow = allUpdatesFlow.mapNotNull {
val result = safelyWithoutExceptions { filter(it) }
if (result == null) {
if (cancelTrigger(it)) {
cancelRequestFactory(it) ?.also {
safelyWithoutExceptions { bot.execute(it) }
throw cancelledByFilterException
}
}
errorFactory(it) ?.also { errorRequest ->
safelyWithoutExceptions { bot.execute(errorRequest) }
}
null
} else {
result
}
}
val result = if (count == null) {
flow
} else {
flow.take(count)
}
initRequest ?.also { safelyWithoutExceptions { bot.execute(initRequest) } }
return result
}
/**
* @param initRequest If not null, this request will be sent by [bot] before returning value
* @param count If set, result [Flow] will return [count] elements on each [Flow.collect]
* @param errorFactory If set, this factory will be used to produce requests in case when user have sent incorrect data
* @param cancelRequestFactory If set, this factory will be used to produce requests in case when it is required to say
* user that chain of scenario has been cancelled
* @param cancelTrigger When this trigger returns true, chain is cancelled
* @param filter It is main param, which will be called on each update. When it return not null, result will be returned
* as is, but when it returns null, then will be called [cancelTrigger] (if it will return true - [cancelRequestFactory]
* will be called too), [errorFactory] and then will be returned null
*/
suspend fun <T> BehaviourContext.expectFlow(
initRequest: Request<*>? = null,
count: Int? = null,
errorFactory: NullableRequestBuilder<*> = { null },
cancelRequestFactory: NullableRequestBuilder<*> = { null },
cancelTrigger: suspend (Update) -> Boolean = { cancelRequestFactory(it) != null },
filter: suspend (Update) -> T?
) = flowsUpdatesFilter.expectFlow(bot, initRequest, count, errorFactory, cancelRequestFactory, cancelTrigger, filter)
/**
* @param initRequest If not null, this request will be sent by [bot] before returning value
* @param errorFactory If set, this factory will be used to produce requests in case when user have sent incorrect data
* @param cancelRequestFactory If set, this factory will be used to produce requests in case when it is required to say
* user that chain of scenario has been cancelled
* @param cancelTrigger When this trigger returns true, chain is cancelled
* @param filter It is main param, which will be called on each update. When it return not null, result will be returned
* as is, but when it returns null, then will be called [cancelTrigger] (if it will return true - [cancelRequestFactory]
* will be called too), [errorFactory] and then will be returned null
*/
@RiskFeature("This method is not very comfortable to use and too low-level. It is recommended to use methods which already included into library")
suspend fun <T> FlowsUpdatesFilter.expectOne(
bot: TelegramBot,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
cancelRequestFactory: NullableRequestBuilder<*> = { null },
cancelTrigger: suspend (Update) -> Boolean = { cancelRequestFactory(it) != null },
filter: suspend (Update) -> T?,
): T = expectFlow(bot, initRequest, 1, errorFactory, cancelRequestFactory, cancelTrigger, filter).first()
/**
* @param initRequest If not null, this request will be sent by [bot] before returning value
* @param errorFactory If set, this factory will be used to produce requests in case when user have sent incorrect data
* @param cancelRequestFactory If set, this factory will be used to produce requests in case when it is required to say
* user that chain of scenario has been cancelled
* @param cancelTrigger When this trigger returns true, chain is cancelled
* @param filter It is main param, which will be called on each update. When it return not null, result will be returned
* as is, but when it returns null, then will be called [cancelTrigger] (if it will return true - [cancelRequestFactory]
* will be called too), [errorFactory] and then will be returned null
*/
suspend fun <T> BehaviourContext.expectOne(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
cancelRequestFactory: NullableRequestBuilder<*> = { null },
cancelTrigger: suspend (Update) -> Boolean = { cancelRequestFactory(it) != null },
filter: suspend (Update) -> T?
) = flowsUpdatesFilter.expectOne(bot, initRequest, errorFactory, cancelRequestFactory, cancelTrigger, filter)

View File

@@ -0,0 +1,102 @@
@file:Suppress("unused")
package dev.inmo.tgbotapi.extensions.behaviour_builder.expectations
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.utils.asCallbackQueryUpdate
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.types.CallbackQuery.*
import kotlinx.coroutines.flow.toList
typealias CallbackQueryMapper<T> = T.() -> T?
private suspend fun <O> BehaviourContext.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> BehaviourContext.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 BehaviourContext.waitDataCallbackQuery(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: CallbackQueryMapper<DataCallbackQuery>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitGameShortNameCallbackQuery(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: CallbackQueryMapper<GameShortNameCallbackQuery>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitInlineMessageIdCallbackQuery(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: CallbackQueryMapper<InlineMessageIdCallbackQuery>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitInlineMessageIdDataCallbackQuery(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: CallbackQueryMapper<InlineMessageIdDataCallbackQuery>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitInlineMessageIdGameShortNameCallbackQuery(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: CallbackQueryMapper<InlineMessageIdGameShortNameCallbackQuery>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitMessageCallbackQuery(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: CallbackQueryMapper<MessageCallbackQuery>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitMessageDataCallbackQuery(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: CallbackQueryMapper<MessageDataCallbackQuery>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitMessageGameShortNameCallbackQuery(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: CallbackQueryMapper<MessageGameShortNameCallbackQuery>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitUnknownCallbackQuery(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: CallbackQueryMapper<UnknownCallbackQueryType>? = null
) = waitEvents(count, initRequest, errorFactory, filter)

View File

@@ -0,0 +1,179 @@
@file:Suppress("unused")
package dev.inmo.tgbotapi.extensions.behaviour_builder.expectations
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.utils.asContentMessage
import dev.inmo.tgbotapi.extensions.utils.asMessageUpdate
import dev.inmo.tgbotapi.requests.abstracts.Request
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 kotlinx.coroutines.flow.toList
typealias ContentMessageToContentMapper<T> = suspend ContentMessage<T>.() -> T?
private suspend fun <O> BehaviourContext.waitContentMessage(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
mapper: suspend ContentMessage<MessageContent>.() -> O?
): List<O> = expectFlow(
initRequest,
count,
errorFactory
) {
it.asMessageUpdate() ?.data ?.asContentMessage() ?.mapper()
}.toList().toList()
private suspend inline fun <reified T : MessageContent> BehaviourContext.waitContent(
count: Int = 1,
initRequest: Request<*>? = null,
noinline errorFactory: NullableRequestBuilder<*> = { 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
} else {
filter(message)
}
} else {
null
}
}
suspend fun BehaviourContext.waitContact(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: ContentMessageToContentMapper<ContactContent>? = null
) = waitContent(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitDice(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: ContentMessageToContentMapper<DiceContent>? = null
) = waitContent(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitGame(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: ContentMessageToContentMapper<GameContent>? = null
) = waitContent(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitLocation(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: ContentMessageToContentMapper<LocationContent>? = null
) = waitContent(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitPoll(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: ContentMessageToContentMapper<PollContent>? = null
) = waitContent(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitText(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: ContentMessageToContentMapper<TextContent>? = null
) = waitContent(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitVenue(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: ContentMessageToContentMapper<VenueContent>? = null
) = waitContent(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitAudioMediaGroup(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: ContentMessageToContentMapper<AudioMediaGroupContent>? = null
) = waitContent(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitDocumentMediaGroup(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: ContentMessageToContentMapper<DocumentMediaGroupContent>? = null
) = waitContent(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitMedia(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: ContentMessageToContentMapper<MediaContent>? = null
) = waitContent(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitMediaGroup(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: ContentMessageToContentMapper<MediaGroupContent>? = null
) = waitContent(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitVisualMediaGroup(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: ContentMessageToContentMapper<VisualMediaGroupContent>? = null
) = waitContent(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitAnimation(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: ContentMessageToContentMapper<AnimationContent>? = null
) = waitContent(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitAudio(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: ContentMessageToContentMapper<AudioContent>? = null
) = waitContent(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitDocument(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: ContentMessageToContentMapper<DocumentContent>? = null
) = waitContent(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitPhoto(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: ContentMessageToContentMapper<PhotoContent>? = null
) = waitContent(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitSticker(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: ContentMessageToContentMapper<StickerContent>? = null
) = waitContent(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitVideo(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: ContentMessageToContentMapper<VideoContent>? = null
) = waitContent(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitVideoNote(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: ContentMessageToContentMapper<VideoNoteContent>? = null
) = waitContent(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitVoice(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: ContentMessageToContentMapper<VoiceContent>? = null
) = waitContent(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitInvoice(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: ContentMessageToContentMapper<InvoiceContent>? = null
) = waitContent(count, initRequest, errorFactory, filter)

View File

@@ -0,0 +1,144 @@
@file:Suppress("unused")
package dev.inmo.tgbotapi.extensions.behaviour_builder.expectations
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.utils.asChatEventMessage
import dev.inmo.tgbotapi.extensions.utils.asMessageUpdate
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 kotlinx.coroutines.flow.toList
typealias EventMessageToEventMapper<T> = suspend ChatEventMessage<T>.() -> T?
private suspend fun <O> BehaviourContext.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> BehaviourContext.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 BehaviourContext.waitChannelEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<ChannelEvent>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitChatEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<ChatEvent>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitCommonEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<CommonEvent>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitGroupEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<GroupEvent>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitSupergroupEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<SupergroupEvent>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitChannelChatCreatedEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<ChannelChatCreated>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitDeleteChatPhotoEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<DeleteChatPhoto>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitGroupChatCreatedEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<GroupChatCreated>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitLeftChatMemberEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<LeftChatMember>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitNewChatPhotoEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<NewChatPhoto>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitNewChatMembersEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<NewChatMembers>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitNewChatTitleEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<NewChatTitle>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitPinnedMessageEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<PinnedMessage>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitProximityAlertTriggeredEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<ProximityAlertTriggered>? = null
) = waitEvents(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitSupergroupChatCreatedEvents(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: EventMessageToEventMapper<SupergroupChatCreated>? = null
) = waitEvents(count, initRequest, errorFactory, filter)

View File

@@ -0,0 +1,91 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling
import dev.inmo.micro_utils.coroutines.safelyWithoutExceptions
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextAndTypeReceiver
import dev.inmo.tgbotapi.extensions.behaviour_builder.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> BehaviourContext.onCallbackQuery(
includeFilterByChatInBehaviourSubContext: Boolean = true,
noinline additionalFilter: (suspend (T) -> Boolean)? = null,
noinline scenarioReceiver: BehaviourContextAndTypeReceiver<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 (includeFilterByChatInBehaviourSubContext) {
val subFilter = FlowsUpdatesFilter()
val subBehaviourContext = 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 subBehaviourContext
} else {
null to this
}
safelyWithoutExceptions { scenario.scenarioReceiver(triggerQuery) }
jobToCancel ?.cancel()
}
suspend fun BehaviourContext.onDataCallbackQuery(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (DataCallbackQuery) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, DataCallbackQuery>
) = onCallbackQuery(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onGameShortNameCallbackQuery(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (GameShortNameCallbackQuery) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, GameShortNameCallbackQuery>
) = onCallbackQuery(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onInlineMessageIdCallbackQuery(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (InlineMessageIdCallbackQuery) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, InlineMessageIdCallbackQuery>
) = onCallbackQuery(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onInlineMessageIdDataCallbackQuery(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (InlineMessageIdDataCallbackQuery) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, InlineMessageIdDataCallbackQuery>
) = onCallbackQuery(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onInlineMessageIdGameShortNameCallbackQuery(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (InlineMessageIdGameShortNameCallbackQuery) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, InlineMessageIdGameShortNameCallbackQuery>
) = onCallbackQuery(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onMessageCallbackQuery(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (MessageCallbackQuery) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, MessageCallbackQuery>
) = onCallbackQuery(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onMessageDataCallbackQuery(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (MessageDataCallbackQuery) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, MessageDataCallbackQuery>
) = onCallbackQuery(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onMessageGameShortNameCallbackQuery(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (MessageGameShortNameCallbackQuery) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, MessageGameShortNameCallbackQuery>
) = onCallbackQuery(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onUnknownCallbackQueryType(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (UnknownCallbackQueryType) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, UnknownCallbackQueryType>
) = onCallbackQuery(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)

View File

@@ -0,0 +1,35 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling
import dev.inmo.tgbotapi.CommonAbstracts.textSources
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.utils.*
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.TextContent
import kotlinx.coroutines.Job
suspend fun BehaviourContext.command(
commandRegex: Regex,
requireOnlyCommandInMessage: Boolean = true,
includeFilterByChatInBehaviourSubContext: Boolean = true,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<TextContent>>
): Job = onText(
includeFilterByChatInBehaviourSubContext,
{ message ->
val content = message.content
val textSources = content.textSources
val sizeRequirement = if (requireOnlyCommandInMessage) {
textSources.size == 1
} else {
true
}
sizeRequirement && textSources.any { commandRegex.matches(it.asBotCommandTextSource() ?.command ?: return@any false) }
},
scenarioReceiver
)
suspend inline fun BehaviourContext.onCommand(
commandRegex: Regex,
requireOnlyCommandInMessage: Boolean = true,
includeFilterByChatInBehaviourSubContext: Boolean = true,
noinline scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<TextContent>>
): Job = command(commandRegex, requireOnlyCommandInMessage, includeFilterByChatInBehaviourSubContext, scenarioReceiver)

View File

@@ -0,0 +1,160 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling
import dev.inmo.micro_utils.coroutines.safelyWithoutExceptions
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextAndTypeReceiver
import dev.inmo.tgbotapi.extensions.behaviour_builder.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.*
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 : MessageContent> BehaviourContext.onContent(
includeFilterByChatInBehaviourSubContext: Boolean = true,
noinline additionalFilter: (suspend (ContentMessage<T>) -> Boolean)? = null,
noinline scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<T>>
) = flowsUpdatesFilter.expectFlow(bot) {
it.asMessageUpdate() ?.data ?.asContentMessage() ?.let { message ->
if (message.content is T) {
val adaptedMessage = message as ContentMessage<T>
if (additionalFilter == null || additionalFilter(adaptedMessage)) adaptedMessage else null
} else {
null
}
}
}.subscribeSafelyWithoutExceptions(scope) { triggerMessage ->
val (jobToCancel, scenario) = if (includeFilterByChatInBehaviourSubContext) {
val subFilter = FlowsUpdatesFilter()
val subBehaviourContext = copy(flowsUpdatesFilter = subFilter)
flowsUpdatesFilter.allUpdatesFlow.filter {
val chat = it.sourceChat() ?: return@filter false
chat.id.chatId == triggerMessage.chat.id.chatId
}.subscribeSafelyWithoutExceptions(scope, subFilter.asUpdateReceiver) to subBehaviourContext
} else {
null to this
}
safelyWithoutExceptions { scenario.scenarioReceiver(triggerMessage) }
jobToCancel ?.cancel()
}
suspend fun BehaviourContext.onContact(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ContentMessage<ContactContent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<ContactContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onDice(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ContentMessage<DiceContent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<DiceContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onGame(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ContentMessage<GameContent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<GameContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onLocation(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ContentMessage<LocationContent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<LocationContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onPoll(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ContentMessage<PollContent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<PollContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onText(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ContentMessage<TextContent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<TextContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onVenue(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ContentMessage<VenueContent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<VenueContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onAudioMediaGroup(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ContentMessage<AudioMediaGroupContent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<AudioMediaGroupContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onDocumentMediaGroup(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ContentMessage<DocumentMediaGroupContent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<DocumentMediaGroupContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onMediaCollection(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ContentMessage<MediaCollectionContent<TelegramMediaFile>>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<MediaCollectionContent<TelegramMediaFile>>>
) = onContent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onMedia(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ContentMessage<MediaContent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<MediaContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onMediaGroup(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ContentMessage<MediaGroupContent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<MediaGroupContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onVisualMediaGroup(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ContentMessage<VisualMediaGroupContent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<VisualMediaGroupContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onAnimation(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ContentMessage<AnimationContent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<AnimationContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onAudio(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ContentMessage<AudioContent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<AudioContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onDocument(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ContentMessage<DocumentContent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<DocumentContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onPhoto(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ContentMessage<PhotoContent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<PhotoContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onSticker(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ContentMessage<StickerContent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<StickerContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onVideo(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ContentMessage<VideoContent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<VideoContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onVideoNote(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ContentMessage<VideoNoteContent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<VideoNoteContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onVoice(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ContentMessage<VoiceContent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<VoiceContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onInvoice(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ContentMessage<InvoiceContent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ContentMessage<InvoiceContent>>
) = onContent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)

View File

@@ -0,0 +1,124 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling
import dev.inmo.micro_utils.coroutines.safelyWithoutExceptions
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextAndTypeReceiver
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.expectFlow
import dev.inmo.tgbotapi.extensions.utils.*
import dev.inmo.tgbotapi.extensions.utils.extensions.sourceChat
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.*
import dev.inmo.tgbotapi.types.message.content.abstracts.*
import dev.inmo.tgbotapi.types.message.content.media.*
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import kotlinx.coroutines.flow.filter
internal suspend inline fun <reified T : ChatEvent> BehaviourContext.onEvent(
includeFilterByChatInBehaviourSubContext: Boolean = true,
noinline additionalFilter: (suspend (ChatEventMessage<T>) -> Boolean)? = null,
noinline scenarioReceiver: BehaviourContextAndTypeReceiver<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 (includeFilterByChatInBehaviourSubContext) {
val subFilter = FlowsUpdatesFilter()
val subBehaviourContext = copy(flowsUpdatesFilter = subFilter)
flowsUpdatesFilter.allUpdatesFlow.filter {
val chat = it.sourceChat() ?: return@filter false
chat.id.chatId == triggerMessage.chat.id.chatId
}.subscribeSafelyWithoutExceptions(scope, subFilter.asUpdateReceiver) to subBehaviourContext
} else {
null to this
}
safelyWithoutExceptions { scenario.scenarioReceiver(triggerMessage) }
jobToCancel ?.cancel()
}
suspend fun BehaviourContext.onChannelEvent(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<ChannelEvent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ChatEventMessage<ChannelEvent>>
) = onEvent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onChatEvent(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<ChatEvent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ChatEventMessage<ChatEvent>>
) = onEvent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onCommonEvent(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<CommonEvent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ChatEventMessage<CommonEvent>>
) = onEvent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onGroupEvent(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<GroupEvent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ChatEventMessage<GroupEvent>>
) = onEvent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onSupergroupEvent(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<SupergroupEvent>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ChatEventMessage<SupergroupEvent>>
) = onEvent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onChannelChatCreated(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<ChannelChatCreated>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ChatEventMessage<ChannelChatCreated>>
) = onEvent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onDeleteChatPhoto(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<DeleteChatPhoto>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ChatEventMessage<DeleteChatPhoto>>
) = onEvent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onGroupChatCreated(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<GroupChatCreated>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ChatEventMessage<GroupChatCreated>>
) = onEvent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onLeftChatMember(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<LeftChatMember>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ChatEventMessage<LeftChatMember>>
) = onEvent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onNewChatMembers(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<NewChatMembers>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ChatEventMessage<NewChatMembers>>
) = onEvent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onNewChatPhoto(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<NewChatPhoto>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ChatEventMessage<NewChatPhoto>>
) = onEvent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onNewChatTitle(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<NewChatTitle>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ChatEventMessage<NewChatTitle>>
) = onEvent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onPinnedMessage(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<PinnedMessage>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ChatEventMessage<PinnedMessage>>
) = onEvent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onProximityAlertTriggered(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<ProximityAlertTriggered>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ChatEventMessage<ProximityAlertTriggered>>
) = onEvent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)
suspend fun BehaviourContext.onSupergroupChatCreated(
includeFilterByChatInBehaviourSubContext: Boolean = true,
additionalFilter: (suspend (ChatEventMessage<SupergroupChatCreated>) -> Boolean)? = null,
scenarioReceiver: BehaviourContextAndTypeReceiver<Unit, ChatEventMessage<SupergroupChatCreated>>
) = onEvent(includeFilterByChatInBehaviourSubContext, additionalFilter, scenarioReceiver)