improvements of commands expectations and triggers

This commit is contained in:
InsanusMokrassar 2023-10-25 14:36:32 +06:00
parent 0efa52fe00
commit 080db09d2c
8 changed files with 503 additions and 89 deletions

View File

@ -2,6 +2,12 @@
## 9.2.4
* `Utils`:
* New extensions `*.parseCommandsWithNamedArgs`
* `BehaviourBuilder`:
* In expectaters and triggers of `commands` add `*WithNamedArgs` variants
* In expectaters and triggers of `commands` add opportunity to use custom separator
## 9.2.3
* `Core`:

View File

@ -3,6 +3,10 @@ package dev.inmo.tgbotapi.extensions.behaviour_builder.expectations
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.handlers_registrar.doWithRegistration
import dev.inmo.tgbotapi.extensions.utils.*
import dev.inmo.tgbotapi.extensions.utils.extensions.TelegramBotCommandsDefaults
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithArgs
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithArgsSources
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithNamedArgs
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.types.BotCommand
import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
@ -82,32 +86,51 @@ fun Flow<CommonMessage<TextContent>>.requireCommandsWithoutParams() = filter {
* Map the commands with their arguments and source messages
*/
fun Flow<CommonMessage<TextContent>>.commandsWithParams(): Flow<Pair<CommonMessage<TextContent>, List<Pair<BotCommandTextSource, Array<TextSource>>>>> = mapNotNull {
var currentCommandTextSource: BotCommandTextSource? = null
val currentArgs = mutableListOf<TextSource>()
val result = mutableListOf<Pair<BotCommandTextSource, Array<TextSource>>>()
fun addCurrentCommandToResult() {
currentCommandTextSource ?.let {
result.add(it to currentArgs.toTypedArray())
currentArgs.clear()
}
}
it.content.textSources.forEach {
it.ifBotCommandTextSource {
addCurrentCommandToResult()
currentCommandTextSource = it
return@forEach
}
currentArgs.add(it)
}
addCurrentCommandToResult()
result.toList().takeIf { it.isNotEmpty() } ?.let { result ->
it to result
}
it to it.content.textSources.parseCommandsWithArgsSources().toList()
}
/**
* Map the commands with their arguments and source messages
*/
fun Flow<CommonMessage<TextContent>>.commandsWithArgs(
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex
): Flow<Pair<CommonMessage<TextContent>, List<Pair<String, Array<String>>>>> = mapNotNull {
val commandsWithArgs = it.content.textSources.parseCommandsWithArgs(argsSeparator).toList().ifEmpty {
return@mapNotNull null
}
it to commandsWithArgs
}
/**
* Map the commands with their arguments and source messages
*/
fun Flow<CommonMessage<TextContent>>.commandsWithArgs(
argsSeparator: String
): Flow<Pair<CommonMessage<TextContent>, List<Pair<String, Array<String>>>>> = commandsWithArgs(Regex(argsSeparator))
/**
* Map the commands with their arguments and source messages
*/
fun Flow<CommonMessage<TextContent>>.commandsWithNamedArgs(
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
): Flow<Pair<CommonMessage<TextContent>, List<Pair<String, List<Pair<String, String>>>>>> = mapNotNull {
val commandsWithArgs = it.content.textSources.parseCommandsWithNamedArgs(argsSeparator, nameArgSeparator).toList().ifEmpty {
return@mapNotNull null
}
it to commandsWithArgs
}
/**
* Map the commands with their arguments and source messages
*/
fun Flow<CommonMessage<TextContent>>.commandsWithNamedArgs(
argsSeparator: String,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
): Flow<Pair<CommonMessage<TextContent>, List<Pair<String, List<Pair<String, String>>>>>> = commandsWithNamedArgs(Regex(argsSeparator), nameArgSeparator)
/**
* Flat [commandsWithParams]. Each [Pair] of [BotCommandTextSource] and its [Array] of arg text sources will
* be associated with its source message

View File

@ -11,7 +11,9 @@ import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.marker_factories.ByC
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.marker_factories.MarkerFactory
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.times
import dev.inmo.tgbotapi.extensions.utils.botCommandTextSourceOrNull
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithParams
import dev.inmo.tgbotapi.extensions.utils.extensions.TelegramBotCommandsDefaults
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithArgs
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithNamedArgs
import dev.inmo.tgbotapi.types.BotCommand
import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.types.message.content.TextMessage
@ -124,6 +126,7 @@ suspend fun <BC : BehaviourContext> BC.commandWithArgs(
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, Array<String>>
) = command(
commandRegex,
@ -132,7 +135,7 @@ suspend fun <BC : BehaviourContext> BC.commandWithArgs(
subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory
) {
val args = it.parseCommandsWithParams().let { commandsWithArgs ->
val args = it.parseCommandsWithArgs(argsSeparator = argsSeparator).let { commandsWithArgs ->
val key = commandsWithArgs.keys.firstOrNull { it.matches(commandRegex) } ?: return@let null
commandsWithArgs[key]
} ?: emptyArray()
@ -144,12 +147,14 @@ suspend fun <BC : BehaviourContext> BC.commandWithArgs(
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, Array<String>>
) = commandWithArgs(
command.toRegex(),
initialFilter = initialFilter,
subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory,
argsSeparator = argsSeparator,
scenarioReceiver = scenarioReceiver
)
@ -158,12 +163,72 @@ suspend fun <BC : BehaviourContext> BC.commandWithArgs(
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, Array<String>>
) = commandWithArgs(
botCommand.command,
initialFilter = initialFilter,
subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory,
argsSeparator = argsSeparator,
scenarioReceiver = scenarioReceiver
)
suspend fun <BC : BehaviourContext> BC.commandWithNamedArgs(
commandRegex: Regex,
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, List<Pair<String, String>>>
) = command(
commandRegex,
requireOnlyCommandInMessage = false,
initialFilter = initialFilter,
subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory
) {
val args = it.parseCommandsWithNamedArgs(argsSeparator = argsSeparator, nameArgSeparator = nameArgSeparator).let { commandsWithArgs ->
val key = commandsWithArgs.keys.firstOrNull { it.matches(commandRegex) } ?: return@let null
commandsWithArgs[key]
} ?: emptyList()
scenarioReceiver(it, args)
}
suspend fun <BC : BehaviourContext> BC.commandWithNamedArgs(
command: String,
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, List<Pair<String, String>>>
) = commandWithNamedArgs(
command.toRegex(),
initialFilter = initialFilter,
subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory,
argsSeparator = argsSeparator,
nameArgSeparator = nameArgSeparator,
scenarioReceiver = scenarioReceiver
)
suspend fun <BC : BehaviourContext> BC.commandWithNamedArgs(
botCommand: BotCommand,
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, List<Pair<String, String>>>
) = commandWithNamedArgs(
botCommand.command,
initialFilter = initialFilter,
subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory,
argsSeparator = argsSeparator,
nameArgSeparator = nameArgSeparator,
scenarioReceiver = scenarioReceiver
)
@ -172,21 +237,99 @@ suspend fun <BC : BehaviourContext> BC.onCommandWithArgs(
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, Array<String>>
): Job = commandWithArgs(commandRegex, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
): Job = commandWithArgs(
commandRegex = commandRegex,
initialFilter = initialFilter,
subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory,
argsSeparator = argsSeparator,
scenarioReceiver = scenarioReceiver
)
suspend fun <BC : BehaviourContext> BC.onCommandWithArgs(
command: String,
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, Array<String>>
): Job = onCommandWithArgs(command.toRegex(), initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
): Job = onCommandWithArgs(
commandRegex = command.toRegex(),
initialFilter = initialFilter,
subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory,
argsSeparator = argsSeparator,
scenarioReceiver = scenarioReceiver
)
suspend fun <BC : BehaviourContext> BC.onCommandWithArgs(
botCommand: BotCommand,
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, Array<String>>
): Job = onCommandWithArgs(botCommand.command, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
): Job = onCommandWithArgs(
command = botCommand.command,
initialFilter = initialFilter,
subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory,
argsSeparator = argsSeparator,
scenarioReceiver = scenarioReceiver
)
suspend fun <BC : BehaviourContext> BC.onCommandWithNamedArgs(
commandRegex: Regex,
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, List<Pair<String, String>>>
) = commandWithNamedArgs(
commandRegex,
initialFilter = initialFilter,
subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory,
argsSeparator = argsSeparator,
nameArgSeparator = nameArgSeparator,
scenarioReceiver = scenarioReceiver,
)
suspend fun <BC : BehaviourContext> BC.onCommandWithNamedArgs(
command: String,
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, List<Pair<String, String>>>
) = onCommandWithNamedArgs(
command.toRegex(),
initialFilter = initialFilter,
subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory,
argsSeparator = argsSeparator,
nameArgSeparator = nameArgSeparator,
scenarioReceiver = scenarioReceiver
)
suspend fun <BC : BehaviourContext> BC.onCommandWithNamedArgs(
botCommand: BotCommand,
initialFilter: CommonMessageFilter<TextContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in TextMessage, Any> = ByChatMessageMarkerFactory,
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
scenarioReceiver: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, TextMessage, List<Pair<String, String>>>
) = onCommandWithNamedArgs(
botCommand.command,
initialFilter = initialFilter,
subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory,
argsSeparator = argsSeparator,
nameArgSeparator = nameArgSeparator,
scenarioReceiver = scenarioReceiver
)

View File

@ -9,7 +9,7 @@ import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.marker_factories.ByC
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.marker_factories.MarkerFactory
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.times
import dev.inmo.tgbotapi.extensions.utils.botCommandTextSourceOrNull
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithParams
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithArgs
import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.types.message.content.TextMessage
import dev.inmo.tgbotapi.types.update.abstracts.Update
@ -65,7 +65,7 @@ suspend fun <BC : BehaviourContext> BC.unhandledCommandWithArgs(
subcontextUpdatesFilter = subcontextUpdatesFilter,
markerFactory = markerFactory
) {
val args = it.parseCommandsWithParams().let { commandsWithArgs ->
val args = it.parseCommandsWithArgs().let { commandsWithArgs ->
commandsWithArgs
}
scenarioReceiver(it, args)

View File

@ -4,22 +4,18 @@ package dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling
import dev.inmo.micro_utils.coroutines.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitDeepLinks
import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.CommonMessageFilterExcludeMediaGroups
import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.MessageFilterByChat
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.SimpleFilter
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.marker_factories.ByChatMessageMarkerFactory
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.marker_factories.MarkerFactory
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.times
import dev.inmo.tgbotapi.extensions.utils.*
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithParams
import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.types.message.content.TextMessage
import dev.inmo.tgbotapi.types.message.textsources.RegularTextSource
import dev.inmo.tgbotapi.types.update.abstracts.Update
import io.ktor.http.decodeURLQueryComponent
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.filter
private val startRegex = Regex("start")
suspend fun <BC : BehaviourContext> BC.onDeepLink(

View File

@ -0,0 +1,247 @@
package dev.inmo.tgbotapi.extensions.utils.extensions
import dev.inmo.tgbotapi.abstracts.TextedWithTextSources
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.types.message.textsources.BotCommandTextSource
import dev.inmo.tgbotapi.types.message.textsources.TextSource
object TelegramBotCommandsDefaults {
const val defaultArgsSeparator = " "
val defaultArgsSeparatorRegex = Regex(defaultArgsSeparator)
const val defaultNamesArgsSeparator = "="
val defaultNamesArgsSeparatorRegex = Regex(defaultNamesArgsSeparator)
}
@Deprecated(message = "Replaced", replaceWith = ReplaceWith("TelegramBotCommandsDefaults.defaultArgsSeparatorRegex", "dev.inmo.tgbotapi.extensions.utils.extensions.TelegramBotCommandsDefaults"))
val defaultArgsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex
/**
* Parse commands and their args. Logic will find command, get all subsequent data as args until new command
*/
fun List<TextSource>.parseCommandsWithArgs(
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex
): MutableMap<String, Array<String>> {
val result = mutableMapOf<String, Array<String>>()
var currentBotCommandSource: BotCommandTextSource? = null
var currentArgs = ""
fun includeCurrent() = currentBotCommandSource?.let {
currentArgs = currentArgs.trim()
result[it.command] = if (currentArgs.isNotEmpty()) {
currentArgs.split(argsSeparator).toTypedArray()
} else {
emptyArray()
}
currentArgs = ""
currentBotCommandSource = null
}
for (textSource in this) {
if (textSource is BotCommandTextSource) {
includeCurrent()
currentBotCommandSource = textSource
} else {
currentArgs += textSource.source
}
}
includeCurrent()
return result
}
/**
* Parse commands and their args. Logic will find command, get all subsequent data as args until new command
*/
fun TextedWithTextSources.parseCommandsWithArgs(
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex
) = textSources?.parseCommandsWithArgs(argsSeparator) ?: emptyMap()
/**
* Parse commands and their args. Logic will find command, get all subsequent data as args until new command
*/
fun ContentMessage<TextContent>.parseCommandsWithArgs(
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex
) = content.parseCommandsWithArgs(argsSeparator)
/**
* Parse commands and their args. Logic will find command, get all subsequent data as args until new command
*/
fun List<TextSource>.parseCommandsWithArgs(
argsSeparator: String
): MutableMap<String, Array<String>> = parseCommandsWithArgs(Regex(argsSeparator))
/**
* Parse commands and their args. Logic will find command, get all subsequent data as args until new command
*/
fun TextedWithTextSources.parseCommandsWithArgs(
argsSeparator: String
) = parseCommandsWithArgs(Regex(argsSeparator))
/**
* Parse commands and their args. Logic will find command, get all subsequent data as args until new command
*/
fun ContentMessage<TextContent>.parseCommandsWithArgs(
argsSeparator: String
) = parseCommandsWithArgs(Regex(argsSeparator))
/**
* Uses [parseCommandsWithArgs] to create base [argsSeparator] split args for commands and map their as k-v pairs.
* Sample:
*
* ```bash
* /command args1=value1 arg2=value2 arg1=value3
* ```
*
* Will produce [Map] with one key `command` and the list of three pairs:
*
* 1. `args1` to `value1`
* 2. `args2` to `value2`
* 3. `args1` to `value3`
*
* @return Array of named arguments
*/
fun List<TextSource>.parseCommandsWithNamedArgs(
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
): Map<String, List<Pair<String, String>>> {
val withArgs = parseCommandsWithArgs(argsSeparator)
return withArgs.mapValues { (k, v) ->
v.flatMap {
it.split(nameArgSeparator, 2).map { v -> it to v }
}
}
}
/**
* Uses [parseCommandsWithArgs] to create base [argsSeparator] split args for commands and map their as k-v pairs.
* Sample:
*
* ```bash
* /command args1=value1 arg2=value2 arg1=value3
* ```
*
* Will produce [Map] with one key `command` and the list of three pairs:
*
* 1. `args1` to `value1`
* 2. `args2` to `value2`
* 3. `args1` to `value3`
*
* @return Array of named arguments
*/
fun TextedWithTextSources.parseCommandsWithNamedArgs(
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
) = textSources?.parseCommandsWithNamedArgs(argsSeparator = argsSeparator, nameArgSeparator = nameArgSeparator) ?: emptyMap()
/**
* Uses [parseCommandsWithArgs] to create base [argsSeparator] split args for commands and map their as k-v pairs.
* Sample:
*
* ```bash
* /command args1=value1 arg2=value2 arg1=value3
* ```
*
* Will produce [Map] with one key `command` and the list of three pairs:
*
* 1. `args1` to `value1`
* 2. `args2` to `value2`
* 3. `args1` to `value3`
*
* @return Array of named arguments
*/
fun ContentMessage<TextContent>.parseCommandsWithNamedArgs(
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
) = content.parseCommandsWithNamedArgs(argsSeparator = argsSeparator, nameArgSeparator = nameArgSeparator)
/**
* Uses [parseCommandsWithArgs] to create base [argsSeparator] split args for commands and map their as k-v pairs.
* Sample:
*
* ```bash
* /command args1=value1 arg2=value2 arg1=value3
* ```
*
* Will produce [Map] with one key `command` and the list of three pairs:
*
* 1. `args1` to `value1`
* 2. `args2` to `value2`
* 3. `args1` to `value3`
*
* @return Array of named arguments
*/
fun List<TextSource>.parseCommandsWithNamedArgs(
argsSeparator: String,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
): Map<String, List<Pair<String, String>>> = parseCommandsWithNamedArgs(Regex(pattern = argsSeparator), nameArgSeparator)
/**
* Uses [parseCommandsWithArgs] to create base [argsSeparator] split args for commands and map their as k-v pairs.
* Sample:
*
* ```bash
* /command args1=value1 arg2=value2 arg1=value3
* ```
*
* Will produce [Map] with one key `command` and the list of three pairs:
*
* 1. `args1` to `value1`
* 2. `args2` to `value2`
* 3. `args1` to `value3`
*
* @return Array of named arguments
*/
fun TextedWithTextSources.parseCommandsWithNamedArgs(
argsSeparator: String,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
) = parseCommandsWithNamedArgs(argsSeparator = Regex(pattern = argsSeparator), nameArgSeparator = nameArgSeparator)
/**
* Uses [parseCommandsWithArgs] to create base [argsSeparator] split args for commands and map their as k-v pairs.
* Sample:
*
* ```bash
* /command args1=value1 arg2=value2 arg1=value3
* ```
*
* Will produce [Map] with one key `command` and the list of three pairs:
*
* 1. `args1` to `value1`
* 2. `args2` to `value2`
* 3. `args1` to `value3`
*
* @return Array of named arguments
*/
fun ContentMessage<TextContent>.parseCommandsWithNamedArgs(
argsSeparator: String,
nameArgSeparator: Regex = TelegramBotCommandsDefaults.defaultNamesArgsSeparatorRegex,
) = parseCommandsWithNamedArgs(argsSeparator = Regex(pattern = argsSeparator), nameArgSeparator = nameArgSeparator)
// Deprecations
/**
* Parse commands and their args. Logic will find command, get all subsequent data as args until new command
*/
@Deprecated("Renamed", ReplaceWith("parseCommandsWithArgs(argsSeparator)", "dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithArgs"))
fun List<TextSource>.parseCommandsWithParams(
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex
): MutableMap<String, Array<String>> = parseCommandsWithArgs(argsSeparator)
/**
* Parse commands and their args. Logic will find command, get all subsequent data as args until new command
*/
@Deprecated("Renamed", ReplaceWith("parseCommandsWithArgs(argsSeparator)", "dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithArgs"))
fun TextedWithTextSources.parseCommandsWithParams(
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex
) = parseCommandsWithArgs(argsSeparator)
/**
* Parse commands and their args. Logic will find command, get all subsequent data as args until new command
*/
@Deprecated("Renamed", ReplaceWith("parseCommandsWithArgs(argsSeparator)", "dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithArgs"))
fun ContentMessage<TextContent>.parseCommandsWithParams(
argsSeparator: Regex = TelegramBotCommandsDefaults.defaultArgsSeparatorRegex
) = parseCommandsWithArgs(argsSeparator)

View File

@ -0,0 +1,53 @@
package dev.inmo.tgbotapi.extensions.utils.extensions
import dev.inmo.tgbotapi.abstracts.TextedWithTextSources
import dev.inmo.tgbotapi.extensions.utils.botCommandTextSourceOrNull
import dev.inmo.tgbotapi.extensions.utils.ifBotCommandTextSource
import dev.inmo.tgbotapi.extensions.utils.whenBotCommandTextSource
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.types.message.textsources.BotCommandTextSource
import dev.inmo.tgbotapi.types.message.textsources.TextSource
// Sources
/**
* Parse text sources to find commands with their arguments. This method will skip all the text sources __before__
* first command and all following text sources until the next command will be guessed as an args of last found command
*/
fun List<TextSource>.parseCommandsWithArgsSources(): Map<BotCommandTextSource, Array<TextSource>> {
var currentCommandTextSource: BotCommandTextSource? = null
val currentArgs = mutableListOf<TextSource>()
val result = mutableMapOf<BotCommandTextSource, Array<TextSource>>()
fun addCurrentCommandToResult() {
currentCommandTextSource ?.let {
result[it] = currentArgs.toTypedArray()
currentArgs.clear()
}
}
forEach {
it.whenBotCommandTextSource {
addCurrentCommandToResult()
currentCommandTextSource = it
return@forEach
}
currentArgs.add(it)
}
addCurrentCommandToResult()
return result
}
/**
* Parse text sources to find commands with their arguments. This method will skip all the text sources __before__
* first command and all following text sources until the next command will be guessed as an args of last found command
*/
fun TextedWithTextSources.parseCommandsWithArgsSources() = textSources?.parseCommandsWithArgsSources() ?: emptyMap()
/**
* Parse text sources to find commands with their arguments. This method will skip all the text sources __before__
* first command and all following text sources until the next command will be guessed as an args of last found command
*/
fun ContentMessage<TextContent>.parseCommandsWithArgsSources() = content.parseCommandsWithArgsSources()

View File

@ -1,54 +0,0 @@
package dev.inmo.tgbotapi.extensions.utils.extensions
import dev.inmo.tgbotapi.abstracts.TextedWithTextSources
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.types.message.textsources.BotCommandTextSource
import dev.inmo.tgbotapi.types.message.textsources.TextSource
val defaultArgsSeparator = Regex(" ")
/**
* Parse commands and their args. Logic will find command, get all subsequent data as args until new command
*/
fun List<TextSource>.parseCommandsWithParams(
argsSeparator: Regex = defaultArgsSeparator
): MutableMap<String, Array<String>> {
val result = mutableMapOf<String, Array<String>>()
var currentBotCommandSource: BotCommandTextSource? = null
var currentArgs = ""
fun includeCurrent() = currentBotCommandSource ?.let {
currentArgs = currentArgs.trim()
result[it.command] = if (currentArgs.isNotEmpty()) {
currentArgs.split(argsSeparator).toTypedArray()
} else {
emptyArray()
}
currentArgs = ""
currentBotCommandSource = null
}
for (textSource in this) {
if (textSource is BotCommandTextSource) {
includeCurrent()
currentBotCommandSource = textSource
} else {
currentArgs += textSource.source
}
}
includeCurrent()
return result
}
/**
* Parse commands and their args. Logic will find command, get all subsequent data as args until new command
*/
fun TextedWithTextSources.parseCommandsWithParams(
argsSeparator: Regex = defaultArgsSeparator
) = textSources ?.parseCommandsWithParams(argsSeparator) ?: emptyMap()
/**
* Parse commands and their args. Logic will find command, get all subsequent data as args until new command
*/
fun ContentMessage<TextContent>.parseCommandsWithParams(
argsSeparator: Regex = defaultArgsSeparator
) = content.parseCommandsWithParams(argsSeparator)