Skip to main content

Declarative, developer-friendly library for building Telegram bots

Project description

TeleKit

PyPI Python PyPI Downloads

Telekit

Telekit is a declarative, developer-friendly library for building Telegram bots. It gives developers a dedicated Sender for composing and sending messages and a Chain for handling dialogue between the user and the bot. The library also handles inline keyboards and callback routing automatically, letting you focus on the bot's behavior instead of repetitive tasks.

import telekit

class MyStartHandler(telekit.Handler):
    @classmethod
    def init_handler(cls):
        cls.on.command('start').invoke(cls.handle_start)

    def handle_start(self):
        self.chain.sender.set_text("Hello!")
        self.chain.sender.set_photo("robot.png")
        self.chain.send()

telekit.Server("BOT_TOKEN").polling()

Send "Hello!" with a photo on /start

Telekit comes with a built-in DSL, allowing developers to create fully interactive bots with minimal code. It also integrates Jinja, giving you loops, conditionals, expressions, and filters to generate dynamic content.

@ main {
    title   = "๐ŸŽ‰ Fun Facts Quiz";
    message = "Test your knowledge with 10 fun questions!";

    buttons {
        question_1("Start Quiz");
    }
}

See the full example

Even in its beta stage, Telekit accelerates bot development, offering typed command parameters, text styling via Bold(), Italic(), a built-in declarative calendar picker, emoji game results for ๐ŸŽฒ ๐ŸŽฏ ๐Ÿ€ โšฝ ๐ŸŽณ ๐ŸŽฐ, and much more out of the box. Its declarative design makes bots easier to read, maintain, and extend.

Key features:

  • Declarative bot logic with chains for effortless handling of complex conversations
  • Ready-to-use DSL for FAQs and other interactive scripts
  • Automatic handling of message formatting via Sender and callback routing
  • Deep Linking support with type-checked Command Parameters for flexible user input
  • Built-in Permission and Logging system for user management
  • Reusable Traits system for pluggable, self-contained behavior modules
  • Seamless integration with pyTelegramBotAPI
  • Fast to develop and easy-to-extend code

GitHub PyPI Telegram Community

Contents

Overview

Telekit is a library for building Telegram bots where dialogs look like normal method calls. No bulky state machines. No scattered handlers.

The idea is simple: you point to the next step โ€” Telekit calls it when the user replies.

Entries

No state machines. Just tell Telekit which method should handle the next user message.

def handle(self):
    self.chain.sender.set_text("๐Ÿ‘‹ Hello! What is your name?")
    self.chain.set_entry_text(self.handle_name)
    self.chain.send()

def handle_name(self, name: str):
    self.chain.sender.set_text(f"Nice to meet you, {name}!")
    self.chain.send()

The handle method sends a message and registers handle_name as the next step using set_entry_text. When the user replies, Telekit automatically calls handle_name and passes the user's message as a plain str argument.

That's it. No enums. No manual state tracking. No boilerplate.

Inline Keyboards

The fastest way to add buttons to a message. Pass a plain dict where each key is the button label and each value is the callback to invoke when pressed:

self.chain.set_inline_keyboard(
    {
        "โœ๏ธ Change": self.change_name,
        "โŒ Delete": self.delete,
    }
)

row_width controls how many buttons appear per row:

self.chain.set_inline_keyboard(
    {
        "One":   self.one,
        "Two":   self.two,
        "Three": self.three,
        "Four":  self.four,
        "Five":  self.five,
    },
    row_width=(3, 2)  # first row: 3 buttons, second row: 2
)
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚   One    โ”‚   Two    โ”‚  Three   โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚    Four     โ”‚       Five       โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

Need more control?

When you need precise row layout or conditional buttons, use InlineKeyboard โ€” a fluent builder that composes keyboards step by step:

self.chain.set_keyboard(
    InlineKeyboard()
        .add_callback("-", self.decrement, style="danger")
        .add_callback("+", self.increment, style="success")
    .row()
        .add_callback("โ†บ Reset", self.reset)
)
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚    -     โ”‚    +     โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚       โ†บ Reset       โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

InlineKeyboard is built by chaining method calls. Just call .row() to start a new row.

Not just callback buttons

Method Description
add_callback(...) Button that fires a callback function.
add_link(...) Button that opens a URL.
add_copy(...) Button that copies text to the clipboard.
add_alert(...) Button that shows a popup alert dialog.
add_notification(...) Button that shows a brief top-of-chat notification.
add_static(...) Decorative button with no action.
add_webapp(...) Button that opens a Telegram Mini App.
add_suggest(...) Button that simulates the user sending a message.

Reply Keyboards

Unlike inline keyboards, reply keyboards replace the user's system keyboard with buttons shown at the bottom of the chat. Tapping a button either sends its text as a regular message or triggers a system action, such as sharing a phone number or location.

self.chain.set_keyboard(
    ReplyKeyboard(one_time_keyboard=True)
        .add_text("Hello!")
        .add_text("Hi")
    .row()
        .add_contact("๐Ÿ“ฑ Share phone")
        .add_location("๐Ÿ“ Share location")
)

Command Parameters

Telekit can parse and validate command parameters for you.

from telekit.parameters import *

class GreetHandler(telekit.Handler):
    @classmethod
    def init_handler(cls) -> None:
        cls.on.command("greet", params=[Int(), Str()]).invoke(cls.handle)

    def handle(self, age: int | None = None, name: str | None = None):
        if age is None or name is None:
            self.chain.sender.set_text("Usage: /greet <age> <name>")
        else:
            self.chain.sender.set_text(f"Hello, {name}! You are {age} years old. Next year you'll turn {age + 1} ๐Ÿ˜…")
        self.chain.send()

Now /greet 64 "Alice Reingold" or /greet 128 Dracula are parsed automatically.

[!NOTE] If arguments are invalid or missing, you simply receive None and decide how to respond.

Dialogue

Dialogs are built as a chain of steps. Each method waits for the user before continuing.

class DialogueHandler(telekit.Handler):

    @classmethod
    def init_handler(cls) -> None:
        cls.on.text("hello", "hi", "hey").invoke(cls.handle_hello)

    def handle_hello(self) -> None:
        self.chain.sender.set_text("๐Ÿ‘‹ Hello! What is your name?")
        if self.user.first_name:
            self.chain.set_entry_suggestions([self.user.first_name])
        self.chain.set_entry_text(self.handle_name)
        self.chain.send()

    def handle_name(self, name: str) -> None:
        self.user_name = name
        self.chain.sender.set_text("Nice! How are you feeling today?")
        self.chain.set_entry_text(self.handle_feeling)
        self.chain.send()

    def handle_feeling(self, feeling: str) -> None:
        self.chain.sender.set_text(f"Got it, {self.user_name.title()}! You feel: {feeling}")
        self.chain.set_inline_keyboard({"โ†บ Restart": self.handle_hello})
        self.chain.send()

How it works:

  • The handler reacts to "hello", "hi", or "hey" (lowercase, UPPERCASE, or mixed).
  • handle_hello asks for the user's name.
  • set_entry_suggestions attaches the user's Telegram first_name as a suggestion button.
  • handle_name stores the name in self.user_name.
  • handle_feeling completes the flow and adds a "โ†บ Restart" button that routes back to the beginning.

It looks like regular Python. And reads like it too.

Sender

Want to add an image, document or an effect in a single line?

self.chain.sender.set_effect(Effect.HEART) # Add effect to message. Use enum or string
self.chain.sender.set_photo("robot.png") # Attach photo. URL, file_id, or path
self.chain.sender.set_document("README.md") # Attach document. URL, file_id, or path
self.chain.sender.set_text_as_document("Hello, this is a text document!") # Convert string to text document
self.chain.sender.send_chat_action(ChatAction.TYPING) # Send chat action. Use enum or string

[!NOTE] Telekit automatically decides whether to use bot.send_message or bot.send_photo based on the content

Styles

Telekit lets you describe formatting as objects instead of writing raw HTML or Markdown.

from telekit.styles import *

def handle(self) -> None:
    self.chain.sender.set_text(
        Bold("Text style examples:\n"),
        Stack(
            Bold("Bold text"),
            Italic("Italic text"),
            Bold(Italic("Bold + italic")),
            Link("Link", url="https://example.com"),
            BotLink("Deep link", username="MyBot", start="promo_42"),
            start="- {{index}}. ",
            sep=".\n",
        )
    )
    self.chain.send()

You describe structure. Telekit generates HTML or MarkdownV2 automatically:

<b>Text style examples:</b>

- 1. <b>Bold text</b>.
- 2. <i>Italic text</i>.
- 3. <b><i>Bold + italic</i></b>.
- 4. <a href="https://example.com">Link</a>.
- 5. <a href="https://t.me/MyBot?start=promo_42">Deep link</a>

No manual escaping. No broken formatting because of one missing character.

Telekit DSL

If you prefer not to write dialog logic in Python, you can use the built-in DSL with Jinja support.

import telekit

class QuizHandler(telekit.DSLHandler):
    @classmethod
    def init_handler(cls) -> None:
        cls.analyze_string(script)
        cls.on.command("start").invoke(cls.start_script)

script = """
$ timeout {
    time = 20; // 20 sec.
}

@ main {
    title   = "๐ŸŽ‰ Fun Facts Quiz";
    message = "Test your knowledge with 10 fun questions!";

    buttons {
        next("Start Quiz");
    }
}

@ question_1 {
    title   = "๐Ÿถ Question 1";
    message = "Which animal is the fastest on land?";
    buttons {
        _lose("Elephant");
        next("Cheetah");       // correct answer
        _lose("Horse");
        _lose("Lion");
    }
}

/* ... */
"""

telekit.Server(BOT_TOKEN).polling()

Key features of the Telekit DSL:

  • Scene-based architecture
  • Anonymous scenes
  • Automatic navigation stack management
  • Input handling
  • Images support and link buttons
  • Template variables
  • Custom variables
  • Hooks (Python API integration)
  • Jinja template engine
๐ŸŽ† Click to see what you can do with the DSL
Telekit Example 7 Telekit Example 8
Telekit Example 7 Telekit Example 8

[!TIP] You can find a full quiz example and DSL reference in the repository.

Traits

Traits are reusable behavior modules you can mix into any handler.

This example demonstrates the simplest way to use the built-in CalendarPick trait. It allows a user to pick a date from an inline calendar and handles the result via a callback.

from telekit.traits import CalendarPick

class CalendarHandler(CalendarPick, telekit.Handler):

    @classmethod
    def init_handler(cls) -> None:
        cls.on.command("calendar").invoke(cls.handle)

    def handle(self) -> None:
        self.chain.sender.set_title("๐Ÿ“… Choose a date")
        self.chain.sender.set_message("Select any date โ€” past or future:")
        self.chain.sender.set_remove_text(False)

        self.calendar_pick(self.handle_date) # HERE

    def handle_date(self, date: datetime.date) -> None:
        self.chain.sender.set_text(f"You picked: {date}")
        self.chain.send()
Result
Telekit Calendar Example

Example Bot

You can launch an example bot by running the following code:

import telekit

telekit.example(YOUR_BOT_TOKEN)

It includes example commands, dialogs, keyboards, and style usage.

Why Telekit

  • No FSM โ€” just chains.
  • Declarative, behavior-focused bot logic with minimal boilerplate.
  • Automatic callback routing and input handling.
  • Styles API for rich text (Bold, Italic, Links) with automatic escaping.
  • Deep linking and typed command parameters.
  • Built-in DSL for menus, FAQs, and simple bots.
  • Reusable Traits for composable, plug-and-play behavior (for example, a built-in declarative calendar picker).
  • Zero-code Obsidian Canvas mode.
  • Seamless integration with pyTelegramBotAPI.

Telekit doesn't try to be everything.
It tries to make Telegram bot development easier.

[!TIP] If you're interested and want to learn more, check out the Tutorial


Changes in version 2.5.0rc1

v2.5.0b0

  • Added support for t-strings (PEP 750, Python 3.14+) in TextEntity.

v2.5.0b1

  • Added Sender.send_message method.
  • Added utils.make_mention utility for generating tg://user?id= mention links.
  • Added new inline button types to inline_buttons:
    • ContactButton โ€” mentions a user by Telegram ID via tg://user?id=.
    • UserLinkButton โ€” opens a user profile by username; supports pre-filled message text.
    • BotLinkButton โ€” opens a bot by username; supports deep-link ?start= payload.
  • Added new methods to InlineKeyboard:
    • add_contact โ€” adds a ContactButton.
    • add_user_link โ€” adds a UserLinkButton.
    • add_bot_link โ€” adds a BotLinkButton.

v2.5.0b2

  • Added the escape parameter to telekit.utils.*:
    • make_user_link
    • make_bot_link
  • Handler.handlers_dict now excludes private handlers (classes whose names start with _).
  • Added Debug.duplicate_handler_warnings to warn about duplicate handler names during initialization.
  • Added Handler.chat object (BETA)

v2.5.0b3

  • Added utils.Markers class
  • Added HTMLText class for handling Telegram HTML strings with tag-aware indexing and slicing.
  • Added PaginatedText trait for displaying long HTML text in a paginated format, supporting navigation and smart splitting.
  • Added __radd__ to TextEntity: "Regular" + Bold(" and Bold")
  • Added __mul__ to TextEntity: Bold("Text") * 3
  • Added enabled= parameter to TextEntity: Bold("bold text", enabled=is_text_bold)
  • Added TextBuilder class โ€“ a fluent message composition API mirroring InlineKeyboard's builder pattern
  • Added styles to telekit.types
  • Added utils.CyclicList
  • Fixed _answer_callback_query to always call bot.answer_callback_query(), even without a popup text

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

telekit-2.5.0rc1.tar.gz (165.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

telekit-2.5.0rc1-py3-none-any.whl (192.6 kB view details)

Uploaded Python 3

File details

Details for the file telekit-2.5.0rc1.tar.gz.

File metadata

  • Download URL: telekit-2.5.0rc1.tar.gz
  • Upload date:
  • Size: 165.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.11

File hashes

Hashes for telekit-2.5.0rc1.tar.gz
Algorithm Hash digest
SHA256 7d929078ef6fcb72f011bc09e5da9a21ae75cfe56fa7d963e0c25a9b223c57b8
MD5 f9f4818cbcc0e2e6a289cadab718e19a
BLAKE2b-256 a6cd10b9325ecf582346ed8ca3e30cdcc804737f492b8e6ccffa64578d76b1d0

See more details on using hashes here.

File details

Details for the file telekit-2.5.0rc1-py3-none-any.whl.

File metadata

  • Download URL: telekit-2.5.0rc1-py3-none-any.whl
  • Upload date:
  • Size: 192.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.11

File hashes

Hashes for telekit-2.5.0rc1-py3-none-any.whl
Algorithm Hash digest
SHA256 901b75f230d9c27a8adc14e932028570a4cbd6e771407418a3f3027475db23a9
MD5 9eed6b3ad58579959202874bd7764af7
BLAKE2b-256 5b9ce8a0ff51220a0bbe7a8e5c9461f2074a5ace7a381ecd6b162234145b8cdf

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page