This release is a pre-release and may not be stable for production use.
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:
- Chains handle complex conversations without state machines
- Ready-to-use DSL for FAQs and interactive scripts
- Automatic message formatting via Sender and callback routing
- Deep Linking with type-checked Command Parameters
- Built-in Permission and Logging system
- Reusable Traits for pluggable behavior modules
- Works with pyTelegramBotAPI
GitHub PyPI Telegram Community
Contents
Overview
In Telekit, dialogs read like normal method calls. 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()
handle sends a message and registers handle_name as the next step with set_entry_text. When the user replies, Telekit calls handle_name and passes the reply as a plain str.
That's it. No enums. No manual state tracking. No boilerplate.
Inline Keyboards
The fastest way to add buttons to a message. Pass a dict where each key is the button label and each value is the callback to run 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 │
╰─────────────┴──────────────────╯
For precise row layout or conditional buttons, use InlineKeyboard, a builder you compose 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 │
╰─────────────────────╯
InlineKeyboardis 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
Reply keyboards replace the system keyboard with buttons at the bottom of the chat. Tapping one sends its text as a message, or triggers a system action like 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 128 Dracula or even /greet 64 "Alice Reingold" are parsed automatically.
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"in any case. handle_helloasks for the user's name.чset_entry_suggestionsattaches the user's Telegramfirst_nameas a suggestion button.handle_namestores the name inself.user_name.handle_feelingcloses the flow and adds a "↺ Restart" button that routes back to the start.
It reads like regular Python because it is regular Python.
Sender
Want to attach an image, document or add 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
Styles
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 turns that structure into HTML or MarkdownV2:
<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>
You skip manual escaping and the broken formatting one stray character causes.
Telekit DSL
Prefer not to write dialog logic in Python? 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
Traits
Traits are reusable behavior modules you can mix into any handler.
Here's the built-in CalendarPick trait: a user picks a date from an inline calendar, and a callback handles the result.
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
Example Bot
Run this to launch an example bot:
import telekit
telekit.example(YOUR_BOT_TOKEN)
It includes example commands, dialogs, keyboards, and style usage.
Why Telekit
- Chains instead of an FSM.
- Declarative, behavior-focused bot logic with minimal boilerplate.
- Automatic callback routing and input handling.
- A Styles API for rich text (
Bold,Italic, links) with automatic escaping. - Deep linking and typed command parameters.
- A built-in DSL for menus, FAQs, and simple bots.
- Reusable Traits for composable behavior, including a built-in calendar picker.
- Zero-code mode Obsidian Canvas mode.
- Works with
pyTelegramBotAPI.
Telekit focuses on one job: making Telegram bot development easier.
Changes in version 2.6.0a3
v2.6.0 a3
- Added support for Rich HTML messages (Bot API 10.1+) and enhance styles
- Introduced a new module
_rich.pyto handle Rich HTML parsing and serialization. - Updated
styles.pyto include new rich tags for enhanced text formatting. - Created
rich.pyexample handler to demonstrate the usage of Rich HTML features, including inline styles, blocks, lists, tables, media, and sender integration.
- Introduced a new module
v2.6.0 a2
- Reworked
.envand token/canvas file reading inutils: full.envsyntax support (comments,export, quotes and escapes, multi-line values,$VARinterpolation), acacheparameter (defaultTrue) withclear_cache(), detailed errors with fix suggestions and creation commands, and newEnv*Errorexceptions;load_envnow raisesEnvFileNotFoundErrorfor a missing file instead of returning{}
v2.6.0 a1
- Added a module-loading utility in
utils
v2.6.0 a0
- Improved formatting in
_on.py
Metadata
Release files for telekit 2.6.0a3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| telekit-2.6.0a3.tar.gz | 187.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| telekit-2.6.0a3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 403.1 kB
Release files / telekit-2.6.0a3.tar.gz
| Download URL | telekit-2.6.0a3.tar.gz |
|---|---|
| Size | 187.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
da83248ac794f08acd4aa26ca16a4b04455d55a4ec3465ed28ac7fcaf9ac9d93
|
|
BLAKE2b-256 checksum How to use checksums |
6e50d296787b20b6d34c6204ff2bbd9f4ee0dc5a6f9a3129826304da00a0a9fc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.11
|
Release files / telekit-2.6.0a3-py3-none-any.whl
| Download URL | telekit-2.6.0a3-py3-none-any.whl |
|---|---|
| Size | 215.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3a9ed4adcdca83e2bd3a0f7e7ae040a79059a61cd218560c38720fdb1bcb9c61
|
|
BLAKE2b-256 checksum How to use checksums |
9cbf25b72d63f6fd7e08819be1a59b1fab62451c91db49c0378bdf9cc2f7161d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.11
|