Skip to main content

A powerful builder library for creating recursive inline keyboard menus in python-telegram-bot

Project description

Telegram Menu Builder

PyPI version Python 3.12+ License: MIT CI Type checked: mypy + pyright Linting: ruff

A type-safe, async-first Python library for building recursive inline keyboard menus in python-telegram-bot v20+. You declare buttons with a fluent MenuBuilder, the library encodes each callback payload to fit Telegram's 64-byte limit (compressing inline or spilling to pluggable storage), and a MenuRouter decodes incoming callbacks and dispatches them to your handlers.

📖 Documentation: https://smoxy.github.io/telegram-menu-builder/ — or jump to the doc index below.

Status: alpha (0.x). The API may change before 1.0. See the changelog.

✨ Features

  • 🏗️ Fluent builder API — chainable, readable menu construction.
  • 📦 Smart callback encoding — automatic inline / short-term / persistent strategy to stay under Telegram's 64-byte limit, with zlib compression and deduplication.
  • 🧭 Routing & middlewareMenuRouter dispatches callbacks to named handlers with before / after / on_error hooks and handler groups.
  • 🔄 Unlimited nesting — submenus and navigation (back / next / exit / cancel) buttons.
  • 🧩 Pluggable storage — built-in in-memory, async SQL, and Redis/Valkey backends are included; or implement the StorageBackend protocol (or subclass BaseStorage) for your own.
  • 🔐 Strict typing — full type hints, validated with both mypy --strict and pyright (Pydantic v2 models), shipped with py.typed.
  • 🧪 Well tested — ~90% coverage, CI on every push and pull request.

🚀 Quick start

pip install telegram-menu-builder

Optional extras: telegram-menu-builder[redis] (built-in Redis/Valkey backend), [sql] (plus [postgres] / [mysql] drivers), [dev], [docs]. See Installation.

from telegram import Update
from telegram.ext import Application, CallbackQueryHandler, ContextTypes
from telegram_menu_builder import MenuBuilder, MenuRouter

router = MenuRouter()


async def show_settings(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    menu = (
        MenuBuilder()
        .add_item("🌍 Language", handler="set_language")
        .add_item("👤 Profile", handler="edit_profile", user_id=update.effective_user.id)
        .columns(2)
        .add_back_button()
        .build()
    )
    await update.message.reply_text("⚙️ Settings", reply_markup=menu)


@router.handler("set_language")
async def handle_language(update: Update, context: ContextTypes.DEFAULT_TYPE, params: dict) -> None:
    # `params` are the values you passed to add_item(...), decoded from the callback.
    await update.callback_query.edit_message_text("Choose a language…")


app = Application.builder().token("YOUR_TOKEN").build()
app.add_handler(CallbackQueryHandler(router.route))
app.run_polling()

Tip: in async code, prefer await builder.build_async(). build() is a synchronous convenience wrapper (it also works inside a running event loop). See Building menus.

📚 Examples at a glance

Buttons carry arbitrary parameters — they are encoded into the callback and decoded back into the params dict your handler receives:

menu = (
    MenuBuilder()
    .add_item("📝 Edit", handler="edit_user", user_id=123, field="email")
    .add_item("🗑️ Delete", handler="delete_user", user_id=123, confirm=True)
    .columns(1)
    .add_back_button(handler="user_list", page=2)
    .build()
)

Nested submenus:

users = MenuBuilder().add_item("Add user", handler="add_user").add_back_button()
main = MenuBuilder().add_submenu("👥 Users", users)

Storage backends — in-memory, SQL, and Redis/Valkey backends ship built-in; for anything else, subclass BaseStorage (or satisfy the StorageBackend protocol) and pass it in:

from telegram_menu_builder import MenuBuilder
from telegram_menu_builder.storage import RedisStorage  # one client, also speaks Valkey

builder = MenuBuilder(storage=RedisStorage(url="redis://localhost:6379/0"))
# custom backends: docs/advanced/custom-storage.md

See the runnable examples/ and the guides for complete, copy-pasteable bots — including ConversationHandler integration.

🧠 How callback encoding works

Telegram limits callback_data to 64 bytes. The library chooses a strategy automatically:

  1. Inline (fits in 64 bytes) — JSON → zlib → base64 directly in the callback.
  2. Short-term — stored in the backend with a TTL; the callback carries a short reference.
  3. Persistent — stored without expiry for large/long-lived payloads.

Identical payloads reuse the same key (deduplication). Full details: Callback encoding internals.

📖 Documentation

📚 Rendered site: https://smoxy.github.io/telegram-menu-builder/

Get started Guides Advanced Reference
Installation Building menus Encoding internals MenuBuilder
Quick start Routing callbacks Custom storage MenuRouter
Storage backends Types & models
ConversationHandler Encoding · Storage

Project docs: Changelog · Security policy · Dependency & CVE audit · Python compatibility · Development guide · Contributing

🤝 Contributing

Every contribution is welcome — bug reports, documentation, examples, and code alike. Start with the Contributing guide and the Development guide.

git clone https://github.com/smoxy/telegram-menu-builder.git
cd telegram-menu-builder
python -m venv .venv && source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
pre-commit install
make test          # run the suite (≈90% coverage)

Before opening a pull request, make sure the gates pass:

make lint          # ruff + black --check
make type-check    # mypy --strict + pyright
make test          # pytest with coverage

Releases are published to PyPI automatically via GitHub Releases and trusted publishing — no manual token handling needed.

🔐 Security

Found a vulnerability? Please report it privately — see SECURITY.md. Dependency CVEs are tracked in the dependency audit (for example, pydantic is pinned >=2.4 to exclude CVE-2024-3772).

🗺️ Roadmap

  • ✅ Fluent builder, navigation, submenus
  • ✅ Smart callback encoding (inline / short-term / persistent) with compression & dedup
  • MenuRouter with middleware and handler groups
  • ✅ In-memory storage backend, strict typing, CI
  • ✅ Built-in SQL backend via SQLAlchemy (async) for PostgreSQL/Supabase, MySQL/MariaDB, and SQLite (install [sql], plus [postgres] or [mysql] for those drivers — see storage backends)
  • ✅ Built-in Redis backend via redis-py that also speaks Valkey (the RESP-compatible Redis fork we recommend), verified against Redis 7.4.9 & Valkey 8.1.8 — install [redis] (see Redis/Valkey storage)
  • 📅 Helpers for pagination and form/wizard flows

📝 License

Released under the MIT License — see LICENSE. Free for commercial, private, and personal use, modification, and distribution. Using this code to train AI/ML models is explicitly permitted. Attribution is appreciated but not required.


Made with ❤️ for the Telegram Bot community · python-telegram-bot v20+ · Pydantic v2

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

telegram_menu_builder-0.3.0.tar.gz (77.9 kB view details)

Uploaded Source

Built Distribution

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

telegram_menu_builder-0.3.0-py3-none-any.whl (35.8 kB view details)

Uploaded Python 3

File details

Details for the file telegram_menu_builder-0.3.0.tar.gz.

File metadata

  • Download URL: telegram_menu_builder-0.3.0.tar.gz
  • Upload date:
  • Size: 77.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for telegram_menu_builder-0.3.0.tar.gz
Algorithm Hash digest
SHA256 56b381e08ad52a15bea18cefb4a6e13ffc6cf9016eadb450fc236ab6281d9b5e
MD5 bf953538cccd6569f0fbec9053555137
BLAKE2b-256 382802eef9d01f11c494cd1c7ce5cea7cfd41332112b93f96f0e9df76d72b422

See more details on using hashes here.

Provenance

The following attestation bundles were made for telegram_menu_builder-0.3.0.tar.gz:

Publisher: python-publish.yml on smoxy/telegram-menu-builder

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file telegram_menu_builder-0.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for telegram_menu_builder-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e894b1955a221c0c4338ab238a519c966410e471e8c1a1fadeceb7929b22472b
MD5 f4f8908b5008483973527889c254c914
BLAKE2b-256 543e7d5f99d1818341241cb160ef8eebcc4369b7d07ff9c627fc7dd8fb249eaf

See more details on using hashes here.

Provenance

The following attestation bundles were made for telegram_menu_builder-0.3.0-py3-none-any.whl:

Publisher: python-publish.yml on smoxy/telegram-menu-builder

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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