Setup Bot Handlers
Two Approaches
- DSL:
bot.setFunctionality { ... } — define handlers inline
- Annotations:
@CommandHandler, @InputHandler, etc. — KSP generates activities; use with bot.handleUpdates() (loads handlers via META-INF)
DSL Handlers
Inside bot.setFunctionality { }:
| Handler |
Purpose |
onCommand("/start") { } |
Commands (e.g. /start) |
onInput("id") { } |
Single-step input; route via bot.inputListener[user] = "id" |
inputChain("id") { }.andThen { } |
Multi-step input chain |
common("text") { } or common(Regex("...")) { } |
Text/regex match (lower priority than commands) |
onUpdate(UpdateType.X) { } |
All updates of given type |
whenNotHandled { } |
Fallback for unprocessed updates |
Typed Update Handlers
Use typed wrappers for ActivityCtx<XxxUpdate>:
onMessage { } — MessageUpdate
onCallbackQuery { } — CallbackQueryUpdate
onInlineQuery { } — InlineQueryUpdate
onEditedMessage { }, onChannelPost { }, onChatMember { }, etc.
Input Listener
Route the next user message to an input handler or chain:
bot.inputListener[user] = "conversation"
// or
bot.inputListener.set(user) { "conversation-2step" }
Annotation Handlers
| Annotation |
Purpose |
@CommandHandler(["/start"]) |
Command handler |
@CommandHandler.CallbackQuery(["/data"]) |
Callback query with optional autoAnswer |
@InputHandler(["id"]) |
Input handler |
@CommonHandler.Text(["value"]) or @CommonHandler.Regex("...") |
Text/regex match |
@UpdateHandler([UpdateType.MESSAGE, ...]) |
Update type handler |
@UnprocessedHandler |
Fallback |
Guards
Add pre-processing checks:
- DSL:
guard = UserPresentGuard::class (or DefaultGuard::class)
- Annotation:
@Guard(UserPresentGuard::class) or @Guard(guard = UserPresentGuard::class)
Supported by: CommandHandler, CommandHandler.CallbackQuery, InputHandler.
Rate Limits
- DSL:
rateLimits = RateLimits.NOT_LIMITED or custom
- Annotation:
@RateLimits(period = 60_000, rate = 5) — 5 requests per 60 seconds
- Default: no limit
Supported by: CommandHandler, CallbackQuery, InputHandler, CommonHandler.
ArgParser
Custom argument parsing for commands and callback data:
- Annotation:
@ArgParser(argParser = CustomArgParser::class)
- Supported by: CommandHandler, CallbackQuery, CommonHandler
- Default:
DefaultArgParser
Annotation Reference
| Annotation |
Parameters |
Default |
@CommandHandler |
value (commands), scope (UpdateType[]) |
scope: MESSAGE |
@CommandHandler.CallbackQuery |
value (callback data prefixes), autoAnswer (bool) |
autoAnswer: false |
@InputHandler |
value (input identifiers) |
— |
@CommonHandler.Text |
value, filters, priority, scope |
filters: [], priority: 0, scope: MESSAGE |
@CommonHandler.Regex |
value (pattern), options, filters, priority, scope |
options: [], filters: [], priority: 0, scope: MESSAGE |
@UpdateHandler |
type (UpdateType[]) |
— |
@UnprocessedHandler |
— |
— |
@Guard |
guard (KClass) |
DefaultGuard |
@ArgParser |
argParser (KClass) |
DefaultArgParser |
@RateLimits |
period (ms), rate (requests) |
0, 0 |
Reference
- CommandHandler.kt
- CommonHandler.kt
- FunctionalHandlingDsl.kt
- FunctionalDSLUtils.kt
- telegram-bot-handling.mdc
Converted and distributed by TomeVault — claim your Tome and manage your conversions.
1---2name: vendelieu-telegram-bot-setup-bot-handlers3description: Setup Bot Handlers4---56# Setup Bot Handlers78## Two Approaches910- **DSL**: `bot.setFunctionality { ... }` — define handlers inline11- **Annotations**: `@CommandHandler`, `@InputHandler`, etc. — KSP generates activities; use with `bot.handleUpdates()` (loads handlers via META-INF)1213## DSL Handlers1415Inside `bot.setFunctionality { }`:1617| Handler | Purpose |18|---------|---------|19| `onCommand("/start") { }` | Commands (e.g. /start) |20| `onInput("id") { }` | Single-step input; route via `bot.inputListener[user] = "id"` |21| `inputChain("id") { }.andThen { }` | Multi-step input chain |22| `common("text") { }` or `common(Regex("...")) { }` | Text/regex match (lower priority than commands) |23| `onUpdate(UpdateType.X) { }` | All updates of given type |24| `whenNotHandled { }` | Fallback for unprocessed updates |2526## Typed Update Handlers2728Use typed wrappers for `ActivityCtx<XxxUpdate>`:2930- `onMessage { }` — `MessageUpdate`31- `onCallbackQuery { }` — `CallbackQueryUpdate`32- `onInlineQuery { }` — `InlineQueryUpdate`33- `onEditedMessage { }`, `onChannelPost { }`, `onChatMember { }`, etc.3435## Input Listener3637Route the next user message to an input handler or chain:3839```kotlin40bot.inputListener[user] = "conversation"41// or42bot.inputListener.set(user) { "conversation-2step" }43```4445## Annotation Handlers4647| Annotation | Purpose |48|------------|---------|49| `@CommandHandler(["/start"])` | Command handler |50| `@CommandHandler.CallbackQuery(["/data"])` | Callback query with optional `autoAnswer` |51| `@InputHandler(["id"])` | Input handler |52| `@CommonHandler.Text(["value"])` or `@CommonHandler.Regex("...")` | Text/regex match |53| `@UpdateHandler([UpdateType.MESSAGE, ...])` | Update type handler |54| `@UnprocessedHandler` | Fallback |5556## Guards5758Add pre-processing checks:5960- DSL: `guard = UserPresentGuard::class` (or `DefaultGuard::class`)61- Annotation: `@Guard(UserPresentGuard::class)` or `@Guard(guard = UserPresentGuard::class)`6263Supported by: CommandHandler, CommandHandler.CallbackQuery, InputHandler.6465## Rate Limits6667- DSL: `rateLimits = RateLimits.NOT_LIMITED` or custom68- Annotation: `@RateLimits(period = 60_000, rate = 5)` — 5 requests per 60 seconds69- Default: no limit7071Supported by: CommandHandler, CallbackQuery, InputHandler, CommonHandler.7273## ArgParser7475Custom argument parsing for commands and callback data:7677- Annotation: `@ArgParser(argParser = CustomArgParser::class)`78- Supported by: CommandHandler, CallbackQuery, CommonHandler79- Default: `DefaultArgParser`8081## Annotation Reference8283| Annotation | Parameters | Default |84|------------|------------|---------|85| `@CommandHandler` | `value` (commands), `scope` (UpdateType[]) | scope: MESSAGE |86| `@CommandHandler.CallbackQuery` | `value` (callback data prefixes), `autoAnswer` (bool) | autoAnswer: false |87| `@InputHandler` | `value` (input identifiers) | — |88| `@CommonHandler.Text` | `value`, `filters`, `priority`, `scope` | filters: [], priority: 0, scope: MESSAGE |89| `@CommonHandler.Regex` | `value` (pattern), `options`, `filters`, `priority`, `scope` | options: [], filters: [], priority: 0, scope: MESSAGE |90| `@UpdateHandler` | `type` (UpdateType[]) | — |91| `@UnprocessedHandler` | — | — |92| `@Guard` | `guard` (KClass) | DefaultGuard |93| `@ArgParser` | `argParser` (KClass) | DefaultArgParser |94| `@RateLimits` | `period` (ms), `rate` (requests) | 0, 0 |9596## Reference9798- [CommandHandler.kt](telegram-bot/src/commonMain/kotlin/eu/vendeli/tgbot/annotations/CommandHandler.kt)99- [CommonHandler.kt](telegram-bot/src/commonMain/kotlin/eu/vendeli/tgbot/annotations/CommonHandler.kt)100- [FunctionalHandlingDsl.kt](telegram-bot/src/commonMain/kotlin/eu/vendeli/tgbot/core/FunctionalHandlingDsl.kt)101- [FunctionalDSLUtils.kt](telegram-bot/src/commonMain/kotlin/eu/vendeli/tgbot/utils/common/FunctionalDSLUtils.kt)102- [telegram-bot-handling.mdc](.cursor/rules/telegram-bot-handling.mdc)103104---105> Converted and distributed by [TomeVault](https://tomevault.io/claim/vendelieu) — claim your Tome and manage your conversions.106<!-- tomevault:4.0:skill_md:2026-04-11 -->