1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2025-11-17 04:20:13 +00:00

Compare commits

..

21 Commits
1.1.0 ... 1.2.0

Author SHA1 Message Date
507cb2af26 cleanup waiters in behaviour builder and add withEvent 2022-05-21 23:38:42 +06:00
126a07cdc7 start 1.2.0 2022-05-21 23:32:36 +06:00
27a24a18a6 fix of updates funnel in subsequent behaviour contexts 2022-05-21 21:44:33 +06:00
562e8c7429 improvements in Beheviour Context 2022-05-21 17:33:44 +06:00
be027efc43 start 1.1.4 2022-05-21 17:27:04 +06:00
e55195b308 Merge pull request #593 from InsanusMokrassar/1.1.3
1.1.3
2022-05-19 13:38:05 -04:00
14afb59b5f StateHandlingErrorHandler in microutils 2022-05-19 20:37:52 +06:00
6b23347d56 start 1.1.3 2022-05-19 20:24:55 +06:00
f54843fb60 Merge pull request #592 from InsanusMokrassar/1.1.2
1.1.2
2022-05-18 07:10:31 -04:00
8a3538d9c7 fixes in relate to main behaviourBuilder fun 2022-05-18 17:05:47 +06:00
e3db4250bb add default copy realization in BehaviourContextWithFSM 2022-05-18 16:52:52 +06:00
4643ac906a StateHandlingErrorHandler 2022-05-18 16:45:08 +06:00
497974e45c fix of #591 2022-05-18 16:31:14 +06:00
78a00f0efb start 1.1.2 2022-05-18 16:27:30 +06:00
14ecb9d948 Merge pull request #590 from InsanusMokrassar/1.1.1
1.1.1
2022-05-17 09:56:47 -04:00
c759b9a466 deprecate hmac and hex extensions for CryptoJS in webapp 2022-05-17 19:54:13 +06:00
58c1f2ee6a fixes in TelegramAPIUrlsKeeper#checkWebAppLink 2022-05-17 19:51:14 +06:00
9d16ca3b7e small refactor of buildBehaviourWithLongPolling 2022-05-16 18:59:30 +06:00
9d40e598f1 buildBehaciour returns BehaviourContext 2022-05-16 18:58:19 +06:00
f1be8bf16e start 1.1.1 2022-05-16 18:53:15 +06:00
56a8804e99 Merge pull request #589 from InsanusMokrassar/1.1.0
1.1.0
2022-05-14 12:21:08 +06:00
30 changed files with 611 additions and 871 deletions

View File

@@ -1,5 +1,36 @@
# TelegramBotAPI changelog
## 1.2.0
* `Behaviour Builder`:
* Mappers have been removed from waiters extensions
* Triggers extensions now will use filtering inside of context receiver instead of passing the filters into `BehaviourContext`. That means that in the subcontext will not be used preinstalled filters for their `BehaviourContext` and filter of trigger will not be used in subcontext
* `Utils`:
* Add opportunity to get event messages with specific `ChatEvent` type using `withEvent`/`requireWithEvent` (by analog with `withEvent` and `requireWithEvent`)
## 1.1.3
* `Behaviour Builder with FSM`:
* Typealias `StateHandlingErrorHandler` from `1.1.2` has been deprecated and should be replaced with the new one from microutils
## 1.1.2
* `Core`:
* Rename of `TelegramAPIUrlsKeeper#checkWebAppLink` -> `TelegramAPIUrlsKeeper#checkWebAppData` (fix of [#591](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/591))
* `Behaviour Builder with FSM`:
* New typealias `StateHandlingErrorHandler`
* `BehaviourBuilderWithFSM` now accepts new parameter `onStateHandlingErrorHandler` which will be used in case if state has not been successfully completed
## 1.1.1
* `Versions`:
* `MicroUtils.Crypto` will not be provided with that library anymore. Instead, it is recommended to use `Korlibs.Krypto`. You still can add crypto from microutils using next groovy dependency: `dev.inmo:micro_utils.crypto:$micro_utils_version`
* `Core`:
* Improvements in `TelegramAPIUrlsKeeper#checkWebAppLink`
* New field in `TelegramAPIUrlsKeeper#webAppDataSecretKeyHash`
* `Behaviour Builder`:
* Extension `TelegramBot#buildBehaviour` now returns `BehaviourContext`
## 1.1.0
* `Utils`:

View File

@@ -8,11 +8,11 @@ kotlin.incremental.js=true
kotlin_version=1.6.21
kotlin_coroutines_version=1.6.1
kotlin_serialisation_runtime_version=1.3.3
klock_version=2.7.0
korlibs_version=2.7.0
uuid_version=0.4.0
ktor_version=2.0.1
micro_utils_version=0.10.4
micro_utils_version=0.10.5
javax_activation_version=1.1.1
@@ -20,6 +20,6 @@ javax_activation_version=1.1.1
dokka_version=1.6.21
library_group=dev.inmo
library_version=1.1.0
library_version=1.2.0
github_release_plugin_version=2.3.7

View File

@@ -1,8 +1,9 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.coroutines.*
import dev.inmo.micro_utils.fsm.common.*
import dev.inmo.micro_utils.fsm.common.utils.StateHandlingErrorHandler
import dev.inmo.micro_utils.fsm.common.utils.defaultStateHandlingErrorHandler
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.types.update.abstracts.Update
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.handlers_registrar.TriggersHolder
@@ -50,12 +51,32 @@ interface BehaviourContextWithFSM<T : State> : BehaviourContext, StatesMachine<T
updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>?
): BehaviourContextWithFSM<T>
fun copy(
bot: TelegramBot = this.bot,
scope: CoroutineScope = this.scope,
broadcastChannelsSize: Int = 100,
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND,
upstreamUpdatesFlow: Flow<Update>? = null,
triggersHolder: TriggersHolder = this.triggersHolder,
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>? = null
): BehaviourContextWithFSM<T> = copy(
bot,
scope,
broadcastChannelsSize,
onBufferOverflow,
upstreamUpdatesFlow,
triggersHolder,
updatesFilter
)
companion object {
operator fun <T : State> invoke(
behaviourContext: BehaviourContext,
handlers: List<BehaviourWithFSMStateHandlerHolder<*, T>>,
statesManager: StatesManager<T>
) = DefaultBehaviourContextWithFSM<T>(behaviourContext, statesManager, handlers)
statesManager: StatesManager<T>,
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler()
) = DefaultBehaviourContextWithFSM<T>(behaviourContext, statesManager, handlers, onStateHandlingErrorHandler)
}
}
@@ -84,16 +105,22 @@ inline fun <reified I : O, O: State> BehaviourContextWithFSM<O>.strictlyOn(handl
/**
* Default realization of [BehaviourContextWithFSM]. It uses [behaviourContext] as a base for this object as
* [BehaviourContext], but managing substates contexts updates for avoiding of updates lost between states
* @param onStateHandlingErrorHandler Will be used in case if state handling has not been successfully completed in [launchStateHandling]
*/
class DefaultBehaviourContextWithFSM<T : State>(
private val behaviourContext: BehaviourContext,
private val statesManager: StatesManager<T>,
private val handlers: List<BehaviourWithFSMStateHandlerHolder<*, T>>
private val handlers: List<BehaviourWithFSMStateHandlerHolder<*, T>>,
private val onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler()
) : BehaviourContext by behaviourContext, BehaviourContextWithFSM<T> {
private val updatesFlows = mutableMapOf<Any, DefaultBehaviourContextWithFSM<T>>()
private val additionalHandlers = mutableListOf<BehaviourWithFSMStateHandlerHolder<*, T>>()
private var actualHandlersList = additionalHandlers + handlers
override suspend fun launchStateHandling(state: T, handlers: List<CheckableHandlerHolder<in T, T>>): T? {
return launchStateHandling(state, handlers, onStateHandlingErrorHandler)
}
private fun getSubContext(context: Any) = updatesFlows.getOrPut(context) {
createSubContext()
}
@@ -174,6 +201,23 @@ class DefaultBehaviourContextWithFSM<T : State>(
): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM(
behaviourContext.copy(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, triggersHolder, updatesFilter),
handlers,
statesManager
statesManager,
onStateHandlingErrorHandler
)
override fun copy(
bot: TelegramBot,
scope: CoroutineScope,
broadcastChannelsSize: Int,
onBufferOverflow: BufferOverflow,
upstreamUpdatesFlow: Flow<Update>?,
triggersHolder: TriggersHolder,
onStateHandlingErrorHandler: StateHandlingErrorHandler<T>,
updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>?
): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM(
behaviourContext.copy(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, triggersHolder, updatesFilter),
handlers,
statesManager,
onStateHandlingErrorHandler
)
}

View File

@@ -4,15 +4,14 @@ import dev.inmo.micro_utils.coroutines.*
import dev.inmo.micro_utils.fsm.common.*
import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManager
import dev.inmo.micro_utils.fsm.common.managers.InMemoryDefaultStatesManagerRepo
import dev.inmo.micro_utils.fsm.common.utils.StateHandlingErrorHandler
import dev.inmo.micro_utils.fsm.common.utils.defaultStateHandlingErrorHandler
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.longPolling
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling
import dev.inmo.tgbotapi.types.update.abstracts.Update
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import dev.inmo.tgbotapi.utils.PreviewFeature
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow
import kotlin.reflect.KClass
@Deprecated("Will be removed soon")
typealias BehaviourContextWithFSMBuilder<T> = BehaviourContextWithFSM<T>
@@ -33,6 +32,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM(
DefaultBehaviourContext(
@@ -41,7 +41,8 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
upstreamUpdatesFlow = upstreamUpdatesFlow
),
presetHandlers,
statesManager
statesManager,
onStateHandlingErrorHandler
).apply { block() }
/**
@@ -55,6 +56,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): Pair<DefaultBehaviourContextWithFSM<T>, Job> = buildBehaviourWithFSM(
upstreamUpdatesFlow,
@@ -62,6 +64,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
defaultExceptionsHandler,
statesManager,
presetHandlers,
onStateHandlingErrorHandler,
block
).run {
this to scope.launch {
@@ -94,6 +97,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM(
DefaultBehaviourContext(
@@ -102,7 +106,8 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
upstreamUpdatesFlow = flowUpdatesFilter.allUpdatesFlow
),
presetHandlers,
statesManager
statesManager,
onStateHandlingErrorHandler
).apply { block() }
/**
@@ -121,6 +126,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
) = FlowsUpdatesFilter().let {
buildBehaviourWithFSM(
@@ -129,6 +135,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
defaultExceptionsHandler,
statesManager,
presetHandlers,
onStateHandlingErrorHandler,
block
).run {
start()

View File

@@ -0,0 +1,10 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder
import dev.inmo.micro_utils.fsm.common.utils.StateHandlingErrorHandler
@Deprecated("Has been added in microutils", ReplaceWith("StateHandlingErrorHandler", "dev.inmo.micro_utils.fsm.common.utils.StateHandlingErrorHandler"))
typealias StateHandlingErrorHandler<T> = StateHandlingErrorHandler<T>
@Deprecated("Has been added in microutils", ReplaceWith("DefaultStateHandlingErrorHandler", "dev.inmo.micro_utils.fsm.common.utils.DefaultStateHandlingErrorHandler"))
val DefaultStateHandlingErrorHandler = dev.inmo.micro_utils.fsm.common.utils.DefaultStateHandlingErrorHandler
@Deprecated("Has been added in microutils", ReplaceWith("defaultStateHandlingErrorHandler", "dev.inmo.micro_utils.fsm.common.utils.defaultStateHandlingErrorHandler"))
inline fun <T> defaultStateHandlingErrorHandler() = dev.inmo.micro_utils.fsm.common.utils.defaultStateHandlingErrorHandler<T>()

View File

@@ -5,6 +5,8 @@ import dev.inmo.micro_utils.fsm.common.State
import dev.inmo.micro_utils.fsm.common.StatesManager
import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManager
import dev.inmo.micro_utils.fsm.common.managers.InMemoryDefaultStatesManagerRepo
import dev.inmo.micro_utils.fsm.common.utils.StateHandlingErrorHandler
import dev.inmo.micro_utils.fsm.common.utils.defaultStateHandlingErrorHandler
import dev.inmo.tgbotapi.bot.ktor.KtorRequestsExecutorBuilder
import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.bot.TelegramBot
@@ -39,6 +41,7 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSM(
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
testServer: Boolean = false,
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): TelegramBot = telegramBot(
token,
@@ -52,6 +55,7 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSM(
defaultExceptionsHandler,
statesManager,
presetHandlers,
onStateHandlingErrorHandler,
block
)
}
@@ -76,6 +80,7 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSMAndStartLongPolling(
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
testServer: Boolean = false,
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): Pair<TelegramBot, Job> {
return telegramBot(
@@ -89,6 +94,7 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSMAndStartLongPolling(
defaultExceptionsHandler,
statesManager,
presetHandlers,
onStateHandlingErrorHandler,
block
)
}

View File

@@ -7,8 +7,7 @@ import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.longPolling
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import dev.inmo.tgbotapi.utils.PreviewFeature
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.plus
import kotlinx.coroutines.*
/**
* This function is used in [buildBehaviour] extensions to provide default [CoroutineScope] and allow to avoid all
@@ -30,8 +29,7 @@ suspend fun TelegramBot.buildBehaviour(
scope: CoroutineScope = defaultCoroutineScopeProvider(),
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
block: BehaviourContextReceiver<Unit>
) {
BehaviourContext(
): BehaviourContext = BehaviourContext(
this,
scope.let {
if (defaultExceptionsHandler == null) {
@@ -41,7 +39,8 @@ suspend fun TelegramBot.buildBehaviour(
}
},
flowUpdatesFilter
).block()
).apply {
block()
}
/**
@@ -56,15 +55,14 @@ suspend fun TelegramBot.buildBehaviourWithLongPolling(
scope: CoroutineScope = defaultCoroutineScopeProvider(),
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
block: BehaviourContextReceiver<Unit>
) = FlowsUpdatesFilter().let {
buildBehaviour(
it,
scope,
defaultExceptionsHandler,
block
): Job {
val behaviourContext = buildBehaviour(
scope = scope,
defaultExceptionsHandler = defaultExceptionsHandler,
block = block
)
longPolling(
it,
scope = scope
return longPolling(
behaviourContext,
scope = behaviourContext
)
}

View File

@@ -49,6 +49,19 @@ interface BehaviourContext : FlowsUpdatesFilter, TelegramBot, CoroutineScope {
val triggersHolder: TriggersHolder
fun copy(
bot: TelegramBot = this.bot,
scope: CoroutineScope = this.scope,
broadcastChannelsSize: Int = 100,
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND,
upstreamUpdatesFlow: Flow<Update>? = null,
triggersHolder: TriggersHolder = TriggersHolder()
): BehaviourContext
/**
* @param updatesFilter unused
*/
@Deprecated("Do not use this method")
fun copy(
bot: TelegramBot = this.bot,
scope: CoroutineScope = this.scope,
@@ -57,7 +70,7 @@ interface BehaviourContext : FlowsUpdatesFilter, TelegramBot, CoroutineScope {
upstreamUpdatesFlow: Flow<Update>? = null,
triggersHolder: TriggersHolder = TriggersHolder(),
updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>? = null
): BehaviourContext
): BehaviourContext = copy(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, triggersHolder)
}
class DefaultBehaviourContext(
@@ -67,20 +80,17 @@ class DefaultBehaviourContext(
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND,
private val upstreamUpdatesFlow: Flow<Update>? = null,
override val triggersHolder: TriggersHolder = TriggersHolder(),
@Deprecated("This method is not used anymore")
private val updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>? = null
) : AbstractFlowsUpdatesFilter(), TelegramBot by bot, CoroutineScope by scope, BehaviourContext {
private val additionalUpdatesSharedFlow = MutableSharedFlow<Update>(0, broadcastChannelsSize, onBufferOverflow)
override val allUpdatesFlow: Flow<Update> = (additionalUpdatesSharedFlow.asSharedFlow()).let {
if (upstreamUpdatesFlow != null) {
(it + upstreamUpdatesFlow).distinctUntilChanged { old, new -> old.updateId == new.updateId }
} else {
it
var lastHandledUpdate = -1L
(it + upstreamUpdatesFlow).filter {
(it.updateId > lastHandledUpdate).also { passed -> if (passed) { lastHandledUpdate = it.updateId } }
}
}.let {
val updatesFilter = updatesFilter
if (updatesFilter != null) {
it.filter { updatesFilter(it) }
} else {
it
}
@@ -93,9 +103,8 @@ class DefaultBehaviourContext(
broadcastChannelsSize: Int,
onBufferOverflow: BufferOverflow,
upstreamUpdatesFlow: Flow<Update>?,
triggersHolder: TriggersHolder,
updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>?
): DefaultBehaviourContext = DefaultBehaviourContext(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, triggersHolder, updatesFilter)
triggersHolder: TriggersHolder
): DefaultBehaviourContext = DefaultBehaviourContext(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, triggersHolder)
}
fun BehaviourContext(
@@ -113,24 +122,34 @@ inline fun <T> BehaviourContext(
crossinline block: BehaviourContext.() -> T
) = DefaultBehaviourContext(bot, scope, upstreamUpdatesFlow = flowsUpdatesFilter.allUpdatesFlow, triggersHolder = triggersHolder).run(block)
/**
* Creates new [BehaviourContext] using its [BehaviourContext.copy] method
*
* @param updatesFilter This param will not be used anymore
*/
fun <BC : BehaviourContext> BC.createSubContext(
scope: CoroutineScope = LinkedSupervisorScope(),
triggersHolder: TriggersHolder = this.triggersHolder,
updatesUpstreamFlow: Flow<Update> = allUpdatesFlow,
updatesFilter: CustomBehaviourContextAndTypeReceiver<BC, Boolean, Update>? = null,
) = copy(
scope = scope,
updatesFilter = updatesFilter ?.let { _ ->
{
(this as? BC) ?.run {
updatesFilter(it)
} ?: true
}
},
upstreamUpdatesFlow = updatesUpstreamFlow,
triggersHolder = triggersHolder
) as BC
/**
* Creates new [BehaviourContext] using its [BehaviourContext.copy] method
*
* @param updatesFilter This param will not be used anymore
*/
@Deprecated("It is not recommended to use updates filter anymore")
fun <BC : BehaviourContext> BC.createSubContext(
scope: CoroutineScope = LinkedSupervisorScope(),
triggersHolder: TriggersHolder = this.triggersHolder,
updatesUpstreamFlow: Flow<Update> = allUpdatesFlow,
updatesFilter: CustomBehaviourContextAndTypeReceiver<BC, Boolean, Update>?,
) = createSubContext(scope, triggersHolder, updatesUpstreamFlow)
/**
* Launch [behaviourContextReceiver] in context of [this] as [BehaviourContext] and as [kotlin.coroutines.CoroutineContext]
*
@@ -155,21 +174,40 @@ suspend fun <T, BC : BehaviourContext> BC.createSubContextAndDoWithUpdatesFilter
scope: CoroutineScope = LinkedSupervisorScope(),
triggersHolder: TriggersHolder = this.triggersHolder,
updatesUpstreamFlow: Flow<Update> = allUpdatesFlow,
updatesFilter: CustomBehaviourContextAndTypeReceiver<BC, Boolean, Update>? = null,
stopOnCompletion: Boolean = true,
behaviourContextReceiver: CustomBehaviourContextReceiver<BC, T>
): T {
return createSubContext(
scope,
triggersHolder,
updatesUpstreamFlow,
updatesFilter
updatesUpstreamFlow
).doInContext(
stopOnCompletion,
behaviourContextReceiver
)
}
/**
* Creates new one [BehaviourContext] using [createSubContext] and launches [behaviourContextReceiver] in a new context
* using [doInContext]
*
* @param stopOnCompletion ___TRUE BY DEFAULT___
* @param updatesFilter Is not used anymore
*/
@Deprecated("It is not recommended to use updates filter anymore")
suspend fun <T, BC : BehaviourContext> BC.createSubContextAndDoWithUpdatesFilter(
scope: CoroutineScope = LinkedSupervisorScope(),
triggersHolder: TriggersHolder = this.triggersHolder,
updatesUpstreamFlow: Flow<Update> = allUpdatesFlow,
updatesFilter: CustomBehaviourContextAndTypeReceiver<BC, Boolean, Update>?,
stopOnCompletion: Boolean = true,
behaviourContextReceiver: CustomBehaviourContextReceiver<BC, T>
): T {
return createSubContextAndDoWithUpdatesFilter(
scope, triggersHolder, updatesUpstreamFlow, stopOnCompletion, behaviourContextReceiver
)
}
/**
* Creates new one [BehaviourContext] using [createSubContext] and launches [behaviourContextReceiver] in a new context
* using [doInContext]

View File

@@ -11,114 +11,76 @@ import kotlinx.coroutines.flow.toList
typealias CallbackQueryMapper<T> = suspend T.() -> T?
private suspend fun <O> BehaviourContext.waitCallbackQueries(
private suspend inline fun <reified O> BehaviourContext.waitCallbackQueries(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<CallbackQuery>? = null,
mapper: suspend CallbackQuery.() -> O?
noinline errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<O>? = null
): List<O> = expectFlow(
initRequest,
count,
errorFactory
) {
val data = it.asCallbackQueryUpdate() ?.data
if (data != null && (filter == null || filter(data))) {
data.mapper().let(::listOfNotNull)
val data = it.asCallbackQueryUpdate() ?.data ?: return@expectFlow emptyList()
if (data is O && (filter == null || filter(data))) {
listOf(data)
} else {
emptyList()
}
}.toList().toList()
private suspend inline fun <reified T : CallbackQuery> BehaviourContext.waitCallbacks(
count: Int = 1,
initRequest: Request<*>? = null,
noinline errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<T>? = null,
noinline mapper: CallbackQueryMapper<T>? = null
) : List<T> = waitCallbackQueries<T>(
count,
initRequest,
errorFactory,
filter ?.let {
{
(it as? T) ?.let { filter(it) } == true
}
}
) {
if (this is T) {
if (mapper == null) {
this
} else {
mapper(this)
}
} else {
null
}
}
suspend fun BehaviourContext.waitDataCallbackQuery(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<DataCallbackQuery>? = null,
mapper: CallbackQueryMapper<DataCallbackQuery>? = null
) = waitCallbacks(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<DataCallbackQuery>? = null
) = waitCallbackQueries(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitGameShortNameCallbackQuery(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<GameShortNameCallbackQuery>? = null,
mapper: CallbackQueryMapper<GameShortNameCallbackQuery>? = null
) = waitCallbacks(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<GameShortNameCallbackQuery>? = null
) = waitCallbackQueries(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitInlineMessageIdCallbackQuery(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<InlineMessageIdCallbackQuery>? = null,
mapper: CallbackQueryMapper<InlineMessageIdCallbackQuery>? = null
) = waitCallbacks(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<InlineMessageIdCallbackQuery>? = null
) = waitCallbackQueries(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitInlineMessageIdDataCallbackQuery(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<InlineMessageIdDataCallbackQuery>? = null,
mapper: CallbackQueryMapper<InlineMessageIdDataCallbackQuery>? = null
) = waitCallbacks(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<InlineMessageIdDataCallbackQuery>? = null
) = waitCallbackQueries(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitInlineMessageIdGameShortNameCallbackQuery(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<InlineMessageIdGameShortNameCallbackQuery>? = null,
mapper: CallbackQueryMapper<InlineMessageIdGameShortNameCallbackQuery>? = null
) = waitCallbacks(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<InlineMessageIdGameShortNameCallbackQuery>? = null
) = waitCallbackQueries(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitMessageCallbackQuery(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<MessageCallbackQuery>? = null,
mapper: CallbackQueryMapper<MessageCallbackQuery>? = null
) = waitCallbacks(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<MessageCallbackQuery>? = null
) = waitCallbackQueries(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitMessageDataCallbackQuery(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<MessageDataCallbackQuery>? = null,
mapper: CallbackQueryMapper<MessageDataCallbackQuery>? = null
) = waitCallbacks(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<MessageDataCallbackQuery>? = null
) = waitCallbackQueries(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitMessageGameShortNameCallbackQuery(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<MessageGameShortNameCallbackQuery>? = null,
mapper: CallbackQueryMapper<MessageGameShortNameCallbackQuery>? = null
) = waitCallbacks(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<MessageGameShortNameCallbackQuery>? = null
) = waitCallbackQueries(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitUnknownCallbackQuery(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<UnknownCallbackQueryType>? = null,
mapper: CallbackQueryMapper<UnknownCallbackQueryType>? = null
) = waitCallbacks(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<UnknownCallbackQueryType>? = null
) = waitCallbackQueries(count, initRequest, errorFactory, filter)

View File

@@ -9,20 +9,19 @@ import kotlinx.coroutines.flow.toList
typealias ChatJoinRequestsMapper = suspend ChatJoinRequest.() -> ChatJoinRequest?
private suspend fun <O> BehaviourContext.waitChatJoinRequests(
private suspend inline fun <reified O> BehaviourContext.internalWaitChatJoinRequests(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<ChatJoinRequest>? = null,
mapper: suspend ChatJoinRequest.() -> O?
noinline errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<O>? = null
): List<O> = expectFlow(
initRequest,
count,
errorFactory
) {
val data = it.asChatJoinRequestUpdate() ?.data
if (data != null && (filter == null || filter(data))) {
data.mapper().let(::listOfNotNull)
val data = it.asChatJoinRequestUpdate() ?.data as? O ?: return@expectFlow emptyList()
if (filter == null || filter(data)) {
listOf(data)
} else {
emptyList()
}
@@ -33,17 +32,10 @@ suspend fun BehaviourContext.waitChatJoinRequests(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatJoinRequest>? = null,
mapper: ChatJoinRequestsMapper? = null
) : List<ChatJoinRequest> = waitChatJoinRequests(
filter: SimpleFilter<ChatJoinRequest>? = null
) : List<ChatJoinRequest> = internalWaitChatJoinRequests(
count,
initRequest,
errorFactory,
filter
) {
if (mapper == null) {
this
} else {
mapper(this)
}
}
)

View File

@@ -11,12 +11,11 @@ import kotlinx.coroutines.flow.toList
typealias ChatMemberUpdatedMapper<T> = suspend T.() -> T?
private suspend inline fun <reified T : ChatMemberUpdatedUpdate> BehaviourContext.waitChatMemberUpdated(
private suspend inline fun <reified T : ChatMemberUpdatedUpdate> BehaviourContext.waitChatMemberUpdatedWithFilter(
count: Int = 1,
initRequest: Request<*>? = null,
noinline errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<T>? = null,
noinline mapper: ChatMemberUpdatedMapper<ChatMemberUpdated>
filter: SimpleFilter<T>? = null
): List<ChatMemberUpdated> = expectFlow(
initRequest,
count,
@@ -24,51 +23,29 @@ private suspend inline fun <reified T : ChatMemberUpdatedUpdate> BehaviourContex
) {
val casted = (it as? T) ?: return@expectFlow emptyList()
if (filter == null || filter(casted)) {
casted.data.mapper().let(::listOfNotNull)
listOf(casted.data)
} else {
emptyList()
}
}.toList().toList()
private suspend inline fun <reified T : ChatMemberUpdatedUpdate> BehaviourContext.waitChatMemberUpdatedWithFilter(
count: Int = 1,
initRequest: Request<*>? = null,
noinline errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<T>? = null,
noinline mapper: ChatMemberUpdatedMapper<ChatMemberUpdated>? = null
) : List<ChatMemberUpdated> = waitChatMemberUpdated<T>(
count,
initRequest,
errorFactory,
filter,
) {
if (mapper == null) {
this
} else {
mapper(this)
}
}
suspend fun BehaviourContext.waitChatMemberUpdated(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatMemberUpdatedUpdate>? = null,
mapper: ChatMemberUpdatedMapper<ChatMemberUpdated>? = null
) = waitChatMemberUpdatedWithFilter<ChatMemberUpdatedUpdate>(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatMemberUpdatedUpdate>? = null
) = waitChatMemberUpdatedWithFilter(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitCommonChatMemberUpdated(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonChatMemberUpdatedUpdate>? = null,
mapper: ChatMemberUpdatedMapper<ChatMemberUpdated>? = null
) = waitChatMemberUpdatedWithFilter<CommonChatMemberUpdatedUpdate>(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<CommonChatMemberUpdatedUpdate>? = null
) = waitChatMemberUpdatedWithFilter(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitMyChatMemberUpdated(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<MyChatMemberUpdatedUpdate>? = null,
mapper: ChatMemberUpdatedMapper<ChatMemberUpdated>? = null
) = waitChatMemberUpdatedWithFilter<MyChatMemberUpdatedUpdate>(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<MyChatMemberUpdatedUpdate>? = null
) = waitChatMemberUpdatedWithFilter(count, initRequest, errorFactory, filter)

View File

@@ -9,73 +9,41 @@ import kotlinx.coroutines.flow.toList
typealias ChosenInlineResultMapper<T> = suspend T.() -> T?
private suspend fun <O> BehaviourContext.waitChosenInlineResultsUpdates(
private suspend inline fun <reified O> BehaviourContext.waitChosenInlineResults(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<ChosenInlineResult>? = null,
mapper: suspend ChosenInlineResult.() -> O?
noinline errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<O>? = null
): List<O> = expectFlow(
initRequest,
count,
errorFactory
) {
val data = it.asChosenInlineResultUpdate() ?.data
if (data != null && (filter == null || filter(data))) {
data.mapper().let(::listOfNotNull)
val data = it.asChosenInlineResultUpdate() ?.data as? O ?: return@expectFlow emptyList()
if (filter == null || filter(data)) {
listOf(data)
} else {
emptyList()
}
}.toList().toList()
private suspend inline fun <reified T : ChosenInlineResult> BehaviourContext.waitChosenInlineResults(
count: Int = 1,
initRequest: Request<*>? = null,
noinline errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<T>? = null,
noinline mapper: ChosenInlineResultMapper<T>? = null
) : List<T> = this@waitChosenInlineResults.waitChosenInlineResultsUpdates<T>(
count,
initRequest,
errorFactory,
filter ?.let {
{
(it as? T) ?.let { filter(it) } == true
}
}
) {
if (this is T) {
if (mapper == null) {
this
} else {
mapper(this)
}
} else {
null
}
}
suspend fun BehaviourContext.waitChosenInlineResult(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChosenInlineResult>? = null,
mapper: ChosenInlineResultMapper<ChosenInlineResult>? = null
) = waitChosenInlineResults(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChosenInlineResult>? = null
) = waitChosenInlineResults(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitLocationChosenInlineResult(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<LocationChosenInlineResult>? = null,
mapper: PollMapper<LocationChosenInlineResult>? = null
) = waitChosenInlineResults(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<LocationChosenInlineResult>? = null
) = waitChosenInlineResults(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitBaseChosenInlineResult(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<BaseChosenInlineResult>? = null,
mapper: PollMapper<BaseChosenInlineResult>? = null
) = waitChosenInlineResults(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<BaseChosenInlineResult>? = null
) = waitChosenInlineResults(count, initRequest, errorFactory, filter)

View File

@@ -2,7 +2,6 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder.expectations
import dev.inmo.micro_utils.coroutines.safelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.SimpleFilter
import dev.inmo.tgbotapi.extensions.utils.withContent
@@ -17,17 +16,15 @@ import dev.inmo.tgbotapi.types.message.content.InvoiceContent
import dev.inmo.tgbotapi.types.update.media_group.SentMediaGroupUpdate
import dev.inmo.tgbotapi.types.update.abstracts.BaseSentMessageUpdate
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.toList
typealias CommonMessageToContentMapper<T> = suspend CommonMessage<T>.() -> T?
private suspend fun <O> BehaviourContext.waitCommonContent(
private suspend inline fun <reified O : MessageContent> BehaviourContext.waitContent(
count: Int = 1,
initRequest: Request<*>? = null,
includeMediaGroups: Boolean = true,
errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<CommonMessage<MessageContent>>? = null,
mapper: suspend CommonMessage<MessageContent>.() -> O?
noinline errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<CommonMessage<O>>? = null
): Flow<O> = expectFlow(
initRequest,
count,
@@ -36,7 +33,7 @@ private suspend fun <O> BehaviourContext.waitCommonContent(
val messages = when (it) {
is SentMediaGroupUpdate -> {
if (includeMediaGroups) {
it.data.map { it as CommonMessage<MessageContent> }
it.data
} else {
emptyList()
}
@@ -45,233 +42,174 @@ private suspend fun <O> BehaviourContext.waitCommonContent(
else -> return@expectFlow emptyList()
}
messages.mapNotNull { message ->
val asCommonMessage = message as CommonMessage<MessageContent>
val asCommonMessage = (message as CommonMessage<*>).withContent<O>() ?: return@mapNotNull null
if (filter == null || filter(asCommonMessage)) {
asCommonMessage.mapper()
asCommonMessage.content
} else {
null
}
}
}
internal inline fun <reified T : MessageContent> contentConverter(
noinline mapper: CommonMessageToContentMapper<T>? = null
): suspend CommonMessage<MessageContent>.() -> T? = mapper ?.let {
{
if (content is T) {
@Suppress("UNCHECKED_CAST")
val message = (this as CommonMessage<T>)
safelyWithoutExceptions { mapper(message) }
} else {
null
}
}
} ?: { content as? T }
private suspend inline fun <reified T : MessageContent> BehaviourContext.waitContent(
count: Int = 1,
initRequest: Request<*>? = null,
includeMediaGroups: Boolean = true,
noinline errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<CommonMessage<T>>? = null,
noinline mapper: CommonMessageToContentMapper<T>? = null
) : List<T> = waitCommonContent<T>(
count,
initRequest,
includeMediaGroups,
errorFactory,
filter ?.let {
{
it.withContent<T>() ?.let { filter(it) } == true
}
},
contentConverter(mapper)
).toList()
suspend fun BehaviourContext.waitContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<MessageContent>>? = null,
mapper: CommonMessageToContentMapper<MessageContent>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<MessageContent>>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitContact(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<ContactContent>>? = null,
mapper: CommonMessageToContentMapper<ContactContent>? = null
) = waitContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<ContactContent>>? = null
) = waitContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitDice(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<DiceContent>>? = null,
mapper: CommonMessageToContentMapper<DiceContent>? = null
) = waitContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<DiceContent>>? = null
) = waitContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitGame(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<GameContent>>? = null,
mapper: CommonMessageToContentMapper<GameContent>? = null
) = waitContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<GameContent>>? = null
) = waitContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitLocation(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<LocationContent>>? = null,
mapper: CommonMessageToContentMapper<LocationContent>? = null
) = waitContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<LocationContent>>? = null
) = waitContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitLiveLocation(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<LiveLocationContent>>? = null,
mapper: CommonMessageToContentMapper<LiveLocationContent>? = null
) = waitContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<LiveLocationContent>>? = null
) = waitContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitStaticLocation(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<StaticLocationContent>>? = null,
mapper: CommonMessageToContentMapper<StaticLocationContent>? = null
) = waitContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<StaticLocationContent>>? = null
) = waitContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitPoll(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<PollContent>>? = null,
mapper: CommonMessageToContentMapper<PollContent>? = null
) = waitContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<PollContent>>? = null
) = waitContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitText(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<TextContent>>? = null,
mapper: CommonMessageToContentMapper<TextContent>? = null
) = waitContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<TextContent>>? = null
) = waitContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitVenue(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<VenueContent>>? = null,
mapper: CommonMessageToContentMapper<VenueContent>? = null
) = waitContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<VenueContent>>? = null
) = waitContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitAudioMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<AudioMediaGroupContent>>? = null,
mapper: CommonMessageToContentMapper<AudioMediaGroupContent>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<AudioMediaGroupContent>>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitDocumentMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<DocumentMediaGroupContent>>? = null,
mapper: CommonMessageToContentMapper<DocumentMediaGroupContent>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<DocumentMediaGroupContent>>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitMedia(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = false,
filter: SimpleFilter<CommonMessage<MediaContent>>? = null,
mapper: CommonMessageToContentMapper<MediaContent>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<MediaContent>>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitAnyMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<MediaGroupContent>>? = null,
mapper: CommonMessageToContentMapper<MediaGroupContent>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<MediaGroupContent>>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitVisualMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<VisualMediaGroupContent>>? = null,
mapper: CommonMessageToContentMapper<VisualMediaGroupContent>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<VisualMediaGroupContent>>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitTextedMediaContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<TextedMediaContent>>? = null,
mapper: CommonMessageToContentMapper<TextedMediaContent>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<TextedMediaContent>>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitAnimation(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<AnimationContent>>? = null,
mapper: CommonMessageToContentMapper<AnimationContent>? = null
) = waitContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<AnimationContent>>? = null
) = waitContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitAudio(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = false,
filter: SimpleFilter<CommonMessage<AudioContent>>? = null,
mapper: CommonMessageToContentMapper<AudioContent>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<AudioContent>>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitDocument(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = false,
filter: SimpleFilter<CommonMessage<DocumentContent>>? = null,
mapper: CommonMessageToContentMapper<DocumentContent>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<DocumentContent>>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitPhoto(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = false,
filter: SimpleFilter<CommonMessage<PhotoContent>>? = null,
mapper: CommonMessageToContentMapper<PhotoContent>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<PhotoContent>>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitSticker(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<StickerContent>>? = null,
mapper: CommonMessageToContentMapper<StickerContent>? = null
) = waitContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<StickerContent>>? = null
) = waitContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitVideo(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = false,
filter: SimpleFilter<CommonMessage<VideoContent>>? = null,
mapper: CommonMessageToContentMapper<VideoContent>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<VideoContent>>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitVideoNote(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<VideoNoteContent>>? = null,
mapper: CommonMessageToContentMapper<VideoNoteContent>? = null
) = waitContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<VideoNoteContent>>? = null
) = waitContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitVoice(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<VoiceContent>>? = null,
mapper: CommonMessageToContentMapper<VoiceContent>? = null
) = waitContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<VoiceContent>>? = null
) = waitContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitInvoice(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<InvoiceContent>>? = null,
mapper: CommonMessageToContentMapper<InvoiceContent>? = null
) = waitContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<InvoiceContent>>? = null
) = waitContent(count, initRequest, false, errorFactory, filter)

View File

@@ -17,17 +17,15 @@ import dev.inmo.tgbotapi.types.message.content.InvoiceContent
import dev.inmo.tgbotapi.types.update.media_group.SentMediaGroupUpdate
import dev.inmo.tgbotapi.types.update.abstracts.BaseSentMessageUpdate
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.toList
typealias CommonMessageToCommonMessageMapper<T> = suspend CommonMessage<T>.() -> CommonMessage<T>?
internal suspend fun <O : MessageContent> BehaviourContext.waitCommonMessage(
internal suspend inline fun <reified O : MessageContent> BehaviourContext.waitContentMessage(
count: Int = 1,
initRequest: Request<*>? = null,
includeMediaGroups: Boolean = true,
errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<CommonMessage<MessageContent>>? = null,
mapper: suspend CommonMessage<MessageContent>.() -> CommonMessage<O>?
noinline errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<CommonMessage<O>>? = null
): Flow<CommonMessage<O>> = expectFlow(
initRequest,
count,
@@ -46,9 +44,9 @@ internal suspend fun <O : MessageContent> BehaviourContext.waitCommonMessage(
}
messages.mapNotNull { message ->
@Suppress("UNCHECKED_CAST")
val asCommonMessage = message as? CommonMessage<MessageContent> ?: return@mapNotNull null
val asCommonMessage = (message as? CommonMessage<*>) ?.withContent<O>() ?: return@mapNotNull null
if (filter == null || filter(asCommonMessage)) {
asCommonMessage.mapper()
asCommonMessage
} else {
null
}
@@ -72,209 +70,164 @@ internal inline fun <reified T : MessageContent> contentMessageConverter(
if (content is T) this as CommonMessage<T> else null
}
private suspend inline fun <reified T : MessageContent> BehaviourContext.waitContentMessage(
count: Int = 1,
initRequest: Request<*>? = null,
includeMediaGroups: Boolean = true,
noinline errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<CommonMessage<T>>? = null,
noinline mapper: CommonMessageToCommonMessageMapper<T>? = null
) : List<CommonMessage<T>> = waitCommonMessage<T>(
count,
initRequest,
includeMediaGroups,
errorFactory,
filter ?.let {
{
it.withContent<T>() ?.let { filter(it) } == true
}
},
contentMessageConverter(mapper)
).toList()
suspend fun BehaviourContext.waitContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<MessageContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<MessageContent>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<MessageContent>>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitContactMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<ContactContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<ContactContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<ContactContent>>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitDiceMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<DiceContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<DiceContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<DiceContent>>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitGameMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<GameContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<GameContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<GameContent>>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitLocationMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<LocationContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<LocationContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<LocationContent>>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitLiveLocationMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<LiveLocationContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<LiveLocationContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<LiveLocationContent>>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitStaticLocationMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<StaticLocationContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<StaticLocationContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<StaticLocationContent>>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitPollMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<PollContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<PollContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<PollContent>>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitTextMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<TextContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<TextContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<TextContent>>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitVenueMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<VenueContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<VenueContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<VenueContent>>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitAudioMediaGroupContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<AudioMediaGroupContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<AudioMediaGroupContent>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<AudioMediaGroupContent>>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitDocumentMediaGroupContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<DocumentMediaGroupContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<DocumentMediaGroupContent>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<DocumentMediaGroupContent>>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitMediaMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = false,
filter: SimpleFilter<CommonMessage<MediaContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<MediaContent>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<MediaContent>>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitAnyMediaGroupContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<MediaGroupContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<MediaGroupContent>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<MediaGroupContent>>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitVisualMediaGroupContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<VisualMediaGroupContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<VisualMediaGroupContent>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<VisualMediaGroupContent>>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitTextedMediaContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<TextedMediaContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<TextedMediaContent>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<TextedMediaContent>>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitAnimationMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<AnimationContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<AnimationContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<AnimationContent>>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitAudioMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = false,
filter: SimpleFilter<CommonMessage<AudioContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<AudioContent>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<AudioContent>>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitDocumentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = false,
filter: SimpleFilter<CommonMessage<DocumentContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<DocumentContent>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<DocumentContent>>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitPhotoMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = false,
filter: SimpleFilter<CommonMessage<PhotoContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<PhotoContent>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<PhotoContent>>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitStickerMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<StickerContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<StickerContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<StickerContent>>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitVideoMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = false,
filter: SimpleFilter<CommonMessage<VideoContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<VideoContent>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<VideoContent>>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitVideoNoteMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<VideoNoteContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<VideoNoteContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<VideoNoteContent>>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitVoiceMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<VoiceContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<VoiceContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<VoiceContent>>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitInvoiceMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<InvoiceContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<InvoiceContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<InvoiceContent>>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter)

View File

@@ -18,13 +18,12 @@ import dev.inmo.tgbotapi.types.message.content.InvoiceContent
import dev.inmo.tgbotapi.types.update.abstracts.BaseEditMessageUpdate
import kotlinx.coroutines.flow.toList
private suspend fun <O> BehaviourContext.waitEditedCommonMessage(
private suspend inline fun <reified O : MessageContent> BehaviourContext.waitEditedContent(
count: Int = 1,
initRequest: Request<*>? = null,
includeMediaGroups: Boolean = true,
errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<CommonMessage<MessageContent>>? = null,
mapper: suspend CommonMessage<MessageContent>.() -> O?
noinline errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<CommonMessage<O>>? = null
): List<O> = expectFlow(
initRequest,
count,
@@ -32,7 +31,7 @@ private suspend fun <O> BehaviourContext.waitEditedCommonMessage(
) {
val messages = when (it) {
is BaseEditMessageUpdate -> {
val commonMessage = it.data.asCommonMessage()
val commonMessage = it.data.asCommonMessage() ?: return@expectFlow emptyList()
if (commonMessage !is MediaGroupMessage<*> || includeMediaGroups) {
listOf(commonMessage)
} else {
@@ -42,211 +41,167 @@ private suspend fun <O> BehaviourContext.waitEditedCommonMessage(
else -> return@expectFlow emptyList()
}
messages.mapNotNull { message ->
val asCommonMessage = message as CommonMessage<MessageContent>
val asCommonMessage = (message as CommonMessage<*>).withContent<O>() ?: return@mapNotNull null
if (filter == null || filter(asCommonMessage)) {
asCommonMessage.mapper()
asCommonMessage.content
} else {
null
}
}
}.toList().toList()
private suspend inline fun <reified T : MessageContent> BehaviourContext.waitEditedContent(
count: Int = 1,
initRequest: Request<*>? = null,
includeMediaGroups: Boolean = true,
noinline errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<CommonMessage<T>>? = null,
noinline mapper: CommonMessageToContentMapper<T>? = null
) : List<T> = waitEditedCommonMessage<T>(
count,
initRequest,
includeMediaGroups,
errorFactory,
filter ?.let {
{
it.withContent<T>() ?.let { filter(it) } == true
}
},
contentConverter(mapper)
)
suspend fun BehaviourContext.waitEditedContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<MessageContent>>? = null,
mapper: CommonMessageToContentMapper<MessageContent>? = null
) = waitEditedContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<MessageContent>>? = null
) = waitEditedContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitEditedContact(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<ContactContent>>? = null,
mapper: CommonMessageToContentMapper<ContactContent>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<ContactContent>>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitEditedDice(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<DiceContent>>? = null,
mapper: CommonMessageToContentMapper<DiceContent>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<DiceContent>>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitEditedGame(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<GameContent>>? = null,
mapper: CommonMessageToContentMapper<GameContent>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<GameContent>>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitEditedLocation(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<LocationContent>>? = null,
mapper: CommonMessageToContentMapper<LocationContent>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<LocationContent>>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitEditedLiveLocation(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<LiveLocationContent>>? = null,
mapper: CommonMessageToContentMapper<LiveLocationContent>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<LiveLocationContent>>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitEditedStaticLocation(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<StaticLocationContent>>? = null,
mapper: CommonMessageToContentMapper<StaticLocationContent>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<StaticLocationContent>>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitEditedText(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<TextContent>>? = null,
mapper: CommonMessageToContentMapper<TextContent>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<TextContent>>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitEditedVenue(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<VenueContent>>? = null,
mapper: CommonMessageToContentMapper<VenueContent>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<VenueContent>>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitEditedAudioMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<AudioMediaGroupContent>>? = null,
mapper: CommonMessageToContentMapper<AudioMediaGroupContent>? = null
) = waitEditedContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<AudioMediaGroupContent>>? = null
) = waitEditedContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitEditedDocumentMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<DocumentMediaGroupContent>>? = null,
mapper: CommonMessageToContentMapper<DocumentMediaGroupContent>? = null
) = waitEditedContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<DocumentMediaGroupContent>>? = null
) = waitEditedContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitEditedMedia(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = false,
filter: SimpleFilter<CommonMessage<MediaContent>>? = null,
mapper: CommonMessageToContentMapper<MediaContent>? = null
) = waitEditedContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<MediaContent>>? = null
) = waitEditedContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitEditedAnyMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<MediaGroupContent>>? = null,
mapper: CommonMessageToContentMapper<MediaGroupContent>? = null
) = waitEditedContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<MediaGroupContent>>? = null
) = waitEditedContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitEditedVisualMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<VisualMediaGroupContent>>? = null,
mapper: CommonMessageToContentMapper<VisualMediaGroupContent>? = null
) = waitEditedContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<VisualMediaGroupContent>>? = null
) = waitEditedContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitEditedTextedMediaContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<TextedMediaContent>>? = null,
mapper: CommonMessageToContentMapper<TextedMediaContent>? = null
) = waitEditedContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<TextedMediaContent>>? = null
) = waitEditedContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitEditedAnimation(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<AnimationContent>>? = null,
mapper: CommonMessageToContentMapper<AnimationContent>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<AnimationContent>>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitEditedAudio(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = false,
filter: SimpleFilter<CommonMessage<AudioContent>>? = null,
mapper: CommonMessageToContentMapper<AudioContent>? = null
) = waitEditedContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<AudioContent>>? = null
) = waitEditedContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitEditedDocument(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = false,
filter: SimpleFilter<CommonMessage<DocumentContent>>? = null,
mapper: CommonMessageToContentMapper<DocumentContent>? = null
) = waitEditedContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<DocumentContent>>? = null
) = waitEditedContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitEditedPhoto(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = false,
filter: SimpleFilter<CommonMessage<PhotoContent>>? = null,
mapper: CommonMessageToContentMapper<PhotoContent>? = null
) = waitEditedContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<PhotoContent>>? = null
) = waitEditedContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitEditedSticker(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<StickerContent>>? = null,
mapper: CommonMessageToContentMapper<StickerContent>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<StickerContent>>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitEditedVideo(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = false,
filter: SimpleFilter<CommonMessage<VideoContent>>? = null,
mapper: CommonMessageToContentMapper<VideoContent>? = null
) = waitEditedContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<VideoContent>>? = null
) = waitEditedContent(count, initRequest, includeMediaGroups, errorFactory, filter)
suspend fun BehaviourContext.waitEditedVideoNote(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<VideoNoteContent>>? = null,
mapper: CommonMessageToContentMapper<VideoNoteContent>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<VideoNoteContent>>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitEditedVoice(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<VoiceContent>>? = null,
mapper: CommonMessageToContentMapper<VoiceContent>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<VoiceContent>>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter)
suspend fun BehaviourContext.waitEditedInvoice(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<InvoiceContent>>? = null,
mapper: CommonMessageToContentMapper<InvoiceContent>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter, mapper)
filter: SimpleFilter<CommonMessage<InvoiceContent>>? = null
) = waitEditedContent(count, initRequest, false, errorFactory, filter)

View File

@@ -4,8 +4,7 @@ package dev.inmo.tgbotapi.extensions.behaviour_builder.expectations
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.SimpleFilter
import dev.inmo.tgbotapi.extensions.utils.asBaseSentMessageUpdate
import dev.inmo.tgbotapi.extensions.utils.asChatEventMessage
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.*
@@ -17,268 +16,208 @@ import kotlinx.coroutines.flow.toList
typealias EventMessageToEventMapper<T> = suspend ChatEventMessage<T>.() -> T?
private suspend fun <O> BehaviourContext.waitEventMessages(
private suspend inline fun <reified O : ChatEvent> BehaviourContext.waitEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
noinline errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<ChatEvent>>? = null,
mapper: suspend ChatEventMessage<ChatEvent>.() -> O?
filter: SimpleFilter<ChatEventMessage<O>>? = null
): List<O> = expectFlow(
initRequest,
count,
errorFactory
) {
val data = it.asBaseSentMessageUpdate() ?.data ?.asChatEventMessage()
if (data != null && (filter == null || filter(data))) {
data.mapper().let(::listOfNotNull)
val data = it.asBaseSentMessageUpdate() ?.data ?.asChatEventMessage() ?.withEvent<O>() ?: return@expectFlow emptyList()
if (filter == null || filter(data)) {
listOf(data.chatEvent)
} else {
emptyList()
}
}.toList().toList()
private suspend inline fun <reified T : ChatEvent> BehaviourContext.waitEvents(
count: Int = 1,
initRequest: Request<*>? = null,
noinline errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<ChatEventMessage<T>>? = null,
noinline mapper: EventMessageToEventMapper<T>? = null
) : List<T> = waitEventMessages<T>(
initRequest,
errorFactory,
count,
filter ?.let {
{
(it.chatEvent as? T) ?.let { filter(it as ChatEventMessage<T>) } == true
}
}
) {
if (chatEvent is T) {
@Suppress("UNCHECKED_CAST")
val message = (this as ChatEventMessage<T>)
if (mapper == null) {
message.chatEvent
} else {
mapper(message)
}
} else {
null
}
}
suspend fun BehaviourContext.waitChannelEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<ChannelEvent>>? = null,
mapper: EventMessageToEventMapper<ChannelEvent>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<ChannelEvent>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
suspend fun BehaviourContext.waitPrivateEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<PrivateEvent>>? = null,
mapper: EventMessageToEventMapper<PrivateEvent>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<PrivateEvent>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
suspend fun BehaviourContext.waitChatEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<ChatEvent>>? = null,
mapper: EventMessageToEventMapper<ChatEvent>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<ChatEvent>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
@Deprecated("Renamed as Video instead of Voice")
suspend fun BehaviourContext.waitVoiceChatEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<VideoChatEvent>>? = null,
mapper: EventMessageToEventMapper<VideoChatEvent>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<VideoChatEvent>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
@Deprecated("Renamed as Video instead of Voice")
suspend fun BehaviourContext.waitVoiceChatStartedEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<VideoChatStarted>>? = null,
mapper: EventMessageToEventMapper<VideoChatStarted>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<VideoChatStarted>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
@Deprecated("Renamed as Video instead of Voice")
suspend fun BehaviourContext.waitVoiceChatEndedEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<VideoChatEnded>>? = null,
mapper: EventMessageToEventMapper<VideoChatEnded>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<VideoChatEnded>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
@Deprecated("Renamed as Video instead of Voice")
suspend fun BehaviourContext.waitVoiceChatParticipantsInvitedEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<VideoChatParticipantsInvited>>? = null,
mapper: EventMessageToEventMapper<VideoChatParticipantsInvited>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<VideoChatParticipantsInvited>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
suspend fun BehaviourContext.waitVideoChatEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<VideoChatEvent>>? = null,
mapper: EventMessageToEventMapper<VideoChatEvent>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<VideoChatEvent>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
suspend fun BehaviourContext.waitVideoChatStartedEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<VideoChatStarted>>? = null,
mapper: EventMessageToEventMapper<VideoChatStarted>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<VideoChatStarted>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
suspend fun BehaviourContext.waitVideoChatEndedEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<VideoChatEnded>>? = null,
mapper: EventMessageToEventMapper<VideoChatEnded>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<VideoChatEnded>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
suspend fun BehaviourContext.waitVideoChatParticipantsInvitedEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<VideoChatParticipantsInvited>>? = null,
mapper: EventMessageToEventMapper<VideoChatParticipantsInvited>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<VideoChatParticipantsInvited>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
suspend fun BehaviourContext.waitMessageAutoDeleteTimerChangedEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<MessageAutoDeleteTimerChanged>>? = null,
mapper: EventMessageToEventMapper<MessageAutoDeleteTimerChanged>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<MessageAutoDeleteTimerChanged>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
suspend fun BehaviourContext.waitPublicChatEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<PublicChatEvent>>? = null,
mapper: EventMessageToEventMapper<PublicChatEvent>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<PublicChatEvent>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
suspend fun BehaviourContext.waitCommonEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<CommonEvent>>? = null,
mapper: EventMessageToEventMapper<CommonEvent>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<CommonEvent>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
suspend fun BehaviourContext.waitGroupEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<GroupEvent>>? = null,
mapper: EventMessageToEventMapper<GroupEvent>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<GroupEvent>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
suspend fun BehaviourContext.waitSupergroupEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<SupergroupEvent>>? = null,
mapper: EventMessageToEventMapper<SupergroupEvent>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<SupergroupEvent>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
suspend fun BehaviourContext.waitChannelChatCreatedEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<ChannelChatCreated>>? = null,
mapper: EventMessageToEventMapper<ChannelChatCreated>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<ChannelChatCreated>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
suspend fun BehaviourContext.waitDeleteChatPhotoEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<DeleteChatPhoto>>? = null,
mapper: EventMessageToEventMapper<DeleteChatPhoto>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<DeleteChatPhoto>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
suspend fun BehaviourContext.waitGroupChatCreatedEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<GroupChatCreated>>? = null,
mapper: EventMessageToEventMapper<GroupChatCreated>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<GroupChatCreated>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
suspend fun BehaviourContext.waitLeftChatMemberEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<LeftChatMember>>? = null,
mapper: EventMessageToEventMapper<LeftChatMember>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<LeftChatMember>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
suspend fun BehaviourContext.waitNewChatPhotoEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<NewChatPhoto>>? = null,
mapper: EventMessageToEventMapper<NewChatPhoto>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<NewChatPhoto>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
suspend fun BehaviourContext.waitNewChatMembersEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<NewChatMembers>>? = null,
mapper: EventMessageToEventMapper<NewChatMembers>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<NewChatMembers>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
suspend fun BehaviourContext.waitNewChatTitleEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<NewChatTitle>>? = null,
mapper: EventMessageToEventMapper<NewChatTitle>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<NewChatTitle>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
suspend fun BehaviourContext.waitPinnedMessageEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<PinnedMessage>>? = null,
mapper: EventMessageToEventMapper<PinnedMessage>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<PinnedMessage>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
suspend fun BehaviourContext.waitProximityAlertTriggeredEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<ProximityAlertTriggered>>? = null,
mapper: EventMessageToEventMapper<ProximityAlertTriggered>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<ProximityAlertTriggered>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
suspend fun BehaviourContext.waitSupergroupChatCreatedEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<SupergroupChatCreated>>? = null,
mapper: EventMessageToEventMapper<SupergroupChatCreated>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<SupergroupChatCreated>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
suspend fun BehaviourContext.waitSuccessfulPaymentEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<SuccessfulPaymentEvent>>? = null,
mapper: EventMessageToEventMapper<SuccessfulPaymentEvent>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<SuccessfulPaymentEvent>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
suspend fun BehaviourContext.waitUserLoggedInEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ChatEventMessage<UserLoggedIn>>? = null,
mapper: EventMessageToEventMapper<UserLoggedIn>? = null
) = waitEvents(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<ChatEventMessage<UserLoggedIn>>? = null
) = waitEvents(initRequest, errorFactory, count, filter)
suspend fun BehaviourContext.waitWebAppDataEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<PrivateEventMessage<WebAppData>>? = null,
mapper: EventMessageToEventMapper<WebAppData>? = null
) = waitEvents(count, initRequest, errorFactory, filter ?.let { { it is PrivateEventMessage && filter(it) } }, mapper)
filter: SimpleFilter<PrivateEventMessage<WebAppData>>? = null
) = waitEvents(initRequest, errorFactory, count, filter ?.let { SimpleFilter<ChatEventMessage<WebAppData>> { it is PrivateEventMessage && filter(it) } })

View File

@@ -9,72 +9,40 @@ import kotlinx.coroutines.flow.toList
typealias InlineQueryMapper<T> = suspend T.() -> T?
private suspend fun <O> BehaviourContext.waitInlineQueries(
private suspend inline fun <reified O : InlineQuery> BehaviourContext.waitInlineQueries(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<InlineQuery>? = null,
mapper: suspend InlineQuery.() -> O?
noinline errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<O>? = null
): List<O> = expectFlow(
initRequest,
count,
errorFactory
) {
val data = it.asInlineQueryUpdate() ?.data
if (data != null && (filter == null || filter(data))) {
data.mapper().let(::listOfNotNull)
val data = (it.asInlineQueryUpdate() ?.data as? O) ?: return@expectFlow emptyList()
if (filter == null || filter(data)) {
listOf(data)
} else {
emptyList()
}
}.toList().toList()
private suspend inline fun <reified T : InlineQuery> BehaviourContext.waitInlines(
count: Int = 1,
initRequest: Request<*>? = null,
noinline errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<T>? = null,
noinline mapper: InlineQueryMapper<T>? = null
) : List<T> = waitInlineQueries<T>(
count,
initRequest,
errorFactory,
filter ?.let {
{
(it as? T) ?.let { casted -> filter(casted) } == true
}
}
) {
if (this is T) {
if (mapper == null) {
this
} else {
mapper(this)
}
} else {
null
}
}
suspend fun BehaviourContext.waitAnyInlineQuery(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<InlineQuery>? = null,
mapper: InlineQueryMapper<InlineQuery>? = null
) = waitInlines(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<InlineQuery>? = null
) = waitInlineQueries(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitBaseInlineQuery(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<BaseInlineQuery>? = null,
mapper: InlineQueryMapper<BaseInlineQuery>? = null
) = waitInlines(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<BaseInlineQuery>? = null
) = waitInlineQueries(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitLocationInlineQuery(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<LocationInlineQuery>? = null,
mapper: InlineQueryMapper<LocationInlineQuery>? = null
) = waitInlines(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<LocationInlineQuery>? = null
) = waitInlineQueries(count, initRequest, errorFactory, filter)

View File

@@ -1,7 +1,9 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder.expectations
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.SimpleFilter
import dev.inmo.tgbotapi.extensions.utils.asSentMediaGroupUpdate
import dev.inmo.tgbotapi.extensions.utils.withContent
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.types.message.abstracts.MediaGroupMessage
import dev.inmo.tgbotapi.types.message.content.*
@@ -9,20 +11,20 @@ import dev.inmo.tgbotapi.types.message.content.AudioMediaGroupContent
import dev.inmo.tgbotapi.types.message.content.DocumentMediaGroupContent
import dev.inmo.tgbotapi.types.message.content.MediaGroupContent
import dev.inmo.tgbotapi.types.message.content.VisualMediaGroupContent
import dev.inmo.tgbotapi.utils.PreviewFeature
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.flow.toList
typealias MediaGroupFilter<T> = suspend List<MediaGroupMessage<T>>.() -> Boolean
typealias MediaGroupFilter<T> = SimpleFilter<List<MediaGroupMessage<T>>>
internal suspend inline fun <reified T : MediaGroupContent> BehaviourContext.buildMediaGroupWaiter(
count: Int = 1,
initRequest: Request<*>? = null,
noinline errorFactory: NullableRequestBuilder<*> = { null },
noinline filter: MediaGroupFilter<T>? = null
filter: SimpleFilter<List<MediaGroupMessage<T>>>? = null
) = flowsUpdatesFilter.expectFlow(bot, initRequest, count, errorFactory) { update ->
update.asSentMediaGroupUpdate() ?.data ?.let { mediaGroup ->
if (mediaGroup.all { message -> message.content is T } && (filter == null || filter(mediaGroup as List<MediaGroupMessage<T>>))) {
val mapped = mediaGroup.mapNotNull { it.withContent<T>() }
if (mediaGroup.all { message -> message.content is T } && (filter == null || filter(mapped))) {
listOf(
mediaGroup.map { it.content as T }
)
@@ -36,35 +38,35 @@ suspend fun BehaviourContext.waitMediaGroup(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: MediaGroupFilter<MediaGroupContent>? = null
filter: SimpleFilter<List<MediaGroupMessage<MediaGroupContent>>>? = null
) = buildMediaGroupWaiter(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitPlaylist(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: MediaGroupFilter<AudioMediaGroupContent>? = null
filter: SimpleFilter<List<MediaGroupMessage<AudioMediaGroupContent>>>? = null
) = buildMediaGroupWaiter(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitDocumentsGroup(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: MediaGroupFilter<DocumentMediaGroupContent>? = null
filter: SimpleFilter<List<MediaGroupMessage<DocumentMediaGroupContent>>>? = null
) = buildMediaGroupWaiter(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitVisualGallery(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: MediaGroupFilter<VisualMediaGroupContent>? = null
filter: SimpleFilter<List<MediaGroupMessage<VisualMediaGroupContent>>>? = null
) = buildMediaGroupWaiter(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitPhotoGallery(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: MediaGroupFilter<PhotoContent>? = null
filter: SimpleFilter<List<MediaGroupMessage<PhotoContent>>>? = null
) = buildMediaGroupWaiter(count, initRequest, errorFactory, filter)
suspend fun BehaviourContext.waitVideoGallery(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: MediaGroupFilter<VideoContent>? = null
filter: SimpleFilter<List<MediaGroupMessage<VideoContent>>>? = null
) = buildMediaGroupWaiter(count, initRequest, errorFactory, filter)

View File

@@ -14,12 +14,11 @@ import kotlinx.coroutines.flow.toList
typealias PassportMessageMapper = suspend PassportMessage.() -> PassportData
@RiskFeature("Do not use this message directly, use waitPassportMessagesWith or waitAnyPassportMessages instead")
suspend fun <O> BehaviourContext.waitPassportMessages(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
suspend inline fun <reified O : EncryptedPassportElement> BehaviourContext.waitPassportMessagesWith(
count: Int = 1,
filter: SimpleFilter<PassportMessage>? = null,
mapper: suspend PassportMessage.() -> O?
initRequest: Request<*>? = null,
noinline errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<PassportMessage>? = null
): List<O> = expectFlow(
initRequest,
count,
@@ -27,39 +26,15 @@ suspend fun <O> BehaviourContext.waitPassportMessages(
) {
val data = it.asMessageUpdate() ?.data ?.asPassportMessage() ?: return@expectFlow emptyList()
if (filter == null || filter(data)) {
data.mapper().let(::listOfNotNull)
data.passportData.data.filterIsInstance<O>()
} else {
emptyList()
}
}.toList().toList()
suspend inline fun <reified T : EncryptedPassportElement> BehaviourContext.waitPassportMessagesWith(
count: Int = 1,
initRequest: Request<*>? = null,
noinline errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<PassportMessage>? = null,
noinline mapper: PassportMessageMapper? = null
) : List<PassportData> = waitPassportMessages(
initRequest,
errorFactory,
count,
filter
) {
if (passportData.data.any { it is T }) {
if (mapper == null) {
passportData
} else {
mapper(this)
}
} else {
null
}
}
suspend fun BehaviourContext.waitAnyPassportMessages(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<PassportMessage>? = null,
mapper: PassportMessageMapper? = null
) = waitPassportMessagesWith<EncryptedPassportElement>(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<PassportMessage>? = null
) = waitPassportMessagesWith<EncryptedPassportElement>(count, initRequest, errorFactory, filter)

View File

@@ -9,49 +9,24 @@ import kotlinx.coroutines.flow.toList
typealias PollAnswerMapper = suspend PollAnswer.() -> PollAnswer?
private suspend fun <O> BehaviourContext.waitPollsAnswers(
private suspend inline fun <reified O : PollAnswer> BehaviourContext.waitPollAnswers(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<PollAnswer>? = null,
mapper: suspend PollAnswer.() -> O?
noinline errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<O>? = null
): List<O> = expectFlow(
initRequest,
count,
errorFactory
) {
val data = it.asPollAnswerUpdate() ?.data
if (data != null && (filter == null || filter(data))) {
data.mapper().let(::listOfNotNull)
val data = it.asPollAnswerUpdate() ?.data as? O ?: return@expectFlow emptyList()
if (filter == null || filter(data)) {
listOf(data)
} else {
emptyList()
}
}.toList().toList()
private suspend inline fun BehaviourContext.waitPollAnswers(
count: Int = 1,
initRequest: Request<*>? = null,
noinline errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<PollAnswer>? = null,
noinline mapper: PollAnswerMapper? = null
) : List<PollAnswer> = this@waitPollAnswers.waitPollsAnswers<PollAnswer>(
count,
initRequest,
errorFactory,
filter ?.let {
{
(it as? PollAnswer) ?.let { filter(it) } == true
}
}
) {
if (mapper == null) {
this
} else {
mapper(this)
}
}
/**
* This wait will be triggered only for stopped polls and polls, which are sent by the bot
*/
@@ -59,6 +34,5 @@ suspend fun BehaviourContext.waitPollAnswers(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<PollAnswer>? = null,
mapper: PollAnswerMapper? = null
) = waitPollAnswers(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<PollAnswer>? = null
) = waitPollAnswers(count, initRequest, errorFactory, filter)

View File

@@ -9,53 +9,24 @@ import kotlinx.coroutines.flow.toList
typealias PollMapper<T> = suspend T.() -> T?
private suspend fun <O> BehaviourContext.waitPollsUpdates(
private suspend inline fun <reified O> BehaviourContext.waitPolls(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<Poll>? = null,
mapper: suspend Poll.() -> O?
noinline errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<O>? = null
): List<O> = expectFlow(
initRequest,
count,
errorFactory
) {
val data = it.asPollUpdate() ?.data
if (data != null && (filter == null || filter(data))) {
data.mapper().let(::listOfNotNull)
val data = it.asPollUpdate() ?.data as? O ?: return@expectFlow emptyList()
if (filter == null || filter(data)) {
listOf(data)
} else {
emptyList()
}
}.toList().toList()
private suspend inline fun <reified T : Poll> BehaviourContext.waitPolls(
count: Int = 1,
initRequest: Request<*>? = null,
noinline errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<T>? = null,
noinline mapper: PollMapper<T>? = null
) : List<T> = this@waitPolls.waitPollsUpdates<T>(
count,
initRequest,
errorFactory,
filter ?.let {
{
(it as? T) ?.let { filter(it) } == true
}
}
) {
if (this is T) {
if (mapper == null) {
this
} else {
mapper(this)
}
} else {
null
}
}
/**
* This wait will be triggered only for stopped polls and polls, which are sent by the bot
*/
@@ -63,9 +34,8 @@ suspend fun BehaviourContext.waitPollUpdates(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<Poll>? = null,
mapper: PollMapper<Poll>? = null
) = waitPolls(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<Poll>? = null
) = waitPolls(count, initRequest, errorFactory, filter)
/**
* This wait will be triggered only for stopped polls and polls, which are sent by the bot
@@ -74,9 +44,8 @@ suspend fun BehaviourContext.waitQuizPollUpdates(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<QuizPoll>? = null,
mapper: PollMapper<QuizPoll>? = null
) = waitPolls(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<QuizPoll>? = null
) = waitPolls(count, initRequest, errorFactory, filter)
/**
* This wait will be triggered only for stopped polls and polls, which are sent by the bot
@@ -85,6 +54,5 @@ suspend fun BehaviourContext.waitRegularPollUpdates(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<RegularPoll>? = null,
mapper: PollMapper<RegularPoll>? = null
) = waitPolls(count, initRequest, errorFactory, filter, mapper)
filter: SimpleFilter<RegularPoll>? = null
) = waitPolls(count, initRequest, errorFactory, filter)

View File

@@ -9,20 +9,19 @@ import kotlinx.coroutines.flow.toList
typealias PreCheckoutQueryMapper = suspend PreCheckoutQuery.() -> PreCheckoutQuery?
private suspend fun <O> BehaviourContext.waitPreCheckoutQueries(
private suspend inline fun <reified O : PreCheckoutQuery> BehaviourContext.waitPreCheckoutQueries(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<PreCheckoutQuery>? = null,
mapper: suspend PreCheckoutQuery.() -> O?
noinline errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<O>? = null
): List<O> = expectFlow(
initRequest,
count,
errorFactory
) {
val data = it.asPreCheckoutQueryUpdate() ?.data
if (data != null && (filter == null || filter(data))) {
data.mapper().let(::listOfNotNull)
val data = it.asPreCheckoutQueryUpdate() ?.data as? O ?: return@expectFlow emptyList()
if (filter == null || filter(data)) {
listOf(data)
} else {
emptyList()
}
@@ -33,17 +32,10 @@ suspend fun BehaviourContext.waitPreCheckoutQueries(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<PreCheckoutQuery>? = null,
mapper: PreCheckoutQueryMapper? = null
filter: SimpleFilter<PreCheckoutQuery>? = null
) : List<PreCheckoutQuery> = waitPreCheckoutQueries(
count,
initRequest,
errorFactory,
filter
) {
if (mapper == null) {
this
} else {
mapper(this)
}
}
)

View File

@@ -9,20 +9,19 @@ import kotlinx.coroutines.flow.toList
typealias ShippingQueryMapper = suspend ShippingQuery.() -> ShippingQuery?
private suspend fun <O> BehaviourContext.waitShippingQueries(
private suspend inline fun <reified O : ShippingQuery> BehaviourContext.waitShippingQueries(
count: Int = 1,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<ShippingQuery>? = null,
mapper: suspend ShippingQuery.() -> O?
noinline errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<O>? = null
): List<O> = expectFlow(
initRequest,
count,
errorFactory
) {
val data = it.asShippingQueryUpdate() ?.data
if (data != null && (filter == null || filter(data))) {
data.mapper().let(::listOfNotNull)
val data = it.asShippingQueryUpdate() ?.data as? O ?: return@expectFlow emptyList()
if (filter == null || filter(data)) {
listOf(data)
} else {
emptyList()
}
@@ -33,17 +32,10 @@ suspend fun BehaviourContext.waitShippingQueries(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<ShippingQuery>? = null,
mapper: ShippingQueryMapper? = null
filter: SimpleFilter<ShippingQuery>? = null
) : List<ShippingQuery> = waitShippingQueries(
count,
initRequest,
errorFactory,
filter
) {
if (mapper == null) {
this
} else {
mapper(this)
}
}
)

View File

@@ -34,7 +34,6 @@ import dev.inmo.tgbotapi.types.message.content.MediaContent
import dev.inmo.tgbotapi.types.message.content.MessageContent
import dev.inmo.tgbotapi.types.update.abstracts.BaseEditMessageUpdate
import dev.inmo.tgbotapi.types.update.abstracts.Update
import dev.inmo.tgbotapi.utils.PreviewFeature
internal suspend inline fun <BC : BehaviourContext, reified T : MessageContent> BC.onEditedContent(
initialFilter: CommonMessageFilter<T>? = null,

View File

@@ -15,16 +15,17 @@ internal suspend inline fun <BC : BehaviourContext, reified T> BC.on(
noinline updateToData: (Update) -> List<T>?
) = flowsUpdatesFilter.expectFlow(bot) {
updateToData(it) ?.mapNotNull { data ->
if (initialFilter ?.invoke(data) != false) data else null
if (initialFilter ?.invoke(data) != false) it to data else null
} ?: emptyList()
}.subscribeSafelyWithoutExceptionsAsync(
scope,
markerFactory::invoke
) { triggerData ->
{ markerFactory(it.second) }
) { (update, triggerData) ->
createSubContextAndDoWithUpdatesFilter(
updatesFilter = subcontextUpdatesFilter ?.toOneType(triggerData),
stopOnCompletion = false
) {
if (subcontextUpdatesFilter ?.invoke(this, triggerData, update) != false) {
scenarioReceiver(triggerData)
}
}
}

View File

@@ -47,10 +47,10 @@ kotlin {
api "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlin_serialisation_runtime_version"
api "org.jetbrains.kotlinx:kotlinx-serialization-properties:$kotlin_serialisation_runtime_version"
api "com.soywiz.korlibs.klock:klock:$klock_version"
api "com.soywiz.korlibs.klock:klock:$korlibs_version"
api "com.soywiz.korlibs.krypto:krypto:$korlibs_version"
api "com.benasher44:uuid:$uuid_version"
api "dev.inmo:micro_utils.crypto:$micro_utils_version"
api "dev.inmo:micro_utils.coroutines:$micro_utils_version"
api "dev.inmo:micro_utils.serialization.base64:$micro_utils_version"
api "dev.inmo:micro_utils.serialization.encapsulator:$micro_utils_version"

View File

@@ -1,7 +1,8 @@
package dev.inmo.tgbotapi.utils
import dev.inmo.micro_utils.crypto.hex
import dev.inmo.micro_utils.crypto.hmacSha256
import com.soywiz.krypto.*
import io.ktor.http.decodeURLQueryComponent
import io.ktor.utils.io.core.toByteArray
const val telegramBotAPIDefaultUrl = "https://api.telegram.org"
@@ -22,9 +23,11 @@ class TelegramAPIUrlsKeeper(
hostUrl: String = telegramBotAPIDefaultUrl,
urlsSuffixes: String = ""
) {
val webAppDataSecretKey by lazy {
token.hmacSha256("WebAppData")
val webAppDataSecretKeyHash by lazy {
HMAC.hmacSHA256("WebAppData".toByteArray(), token.toByteArray())
}
val webAppDataSecretKey
get() = webAppDataSecretKeyHash.hexLower
val commonAPIUrl: String
val fileBaseUrl: String
@@ -47,5 +50,21 @@ class TelegramAPIUrlsKeeper(
* @param rawData Data from [dev.inmo.tgbotapi.webapps.WebApp.initData]
* @param hash Data from [dev.inmo.tgbotapi.webapps.WebApp.initDataUnsafe] from the field [dev.inmo.tgbotapi.webapps.WebAppInitData.hash]
*/
fun checkWebAppLink(rawData: String, hash: String) = rawData.hmacSha256(webAppDataSecretKey).hex() == hash
fun checkWebAppData(rawData: String, hash: String): Boolean {
val preparedData = rawData
.decodeURLQueryComponent()
.split("&")
.filterNot { it.startsWith("hash=") }
.sorted()
.joinToString("\n")
return HMAC.hmacSHA256(webAppDataSecretKeyHash.bytes, preparedData.toByteArray()).hexLower == hash.lowercase()
}
/**
* @param rawData Data from [dev.inmo.tgbotapi.webapps.WebApp.initData]
* @param hash Data from [dev.inmo.tgbotapi.webapps.WebApp.initDataUnsafe] from the field [dev.inmo.tgbotapi.webapps.WebAppInitData.hash]
*/
@Deprecated("Renamed", ReplaceWith("checkWebAppData"))
inline fun checkWebAppLink(rawData: String, hash: String) = checkWebAppData(rawData, hash)
}

View File

@@ -0,0 +1,31 @@
@file:Suppress("UNCHECKED_CAST")
package dev.inmo.tgbotapi.extensions.utils
import dev.inmo.tgbotapi.types.message.*
import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.*
import dev.inmo.tgbotapi.types.message.abstracts.*
import dev.inmo.tgbotapi.types.message.content.MessageContent
import dev.inmo.tgbotapi.types.message.abstracts.PossiblySentViaBotCommonMessage
import dev.inmo.tgbotapi.types.message.content.MediaGroupContent
inline fun <reified T : ChatEvent> ChatEventMessage<*>.withEvent() = if (chatEvent is T) { this as ChatEventMessage<T> } else { null }
inline fun <reified T : ChatEvent> ChatEventMessage<*>.requireWithEvent() = withEvent<T>()!!
inline fun <reified T : GroupEvent> GroupEventMessage<*>.withEvent() = if (chatEvent is T) { this as GroupEventMessage<T> } else { null }
inline fun <reified T : GroupEvent> GroupEventMessage<*>.requireWithEvent() = withEvent<T>()!!
inline fun <reified T : SupergroupEvent> SupergroupEventMessage<*>.withEvent() = if (chatEvent is T) { this as SupergroupEventMessage<T> } else { null }
inline fun <reified T : SupergroupEvent> SupergroupEventMessage<*>.requireWithEvent() = withEvent<T>()!!
inline fun <reified T : PrivateEvent> PrivateEventMessage<*>.withEvent() = if (chatEvent is T) { this as PrivateEventMessage<T> } else { null }
inline fun <reified T : PrivateEvent> PrivateEventMessage<*>.requireWithEvent() = withEvent<T>()!!
inline fun <reified T : ChannelEvent> ChannelEventMessage<*>.withEvent() = if (chatEvent is T) { this as ChannelEventMessage<T> } else { null }
inline fun <reified T : ChannelEvent> ChannelEventMessage<*>.requireWithEvent() = withEvent<T>()!!
inline fun <reified T : GroupEvent> CommonGroupEventMessage<*>.withEvent() = if (chatEvent is T) { this as CommonGroupEventMessage<T> } else { null }
inline fun <reified T : GroupEvent> CommonGroupEventMessage<*>.requireWithEvent() = withEvent<T>()!!
inline fun <reified T : SupergroupEvent> CommonSupergroupEventMessage<*>.withEvent() = if (chatEvent is T) { this as CommonSupergroupEventMessage<T> } else { null }
inline fun <reified T : SupergroupEvent> CommonSupergroupEventMessage<*>.requireWithEvent() = withEvent<T>()!!

View File

@@ -2,6 +2,8 @@ package dev.inmo.tgbotapi.webapps
import dev.inmo.micro_utils.crypto.CryptoJs
@Deprecated("Useless")
fun CryptoJs.HmacSHA256(text: String, key: String) = this.asDynamic().HmacSHA256(text, key).unsafeCast<String>()
@Deprecated("Useless")
fun CryptoJs.hex(text: String) = this.asDynamic().format.Hex(text).unsafeCast<String>()

View File

@@ -1,6 +1,5 @@
package dev.inmo.tgbotapi.webapps
import dev.inmo.micro_utils.crypto.CryptoJS
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
external class WebApp {
@@ -77,7 +76,7 @@ fun WebApp.onMainButtonClicked(eventHandler: EventHandler) = onEvent(EventType.M
*/
fun WebApp.onViewportChanged(eventHandler: ViewportChangedEventHandler) = onEvent(EventType.ViewportChanged, eventHandler)
fun WebApp.isInitDataSafe(botToken: String) = TelegramAPIUrlsKeeper(botToken).checkWebAppLink(
fun WebApp.isInitDataSafe(botToken: String) = TelegramAPIUrlsKeeper(botToken).checkWebAppData(
initData,
initDataUnsafe.hash
)