Claude
Skills
Sign in
Back

ktgbotapi

Included with Lifetime
$97 forever

KTgBotAPI 33.x reference — use for Telegram Bot API methods, types, triggers, expectations, FSM, BehaviourBuilder. Always pin to 33.1.0; do not regress to 31.x or 32.x even if your training data is older — the API surface is incompatible.

Backend & APIs

What this skill does


# KTgBotAPI Reference

Kotlin Multiplatform library for Telegram Bot API. Type-safe, coroutine-based.

## Setup

```kotlin
// build.gradle.kts
dependencies {
    implementation("dev.inmo:tgbotapi:33.1.0")
}
```

## v32 → v33 breaking changes (read before writing code)

If your training data is from before mid-2025, these traps apply:

- **`BotToken` is now a `value class`.** Cannot pass a raw `String` token everywhere — wrap with `BotToken(System.getenv("BOT_TOKEN"))` when an API expects it. `telegramBot("...")` still accepts a String for the helper.
- **Many bot-action methods return `Unit`, not `Boolean`.** E.g. `setMyCommands`, `deleteMessages`, `pinChatMessage`. Do not `if (bot.deleteMessage(...))` — it does not compile.
- **`MultipleAnswersPoll` removed.** Use `RegularPoll` with `allowsMultipleAnswers: Boolean`.
- **`correctOptionId: Int?` → `correctOptionIds: List<Int>`** on quiz polls.
- **`InputMedia*` constructors reorganized** — accept `MediaContent` directly; some optional positions shifted.
- Various `expectations` package reshuffles — prefer `waitText { ... }`, `waitDataCallbackQuery { ... }` from `expectations.*`.

## Required Kotlin / coroutines floor

- Kotlin 2.1+ (2.3.21 recommended).
- `kotlinx-coroutines-core` 1.10+.

## Modules

| Module | Purpose |
|--------|---------|
| `tgbotapi.core` | Core types, requests |
| `tgbotapi.api` | API extension methods |
| `tgbotapi.utils` | Utilities, keyboard builders |
| `tgbotapi.behaviour_builder` | BehaviourBuilder DSL |
| `tgbotapi.behaviour_builder.fsm` | FSM integration |

## Quick Start

```kotlin
suspend fun main() {
    val bot = telegramBot(System.getenv("BOT_TOKEN"))

    bot.buildBehaviourWithLongPolling {
        onCommand("start") { reply(it, "Hello!") }
    }.join()
}
```

## Triggers Reference

### Commands

**CRITICAL: Understanding `requireOnlyCommandInMessage` parameter**

By default, `onCommand` has `requireOnlyCommandInMessage = true`, meaning it ONLY triggers when the message contains JUST the command with no additional text.

```kotlin
// DEFAULT BEHAVIOR - triggers ONLY for "/start" (no extra text)
onCommand("start") { message -> }

// This will NOT trigger for "/start hello" or "/warn @username"!
```

**For commands with arguments, you MUST use one of these approaches:**

```kotlin
// APPROACH 1: Set requireOnlyCommandInMessage = false
// Triggers for "/mute @username reason" - you parse args manually
onCommand("mute", requireOnlyCommandInMessage = false) { message ->
    val text = (message.content as TextContent).text
    val args = text.split(" ").drop(1)  // skip command
}

// APPROACH 2: Use onCommandWithArgs (recommended for simple args)
// Automatically sets requireOnlyCommandInMessage = false and parses args
onCommandWithArgs("echo") { message, args ->
    // args = arrayOf("hello", "world") for "/echo hello world"
    reply(message, args.joinToString(" "))
}

// APPROACH 3: Use onCommandWithNamedArgs for key=value format
onCommandWithNamedArgs("config") { message, args ->
    // args = listOf("key" to "value") for "/config key=value"
}
```

**Common patterns:**

```kotlin
onCommand("start") { message -> }                          // no args expected
onCommand("help", "info") { message -> }                   // multiple commands, no args
onCommand(Regex("set_.*")) { message -> }                  // regex pattern
onCommand("warn", requireOnlyCommandInMessage = false) { } // manual arg parsing
onCommandWithArgs("echo") { message, args -> }             // auto arg parsing
onDeepLink { message, deepLink -> }                        // t.me/bot?start=payload
onUnhandledCommand { message -> }                          // fallback for unknown commands
```

### Text

```kotlin
onText { message -> }
onText(initialFilter = { it.content.text.length > 10 }) { message -> }
onText(Regex("\\d+")) { message -> }
```

### Media

```kotlin
onPhoto { message -> }
onVideo { message -> }
onAudio { message -> }
onDocument { message -> }
onVoice { message -> }
onVideoNote { message -> }
onSticker { message -> }
onAnimation { message -> }
onMediaGroup { messages -> }                       // album
onVisualMediaGroup { messages -> }                 // photos/videos only
```

### Callbacks & Queries

```kotlin
onDataCallbackQuery { query -> }
onDataCallbackQuery(Regex("action:.*")) { query -> }
onInlineQuery { query -> }
onChosenInlineResult { result -> }
```

### Other Updates

```kotlin
onContact { message -> }
onLocation { message -> }
onPoll { poll -> }
onPollAnswer { answer -> }
onChatMemberUpdated { update -> }
onMyChatMemberUpdated { update -> }
onNewChatMembers { message -> }
onLeftChatMember { message -> }
```

## Expectations Reference

Wait for specific user input:

```kotlin
// Wait for text
val text = waitText().first()
val text = waitText { it.chat.id == chatId }.first()

// Wait for media
val photo = waitPhoto().first()
val document = waitDocument().first()

// Wait for callback
val callback = waitDataCallbackQuery().first()
val callback = waitDataCallbackQuery { it.data.startsWith("confirm:") }.first()

// With request (send message and wait)
val photo = waitPhoto(
    SendTextMessage(chatId, "Send me a photo")
).first()
```

## Sending Messages

```kotlin
// Text
sendMessage(chatId, "Hello")
sendTextMessage(chatId, "Hello", parseMode = ParseMode.HTML)
reply(message, "Reply text")

// With entities
send(chatId, buildEntities {
    bold("Bold") + " and " + italic("italic")
})

// Media
sendPhoto(chatId, InputFile.fromFile(File("photo.jpg")))
sendPhoto(chatId, InputFile.fromUrl("https://..."))
sendDocument(chatId, InputFile.fromFile(File("doc.pdf")))
sendVideo(chatId, InputFile.fromFile(File("video.mp4")))
sendAudio(chatId, InputFile.fromFile(File("audio.mp3")))
sendVoice(chatId, InputFile.fromFile(File("voice.ogg")))
sendSticker(chatId, InputFile.fromId(stickerFileId))

// Media group
sendMediaGroup(chatId, listOf(
    TelegramMediaPhoto(InputFile.fromFile(File("1.jpg"))),
    TelegramMediaPhoto(InputFile.fromFile(File("2.jpg")))
))

// Location
sendLocation(chatId, latitude = 55.75, longitude = 37.62)

// Contact
sendContact(chatId, phoneNumber = "+123456789", firstName = "John")
```

## Text Formatting

### buildEntities DSL

```kotlin
val text = buildEntities {
    bold("Bold") + "\n"
    italic("Italic") + "\n"
    underline("Underline") + "\n"
    strikethrough("Strike") + "\n"
    spoiler("Spoiler") + "\n"
    code("inline code") + "\n"
    pre("code block", language = "kotlin")
    link("Link", "https://example.com") + "\n"
    mention("username")
    textMention("User", userId)
    botCommand("start")
    hashtag("tag")
    cashtag("USD")
    email("[email protected]")
    phoneNumber("+123456789")
    regular("Plain text")
}
```

### Parse Modes

```kotlin
// HTML
sendTextMessage(chatId, """
    <b>Bold</b>, <i>italic</i>, <u>underline</u>
    <s>strikethrough</s>, <tg-spoiler>spoiler</tg-spoiler>
    <code>code</code>, <pre>block</pre>
    <a href="https://...">link</a>
""".trimIndent(), parseMode = ParseMode.HTML)

// MarkdownV2 (escape special chars: _*[]()~`>#+-=|{}.!)
sendTextMessage(chatId, """
    *bold*, _italic_, __underline__
    ~strikethrough~, ||spoiler||
    `code`, ```block```
    [link](https://...)
""".trimIndent(), parseMode = ParseMode.MarkdownV2)
```

## Reply Keyboard

```kotlin
val keyboard = replyKeyboard(
    resizeKeyboard = true,
    oneTimeKeyboard = true,
    inputFieldPlaceholder = "Choose option"
) {
    row {
        simpleButton("Button 1")
        simpleButton("Button 2")
    }
    row {
        requestContactButton("Share Contact")
        requestLocationButton("Share Location")
    }
    row {
        requestPollButton("Create Poll", type = RegularPoll)
        webAppButton("Web App", WebAppInfo("https://..."))
    }
}

sendMessage(chatId, "Menu:", replyMarkup = keyboard)

// Remove keyboard
sendMessage(chatId, "Done", replyMarkup = ReplyKeyboardRemove())
```

## Inline Keyboard

```kotlin
val keyboard = inlineKeyboard {
    row
Files: 2
Size: 16.5 KB
Complexity: 22/100
Category: Backend & APIs

Related in Backend & APIs