tgbotapi/tgbotapi.extensions.behavio.../src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/Base.kt

127 lines
6.7 KiB
Kotlin
Raw Normal View History

package dev.inmo.tgbotapi.extensions.behaviour_builder.expectations
2021-01-06 17:06:48 +00:00
2021-06-27 19:00:20 +00:00
import dev.inmo.micro_utils.coroutines.safelyWithResult
2021-01-06 17:06:48 +00:00
import dev.inmo.micro_utils.coroutines.safelyWithoutExceptions
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
2021-10-13 08:22:01 +00:00
import dev.inmo.tgbotapi.extensions.utils.flatMap
2021-01-06 17:06:48 +00:00
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
2021-01-09 16:18:31 +00:00
import dev.inmo.tgbotapi.utils.lowLevelRiskFeatureMessage
2021-01-07 06:45:54 +00:00
import kotlinx.coroutines.CancellationException
2021-01-06 17:06:48 +00:00
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>?
2021-01-07 10:01:25 +00:00
/**
* @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
*/
2021-01-09 16:18:31 +00:00
@RiskFeature(lowLevelRiskFeatureMessage)
2021-01-06 17:06:48 +00:00
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 },
2021-01-07 18:22:19 +00:00
filter: suspend (Update) -> List<T>
2021-01-06 17:06:48 +00:00
): Flow<T> {
2021-10-13 08:22:01 +00:00
val flow = allUpdatesFlow.map {
2021-06-27 19:00:20 +00:00
val result = safelyWithResult { filter(it) }
2021-10-13 08:22:01 +00:00
if (result.isFailure || result.getOrThrow().isEmpty()) {
2021-01-06 17:06:48 +00:00
if (cancelTrigger(it)) {
cancelRequestFactory(it) ?.also {
2021-06-27 19:00:20 +00:00
safelyWithResult { bot.execute(it) }
2021-01-06 17:06:48 +00:00
throw cancelledByFilterException
}
}
errorFactory(it) ?.also { errorRequest ->
safelyWithoutExceptions { bot.execute(errorRequest) }
}
2021-01-07 18:22:19 +00:00
emptyList()
2021-01-06 17:06:48 +00:00
} else {
2021-06-27 19:00:20 +00:00
result.getOrThrow()
2021-10-13 08:22:01 +00:00
}
}.flatMap()
2021-01-06 17:06:48 +00:00
val result = if (count == null) {
flow
} else {
flow.take(count)
}
initRequest ?.also { safelyWithoutExceptions { bot.execute(initRequest) } }
return result
}
2021-01-07 10:01:25 +00:00
/**
* @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
*/
2021-01-07 12:11:01 +00:00
suspend fun <T> BehaviourContext.expectFlow(
2021-01-06 17:06:48 +00:00
initRequest: Request<*>? = null,
count: Int? = null,
errorFactory: NullableRequestBuilder<*> = { null },
cancelRequestFactory: NullableRequestBuilder<*> = { null },
cancelTrigger: suspend (Update) -> Boolean = { cancelRequestFactory(it) != null },
2021-01-07 18:22:19 +00:00
filter: suspend (Update) -> List<T>
2021-01-06 17:06:48 +00:00
) = flowsUpdatesFilter.expectFlow(bot, initRequest, count, errorFactory, cancelRequestFactory, cancelTrigger, filter)
2021-01-07 10:01:25 +00:00
/**
* @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
*/
2021-01-09 16:18:31 +00:00
@RiskFeature(lowLevelRiskFeatureMessage)
2021-01-06 17:06:48 +00:00
suspend fun <T> FlowsUpdatesFilter.expectOne(
bot: TelegramBot,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
cancelRequestFactory: NullableRequestBuilder<*> = { null },
cancelTrigger: suspend (Update) -> Boolean = { cancelRequestFactory(it) != null },
2021-01-07 18:22:19 +00:00
filter: suspend (Update) -> T?
): T = expectFlow(bot, initRequest, 1, errorFactory, cancelRequestFactory, cancelTrigger) {
listOfNotNull(filter.invoke(it))
}.first()
2021-01-06 17:06:48 +00:00
2021-01-07 10:01:25 +00:00
/**
* @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
*/
2021-01-07 12:11:01 +00:00
suspend fun <T> BehaviourContext.expectOne(
2021-01-06 17:06:48 +00:00
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)