Telegram Bot API wrapper for Kotlin

repository·master·Indexed 42 days ago

https://github.com/vendelieu/telegram-bot

A Kotlin wrapper for the Telegram Bot API featuring annotation-based handlers, a DSL for defining bot logic, and Finite State Machine (FSM) capabilities. It includes a Ktor starter for server configuration via ServerBuilder and BotConfiguration, and supports both Gradle plugin and manual installation for version 9.6.0.

Tokens
4.4K
Snippets
14
Records
18
Agent score
54%

What's inside Telegram Bot

  1. Prepare a release

    master

    Before releasing a new version, perform the following tasks:

    1. If you have introduced any API changes, run the Gradle apiDump task to update the API documentation.
    2. Run the Gradle prepareRelease task to finalize the release process.
    gradle apiDump
    gradle prepareRelease
  2. Install snapshot versions

    master

    To use snapshot versions, you can either use the Gradle plugin with the addSnapshotRepo parameter or manually add the snapshot Maven repository to your repositories block.

    Using the plugin:

    ktGram {
        forceVersion = "branch-xxxxxx~xxxxxx"
        addSnapshotRepo = true
    }

    Manually adding the repository:

    repositories {
        mavenCentral()
        // ...
        maven("https://mvn.vendeli.eu/telegram-bot")
    }
  3. How to add a new Update part

    master

    To extend the library with a new type of Telegram Update, follow these steps to ensure the data flows correctly from the raw update through the processing logic and into the KSP-generated DSL:

    1. Define the Data: Add a new field to the Update class in eu/vendeli/tgbot/types/common/Update.kt.
    2. Define the Type: Add a new entry to the UpdateType enum in telegram-bot/src/commonMain/kotlin/eu/vendeli/tgbot/types/component/UpdateType.kt.
    3. Create the Processed Type: Create a new subclass of ProcessedUpdate in telegram-bot/src/commonMain/kotlin/eu/vendeli/tgbot/types/component/ProcessedUpdate.kt.
    4. Update Processing Logic: Add a new clause to the processUpdate() function in telegram-bot/src/commonMain/kotlin/eu/vendeli/tgbot/utils/common/ProcessUpdate.kt.
    5. Configure KSP (Code Generation):
      • Add a new TypeName constant to TypeConstants in ktnip/src/jvmMain/kotlin/eu/vendeli/ktnip/utils/TypeConstants.kt.
      • Add a corresponding mapping entry to TypeMapper in ktnip/src/jvmMain/kotlin/eu/vendeli/ktnip/codegen/TypeMapper.kt.
    6. Update DSL: Run the helper KSP task to add the new section for FunctionalDSLUtils in telegram-bot/src/commonMain/kotlin/eu/vendeli/tgbot/utils/common/FunctionalDSLUtils.kt.
  4. Manually install the Telegram Bot library

    master

    If you prefer not to use the Gradle plugin, you must manually add the KSP plugin, the library dependency, and the ktnip KSP processor to your build.gradle.kts file.

    For multiplatform projects, add the dependency to common sources and define KSP for each target.

    plugins {
        // ...
        id("com.google.devtools.ksp") version "2.3.10"
    }
    
    dependencies {
        // ...
        implementation("eu.vendeli:telegram-bot:9.6.0")
        ksp("eu.vendeli:ktnip:9.6.0")
    }
  5. Install the Telegram Bot library via Gradle

    master

    To install the library, add the KSP plugin and the Telegram Bot library plugin to your build.gradle.kts file. This is the recommended way to set up the project.

    If you are using a snapshot version, you can configure the ktGram block with addSnapshotRepo = true to automatically include the necessary snapshot repository.

    plugins {
        // ...
        id("com.google.devtools.ksp") version "2.3.10"
        id("eu.vendeli.telegram-bot") version "9.6.0"
    }
  6. Initialize the TelegramBot client

    master

    To use the library, you must instantiate the TelegramBot class. You can do this in three ways:

    1. Directly with a token: Provide the bot token and an optional commandsPackage (where the bot will look for annotated commands).
    2. Using a ConfigLoader: Pass a ConfigLoader instance to automatically load the token and configuration.
    3. With a custom HttpClient: Provide your own Ktor HttpClient instance if you need to customize the underlying network layer.

    You can also provide a BotConfigurator lambda to customize the BotConfiguration during initialization.

    // 1. Basic initialization
    val bot = TelegramBot(token = "YOUR_BOT_TOKEN")
    
    // 2. Initialization with custom configuration
    val bot = TelegramBot(token = "YOUR_BOT_TOKEN") { config ->
        // Customize config here
    }
    
    // 3. Initialization with a custom HttpClient
    val bot = TelegramBot(
        token = "YOUR_BOT_TOKEN",
        httpClient = myCustomHttpClient
    )
  7. Use handlers with annotations

    master

    You can define bot logic using annotated functions. Common annotations include:

    • @CommandHandler(commands: Array<String>): Responds to specific slash commands.
    • @InputHandler(inputs: Array<String>): Responds to specific input states (useful for Finite State Machines).
    • @CommonHandler.Regex(pattern: String): Responds to messages matching a regular expression.

    You can use @Guard(guardClass: KClass<T>) to restrict access to a handler based on specific conditions.

    To manage user states (Finite State Machine), use bot.inputListener[user] = "state_name" to transition a user to a specific input handler.

    suspend fun main() {
        val bot = TelegramBot("BOT_TOKEN")
        bot.handleUpdates()
    }
    
    @CommandHandler(["/start"])
    suspend fun start(user: User, bot: TelegramBot) {
        sendMessage { "Hello, what's your name?" }.send(user, bot)
        bot.inputListener[user] = "conversation"
    }
    
    @InputHandler(["conversation"])
    @Guard(UserPresentGuard::class)
    suspend fun startConversation(update: ProcessedUpdate, user: User, bot: TelegramBot) {
        sendMessage { "Nice to meet you, ${update.text}" }.send(user, bot)
        sendMessage { "What is your favorite food?" }.send(user, bot)
    
        bot.inputListener.set(user) { "conversation-2step" }
    }
    
    @CommonHandler.Regex("blue colo?r")
    suspend fun color(user: User, bot: TelegramBot) {
        message { "Oh you also like blue color?" }.send(user, bot)
    }
  8. Use the DSL for defining functionality

    master

    Instead of annotations, you can define bot behavior using a DSL within the bot.setFunctionality block. This allows for complex logic like inputChain, which defines a sequence of inputs, and breakIf, which allows you to interrupt a chain based on a condition.

    inputChain supports .breakIf({ condition }) to handle specific user responses and .andThen { ... } to define what happens if the break condition is not met.

    fun main() = runBlocking {
        val bot = TelegramBot("BOT_TOKEN")
    
        bot.setFunctionality {
            onCommand("/start") {
                message { "Hello, what's your name?" }.send(user, bot)
                bot.inputListener[user] = "conversation"
            }
            inputChain("conversation") {
                message { "Nice to meet you, ${message.text}" }.send(update.getUser(), bot)
                message { "What is your favorite food?" }.send(update.getUser(), bot)
            }.breakIf({ message.text == "peanut butter" }) {
                message { "Oh, too bad, I'm allergic to it." }.send(update.getUser(), bot)
            }.andThen {
                // next input point if break condition doesn't match
            }
        }
        
        bot.handleUpdates()
    }
  9. Process responses with sendReturning()

    master

    By default, send() is used to dispatch messages. To gain more control over the request flow and handle potential errors, use sendReturning() instead.

    sendReturning() returns a Response object, which provides the following methods:

    • .onFailure { ... }: Executes a block if the request fails.
    • .getOrNull(): Returns the result or null.
    • .isSuccess(): Returns true if the request was successful.

    When a failure occurs, the Response object contains errorCode and description.

    message { "test" }.sendReturning(user, bot).onFailure {
        println("code: ${it.errorCode} description: ${it.description}")
    }
  10. Configure the Telegram Bot in Ktor using BotConfiguration

    master

    The BotConfiguration class is used to set up a Telegram bot within a Ktor application. It allows you to define the bot's authentication token, the package where commands are located, custom handling behaviors for updates, and initialization hooks.

    Key properties:

    • token: The unique Telegram bot token (must be provided).
    • pckg: An optional string specifying the package where the bot should search for commands.

    Available configuration methods:

    • configuration(config: BotConfigurator): Sets the core bot configuration using a BotConfigurator lambda.
    • handlingBehaviour(block: HandlingBehaviourBlock): Defines how updates are handled.
    • onInit(block: suspend (bot: TelegramBot) -> Unit): Provides a hook to execute code immediately after the bot is initialized.
    // Example usage within a Ktor configuration context
    BotConfiguration().apply {
        token = "YOUR_BOT_TOKEN"
        pckg = "eu.vendeli.myapp.commands"
        
        configuration { 
            // Configure using BotConfigurator
        }
    
        handlingBehaviour { 
            // Define handling behavior
        }
    
        onInit { bot ->
            // Perform actions after bot initialization
            println("Bot initialized: ${bot.name}")
        }
    }
  11. Configure the Ktor starter via environment variables

    master

    The EnvConfiguration implementation allows you to configure the server using environment variables prefixed with KTGRAM_.

    Required variables must be provided, or the application will throw an ApplicationConfigurationException. Some variables have default values if not specified.

    Environment VariableDescriptionDefaultRequired
    KTGRAM_HOSTHost to bind to0.0.0.0No
    KTGRAM_PORTPort to bind to8080No
    KTGRAM_SSL_PORTPort to use for SSL8443No
    KTGRAM_PEM_PRIVATE_KEY_PATHPath to the PEM private keyN/AYes
    KTGRAM_CHAIN_PATHPath to the PEM chainN/AYes
    KTGRAM_PRIVATE_KEYThe PEM private key contentN/AYes
    KTGRAM_KEYSTORE_PATHPath to the keystoreN/AYes
    KTGRAM_KEYSTORE_PASSWORDKeystore passwordN/AYes
    KTGRAM_KEY_ALIASAlias for the key in the keystoreserverSslNo
    KTGRAM_SSL_ONIf SSL is enabled (use true or false)trueNo
  12. Declare a bot using declareBot

    master

    Use declareBot within a ServerBuilder instance to register a Telegram bot. This method accepts a BotConfiguration lambda where you must specify the bot's token and its pckg (the package containing your command handlers).

    If shareHttpClient is enabled in the ServerBuilder, the bot will use a shared HttpClient configured via HttpConfiguration.

    builder.declareBot {
        token = "123456:ABC-DEF"
        pckg = "eu.vendeli.mybot.commands"
        // configuration and hooks can also be set here
    }