Kotlin Telegram Bot 0.11.4 Help

Kotlin Telegram Bot

Kotlin library for creating Telegram Bots. You can use clean version, with implementation for Spring, Ktor+Koin or create with you own implementation. It have also possibility to save state in database with Spring JPA or Exposed.

Easy to handle dialogs with users. Supporting message templates and other helpful features. Working on coroutines.

Example of applications in example-spring, example-ktor, example-core directories.

Why this library

  • Focused on building a dialog with the user (for example, no need to specify chatId in dialog chains).

  • Has many useful utilities (such as templating, keyboard and button creating and other).

  • Telegram API methods realization have overloads for more comfortable usage (like a chatId as String or Long).

  • Working on coroutines.

  • Has clean version or with Spring or Ktor+Koin frameworks.

  • Has possibility to save state in database with Spring JPA or Exposed.

  • Easy to write tests for your bot.

⚠️ Limitations (will be resolved) ⚠️

  • Now available only long polling (will be added webhook also).

Prerequisites

  • JDK 17 or higher

  • Kotlin 1.8 or higher

  • Gradle or Maven

Simple examples

com/example/myproject/handler/RegistrationHandler.kt

@HandlerComponent class RegistrationHandler : BotHandler({ command("/register", next = "get_contact") { // send message with contact button sendMessage("Please, share your phone number", replyMarkup = contactKeyboard("Share contact")) } // if user share contact, then store number step("get_contact", type = CONTACT) { sendMessage("Hello, ${contact.firstName}! Stored number: ${contact.phoneNumber}.", replyMarkup = removeKeyboard()) } // if user typed phone number, then we need ask one more question step("get_contact", type = TEXT, next = "get_firstname") { sendMessage("What is your name?", replyMarkup = removeKeyboard()) transfer(text) // transfer the phone number to next step } // step will executed only if user typed phone number as string step("get_firstname") { // we can receive phone number from previous step (because transferred it) val phone = transferred<String>() sendMessage("Hello, $text! Stored number: $phone.") } })

com/example/myproject/handler/RegistrationHandler.kt

@Factory class RegistrationHandler : BotHandler({ command("/register", next = "get_contact") { // send message with contact button sendMessage("Please, share your phone number", replyMarkup = contactKeyboard("Share contact")) } // if user share contact, then store number step("get_contact", type = CONTACT) { sendMessage("Hello, ${contact.firstName}! Stored number: ${contact.phoneNumber}.", replyMarkup = removeKeyboard()) } // if user typed phone number, then we need ask one more question step("get_contact", type = TEXT, next = "get_firstname") { sendMessage("What is your name?", replyMarkup = removeKeyboard()) transfer(text) // transfer the phone number to next step } // step will executed only if user typed phone number as string step("get_firstname") { // we can receive phone number from previous step (because transferred it) val phone = transferred<String>() sendMessage("Hello, $text! Stored number: $phone.") } })

com/example/myproject/handler/RegistrationHandler.kt

fun BotHandling.registrationHandler() { command("/register", next = "get_contact") { // send message with contact button sendMessage("Please, share your phone number", replyMarkup = contactKeyboard("Share contact")) } // if user share contact, then store number step("get_contact", type = CONTACT) { sendMessage("Hello, ${contact.firstName}! Stored number: ${contact.phoneNumber}.", replyMarkup = removeKeyboard()) } // if user typed phone number, then we need ask one more question step("get_contact", type = TEXT, next = "get_firstname") { sendMessage("What is your name?", replyMarkup = removeKeyboard()) transfer(text) // transfer the phone number to next step } // step will executed only if user typed phone number as string step("get_firstname") { // we can receive phone number from previous step (because transferred it) val phone = transferred<String>() sendMessage("Hello, $text! Stored number: $phone.") } }

More complex examples

com/example/myproject/holder/MessageTemplateHolder.kt

@ConfigurationProperties("telegram-bot.template") class MessageTemplateHolder { // from application.yml telegram-bot.template.chain = "Let's play a game. Write any positive integer." lateinit var chain: String // from application.yml telegram-bot.template.chain-start-sum = "I will now add the numbers entered until the sum of them is greater than or equal to ${target}." lateinit var chainStartSum: String // from application.yml telegram-bot.template.chain-one-more = "The current amount is ${sum}. Target: ${target}." lateinit var chainOneMore: String // from application.yml telegram-bot.template.chain-end = "Let's play a game. Write any positive integer." lateinit var chainEnd: String // from application.yml telegram-bot.template.chain-integer-is-expected = "Integer is expected." lateinit var chainIntegerIsExpected: String // from application.yml telegram-bot.template.chain-positive-is-expected = "Positive integer is expected." lateinit var chainPositiveIsExpected: String }

com/example/myproject/handler/GameChainHandler.kt

@HandlerComponent class GameChainHandler( private val template: MessageTemplateHolder, ) : BotHandler({ val startSum = 0 command("/chain", next = "get_target") { sendMessage(template.chain) } step("get_target", next = "sum_numbers") { val target = text.toIntOrNull() ?: throw ChatException(template.chainIntegerIsExpected) if (target < 1) throw ChatException(template.chainPositiveIsExpected) sendMessage(template.chainStartSum with ("target" to target)) transfer(target to startSum) } step("sum_numbers") { val value = text.toIntOrNull() ?: throw ChatException(template.chainIntegerIsExpected) var (target, sum) = transferred<Pair<Int, Int>>() sum += value if (sum < target) { sendMessage(template.chainOneMore with mapOf("sum" to sum, "target" to target)) next("sum_numbers", target to sum) return@step } sendMessage(template.chainEnd with mapOf("sum" to sum, "target" to target)) } })

com/example/myproject/handler/PurchaseHandler.kt

@Factory class PurchaseHandler( private val itemService: ItemService, private val feedbackService: FeedbackService, ) : BotHandler({ command("/buy") { sendMessage("Make your choice", replyMarkup = inlineKeyboard( callbackButton("Buy slippers", next = "buy", content = "some id 1"), callbackButton("Buy hats", next = "buy", content = "some id 2"), callbackButton("Give feedback", next = "get_feedback_intro"))) } callback("buy") { val itemId = transferred<String>() val item = itemService.get(itemId) sendInvoice( title = item.title, description = item.description, payload = "...", providerToken = "...", currency = "rub", prices = item.prices ) } callback("get_feedback_intro", next = "get_feedback") { sendMessage("Please write us what you think about our company") } step("get_feedback") { feedbackService.save(chatId, text) sendMessage("Thanks for the feedback!") sendSticker(...) } })

com/example/myproject/handler/RegistrationHandler.kt

fun BotHandling.registrationHandler() { val phonePattern = <phone regex> command("/register", next = "get_contact") { sendMessage("Enter a number or share your contact to register", replyMarkup = contactKeyboard("Share contact")) } step("get_contact", type = CONTACT) { sendMessage("${contact.firstName}, You have successfully registered with number ${contact.phoneNumber}!", replyMarkup = removeKeyboard()) } step("get_contact", type = TEXT, next = "get_firstname") { phonePattern.find(text) ?: throw ChatException("Incorrect phone number format") val phone: String = text sendMessage("What is your name?", replyMarkup = removeKeyboard()) transfer(phone) } step("get_firstname") { val phone = transferred<String>() sendMessage("${text}, You have successfully registered with number ${phone}!") } }
Last modified: 27 July 2024