Functional Dsl
To ~~infinity~~ functional dsl and beyond!¶
The bot supports both annotation-based and functional dsl setting context. You can combine both approaches.
Functional DSL¶
Functional DSL is just different way of defining bot context.
Example:
suspend fun main() {
val bot = TelegramBot("BOT_TOKEN")
bot.setFunctionality {
onChosenInlineResult {
println("got a result ${update.chosenInlineResult.resultId} from ${update.user}")
}
}
}
Commands and Inputs¶
You can handle both commands and inputs using the functional DSL.
Commands¶
suspend fun main() {
val bot = TelegramBot("BOT_TOKEN")
bot.setFunctionality {
// Regular command
onCommand("/start") {
message { "Hello" }.send(user, bot)
}
// Regex-based command matching
onCommand("""(red|green|blue)""".toRegex()) {
message { "you typed ${update.text} color" }.send(user, bot)
}
}
}
In onCommand, parsed parameters are available as Map<String, String> based on your configuration.
Inputs¶
You can use inputs via bot.inputListener.
suspend fun main() {
val bot = TelegramBot("BOT_TOKEN")
bot.setFunctionality {
onCommand("/start") {
message { "Hello, what's your name?" }.send(user, bot)
bot.inputListener[user] = "testInput"
}
onInput("testInput") {
message { "Hey, nice to meet you, ${update.text}" }.send(user, bot)
}
}
}
Input Chains¶
For multi-step input flows, use inputChain:
bot.setFunctionality {
inputChain("conversation") {
message { "Nice to meet you, ${update.text}" }.send(user, bot)
message { "What is your favorite food?" }.send(user, bot)
}.breakIf({ update.text == "peanut butter" }) { // chain break condition
message { "Oh, too bad, I'm allergic to it." }.send(user, bot)
// action that will be applied when condition matches
}.andThen {
// next input point if break condition doesn't match
message { "Great choice!" }.send(user, bot)
}
}
The chain automatically advances to the next step unless a break condition is met. If a break condition matches and repeat is true (default), the user stays on the current step.
Update Type Handlers¶
Handle specific update types directly:
bot.setFunctionality {
onUpdate(UpdateType.MESSAGE, UpdateType.CALLBACK_QUERY) {
// Handle both message and callback query updates
println("Received update: ${update.type}")
}
}
Common Matchers¶
Match text content (not just commands) using common:
bot.setFunctionality {
// String matching
common("hello") {
message { "Hi there!" }.send(user, bot)
}
// Regex matching
common("""\d+""".toRegex()) {
message { "You sent a number!" }.send(user, bot)
}
}
Fallback Handler¶
Handle updates that weren't processed by any handler:
Advanced Configuration¶
Rate Limiting¶
Apply rate limits to any handler:
bot.setFunctionality {
onCommand("/expensive", rateLimits = RateLimits(5, 60)) {
// This command can only be called 5 times per 60 seconds
message { "Processing..." }.send(user, bot)
}
}
Guards¶
Use guards to add custom validation logic:
bot.setFunctionality {
onCommand("/admin", guard = AdminGuard::class) {
message { "Admin command executed" }.send(user, bot)
}
}
Argument Parsing¶
Customize how command arguments are parsed:
bot.setFunctionality {
onCommand("/custom", argParser = CustomArgParser::class) {
// parameters will be parsed using CustomArgParser
message { "Parameters: $parameters" }.send(user, bot)
}
}
Combining Functional and Annotation-Based setting¶
You can use both approaches in the same bot:
// Annotation-based handler
@CommandHandler(["/register"])
suspend fun register(ctx: CommandContext) {
message { "Registration started" }.send(ctx.user, ctx.bot)
}
// Functional handler
bot.setFunctionality {
onCommand("/help") {
message { "Available commands: /register, /help" }.send(user, bot)
}
}
Both handlers are registered in the same ActivityRegistry and work seamlessly together.