Changelog¶
Changelog¶
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased¶
0.4.0 - 2026-06-04¶
Added¶
- Synchronous, storage-free build surface on
MenuBuilderfor application-free / budget-enforced menus:to_markup()returns atelegram.InlineKeyboardMarkup,to_raw()returns a plain Telegram Bot API dict ({"inline_keyboard": [[...]]}) built directly from items, andassert_inline()is a cheap pre-flight check. All three run with no event loop, no storage access, and no worker thread: every button is encoded inline and any item that would otherwise spill to storage raisesEncodingError(usebuild_async()for those). The newon_oversize="spill" | "error"constructor policy makesbuild_async()raise (via the new syncCallbackEncoder.encode_inline()) instead of spilling when set to"error". - Atomic set-if-absent primitive
StorageBackend.add(key, data, ttl=None) -> boolon the storage Protocol, with a non-atomic default implementation onBaseStorage(existing custom backends keep working) and concurrency-safe per-backend overrides:MemoryStorage(noawaitbetween check and store, expired keys reclaimable),RedisStorage(server-sideSET ... NX), andSQLAlchemyStorage(single transaction that frees expired rows then dialect INSERT no-op-on-conflict). On top of it,MenuRoutergains single-winner claim helpersclaim(key, user_id, *, ttl=3600),who_claimed(key), andrelease(key). - New
telegram_menu_builder.testinghelper module (stdlibunittest.mockonly, imported as a submodule):simulate_tap(router, callback_data, ...)andtap(router, handler, /, **params)fabricate a mock callback-queryUpdate, route it, and return aTapResult(answered,answer_text,edited_text,handler_error);assert_inline(target)verifies that aMenuBuilder,InlineKeyboardMarkup, or raw dict contains only inlineI:/IC:callback buttons (noS:/P:storage references).
0.3.0 - 2026-06-04¶
Added¶
- Built-in async Redis storage backend (
RedisStorage) onredis-py, with first-class Valkey support (RESP-compatible — point aredis://URL at a Valkey server); native server-side TTL, namespaced keys, and owns-vs-borrows client lifecycle. Valkey is the recommended option, and the backend is verified end-to-end against live Redis 7.4.9 and Valkey 8.1.8 (parametrized suite gated byTMB_TEST_REDIS_URL/TMB_TEST_VALKEY_URL, falling back tofakeredis). - Built-in async SQL storage backend (
SQLAlchemyStorage) for PostgreSQL/Supabase, MySQL/MariaDB, and SQLite via SQLAlchemy 2.0 (async); new optional extras[postgres]and[mysql]; explicit schema management viacreate_schema()/drop_schema(). SQLAlchemyStorageis now verified end-to-end against live PostgreSQL 16 and MariaDB 12.3.2. The SQL test suite (tests/test_sql_storage.py) is parametrized to run the full behavioral suite against any database given byTMB_TEST_POSTGRES_URL/TMB_TEST_MYSQL_URL, falling back to in-memory SQLite by default.
Changed¶
- The
[sql]extra now pinssqlalchemy[asyncio]>=2.0.30,<3.0. - Documentation refreshed across the SQL storage, installation, dependency-audit, and
development guides: the per-DB driver matrix (including the pure-Python
aiomysqlalternative toasyncmy), UTCexpires_athandling, portableget_stats, and Docker-based integration testing. Corrected the documentedpython-telegram-botrange to>=20.0,<22.8.
0.2.0 - 2026-06-03¶
Fixed¶
- Critical: callback data is no longer lost when a menu is built inside a running
event loop.
add_item()previously discarded the handler and parameters in async contexts, producing buttons with emptycallback_data; encoding is now deferred tobuild_async()so every item is encoded correctly. MenuBuilder.build()now works when called inside a running event loop (it runs the async build on a short-lived worker thread) instead of raisingRuntimeError. In async code, preferawait build_async().add_submenu()no longer stores the non-serializable submenu builder in callback params (which broke encoding the moment it ran); only a JSON-safe_submenu_idis stored, with the builder kept in an internal registry accessible via the newget_submenu().- Corrected
__version__, which was hard-coded to0.1.0while the package was0.1.1; it is now single-sourced from the installed package metadata. - Hardened the deterministic dedup hash in
CallbackEncoderwithhashlib.md5(..., usedforsecurity=False)(resolves Bandit B324).
Added¶
- Exported the exception hierarchy (
MenuBuilderError,EncodingError,DecodingError,StorageError,ValidationError) from the package root. MenuBuilder.get_submenu()to retrieve a registered submenu builder by id.- MkDocs Material documentation site (
mkdocs.yml,docs/), including a dependency/CVE audit page and a Python-compatibility analysis. - Tests for the router, storage backends, type validators, and package metadata (overall coverage raised from ~62% to ~90%).
- Continuous-integration workflow (ruff, black, mypy, pyright, pytest, pip-audit) on every push and pull request, plus a documentation-deploy workflow.
- Project metadata:
SECURITY.md, Dependabot configuration, and issue/PR templates. - Claude Code maintenance tooling (
CLAUDE.md,AGENTS.md,.claude/agents and skills).
Changed¶
- Raised the pydantic floor to
>=2.4to exclude CVE-2024-3772 (ReDoS in pydantic email validation, fixed in 2.4.0). The library does not use email validation, so it was never exploitable here; the bump is defense-in-depth. StorageStrategynow subclassesenum.StrEnum.- Refactored the navigation-button methods and
MenuRouter.route()for clarity with no behavior change; removed the# noqa: PLR0912suppression. - Added a
[docs]optional-dependency group,pip-auditto[dev], and a[tool.bandit]configuration.
0.1.1 - 2025-11-08¶
Added¶
- Comprehensive guide for using MenuBuilder with ConversationHandler
- New example
conversation_handler_menu.pydemonstrating properper_messageconfiguration - Documentation file
docs/conversation_handler_guide.mdwith best practices - Support for python-telegram-bot up to version 22.5
Changed¶
- Updated dependency range:
python-telegram-bot>=20.0,<22.6(was<22.0) - Clarified ConversationHandler integration requirements in documentation
Fixed¶
- Documented solution for
CallbackQueryHandlerin fallbacks not working withper_message=False - Added warning about PTBUserWarning when using incompatible handler types with
per_message=True
0.1.0 - 2025-11-08¶
Added¶
- Initial alpha release
- Core functionality for menu building
- MenuBuilder with fluent API
- CallbackEncoder with intelligent storage strategies
- MenuRouter for callback handling
- MemoryStorage backend
- Type-safe interfaces with Pydantic v2
- Full Pyright/MyPy type checking
- Comprehensive test suite
- Example applications
- Documentation