Kotlin Telegram Bot 0.8.1a 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.

⚠️ Caveat (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/handler/Template.kt

// from application.yml telegram-bot.template.chain = "Let's play a game. Write any positive integer." val GameChainHandler.chain by property() // 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}." val GameChainHandler.chainStartSum by property() // from application.yml telegram-bot.template.chain-one-more = "The current amount is ${sum}. Target: ${target}." val GameChainHandler.chainOneMore by property() // from application.yml telegram-bot.template.chain-end = "Let's play a game. Write any positive integer." val GameChainHandler.chainEnd by property() // from application.yml telegram-bot.template.chain-integer-is-expected = "Integer is expected." val GameChainHandler.chainIntegerIsExpected by property() // from application.yml telegram-bot.template.chain-positive-is-expected = "Positive integer is expected." val GameChainHandler.chainPositiveIsExpected by property()

com/example/myproject/handler/GameChainHandler.kt

@HandlerComponent class GameChainHandler : BotHandler({ val startSum = 0 command("/chain", next = "get target") { sendMessage(chain) } step("get target", next = "sum numbers") { val target = text.toIntOrNull() ?: throw ChatException(chainIntegerIsExpected) if (target < 1) throw ChatException(chainPositiveIsExpected) sendMessage(chainStartSum with ("target" to target)) transfer(target to startSum) } step("sum numbers") { val value = text.toIntOrNull() ?: throw ChatException(chainIntegerIsExpected) var (target, sum) = transferred<Pair<Int, Int>>() sum += value if (sum < target) { sendMessage(chainOneMore with mapOf("sum" to sum, "target" to target)) next("sum numbers", target to sum) return@step } sendMessage(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("Выберите действие", 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: 06 May 2024