Declarative UI routing framework for aiogram 3 — define Telegram bot interfaces through Pydantic schemas
Project description
aiogram-ui-router
Declarative UI routing framework for aiogram 3. Define complex Telegram bot interfaces through Pydantic schemas instead of writing handler code.
Features
- Declarative scenes — describe screens, keyboards, content and navigation as data
- Scene navigation — tree-based navigation with history, back button support
- Dynamic content — text templates with flags, variables and conditional rendering
- Inline & reply keyboards — static and dynamic keyboards with pagination
- Rule engine — declarative conditions for handlers and content
- Variables — bot/user/chat-scoped state with typed values and TTL
- Events — internal pub/sub event bus with scheduled events (cron, interval)
- Localization — multi-language support via Mozilla Fluent
- Global handlers — priority-based handlers that work across all scenes
- Input validation — custom validators for user text input
- Analytics — track scene views, button clicks, navigation flows
- Schema validation — detect broken references, unreachable scenes, missing functions
- Hot reload — update UI schema without restarting the bot
- Multi-router — multiple independent UI routers per bot
Installation
pip install aiogram-ui-router
With structured logging:
pip install aiogram-ui-router[logging]
Quick Start
import asyncio
from aiogram import Bot, Dispatcher
from aiogram.filters import CommandStart
from aiogram.fsm.storage.memory import MemoryStorage
from aiogram.types import Message
from ui_router import (
UIRouter, UIRouterExecutor, Scene, Handler, ActionInstruction,
ActionType, EventType, TransitionType, MessageContent,
Keyboard, Button, Flag, FlagGetter, DynamicContent, ContentTemplate,
SharedServices, InMemoryNavigationStorage, InMemoryVariableRepository,
EventBus, InMemoryEventScheduler,
)
async def get_user_name(context) -> str:
return context.event_data["event_from_user"].first_name
schema = UIRouter(
name="my_bot",
version="1.0.0",
initial_scene="welcome",
scenes=[
Scene(
id="welcome",
name="Welcome",
flags=[
Flag(
name="name",
getter=FlagGetter(type="function", function="get_user_name"),
),
],
default_content=MessageContent(
text=DynamicContent(
type="template",
template=ContentTemplate(
template="Hello, {name}!",
flags=["name"],
),
),
),
default_keyboard=Keyboard(
is_inline=True,
buttons=[
[Button(text="About", callback_action="goto_about")],
],
),
handlers=[
Handler(
name="goto_about",
event_type=EventType.CALLBACK,
actions=[
ActionInstruction(
type=ActionType.GOTO_SCENE,
scene_id="about",
transition=TransitionType.EDIT_SMART,
),
],
),
],
),
Scene(
id="about",
name="About",
default_content=MessageContent(text="Built with aiogram-ui-router"),
default_keyboard=Keyboard(
buttons=[[Button(text="Back", callback_action="go_back")]],
),
handlers=[
Handler(
name="go_back",
event_type=EventType.CALLBACK,
actions=[ActionInstruction(type=ActionType.BACK)],
),
],
),
],
)
async def main() -> None:
bot = Bot(token="YOUR_TOKEN")
dp = Dispatcher(storage=MemoryStorage())
shared = SharedServices(
navigation_storage=InMemoryNavigationStorage(),
variable_repository=InMemoryVariableRepository(),
event_bus=EventBus(),
event_scheduler=InMemoryEventScheduler(event_callback=lambda e: None),
)
executor = UIRouterExecutor(schema=schema, shared=shared)
executor.registry.getters.register("get_user_name", get_user_name)
dp.include_router(executor.get_router())
@dp.message(CommandStart())
async def start(message: Message, **kwargs) -> None:
await executor.start(message.from_user.id, message, **kwargs)
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())
Core Concepts
Scenes
A scene is a screen in your bot UI. Each scene has default content, a keyboard, flags (dynamic data) and handlers (reactions to user actions).
Handlers
Handlers respond to events within a scene. Each handler has a list of sequential actions: navigate to another scene, send/edit messages, save input, call business logic, emit events, etc.
Flags
Flags provide dynamic data for content templates. They can be static values, async function results, FSM state, user input, variables, or conditional (rule-based).
Actions
Over 20 action types: GOTO_SCENE, BACK, SEND_MESSAGE, EDIT_MESSAGE, DELETE_MESSAGE, SAVE_INPUT, SET_VARIABLE, BUSINESS_ACTION, SCHEDULE_EVENT, EMIT_EVENT, ANSWER_CALLBACK, ANSWER_INLINE_QUERY, CUSTOM, and more.
Variables
Typed key-value storage scoped to bot, user or chat. Supports int, float, bool, str, datetime, json types with optional TTL and default values.
Rule Engine
Declarative condition evaluation using operators: eq, ne, gt, gte, lt, lte, in, not_in, contains, starts_with, ends_with. Used for conditional content, flags and handler filtering.
Examples
See the examples/ directory:
| Example | Description |
|---|---|
example.py |
Basic setup with scenes, flags and navigation |
example_advanced.py |
Shop with cart, forms, validation and business logic |
Requirements
- Python >= 3.13
- aiogram >= 3.25
License
aiogram-ui-router (RU)
Декларативный UI-фреймворк для aiogram 3. Описывайте интерфейс Telegram-бота через Pydantic-схемы вместо написания хендлеров вручную.
Возможности
- Декларативные сцены — экраны, клавиатуры, контент и навигация описываются как данные
- Навигация — древовидная навигация с историей и кнопкой «Назад»
- Динамический контент — шаблоны с флагами, переменными и условным рендерингом
- Клавиатуры — inline и reply, статические и динамические с пагинацией
- Движок правил — декларативные условия для хендлеров и контента
- Переменные — типизированное состояние на уровне бота/пользователя/чата с TTL
- События — внутренняя шина событий с планировщиком (cron, interval)
- Локализация — мультиязычность через Mozilla Fluent
- Глобальные хендлеры — обработчики с приоритетами, работающие во всех сценах
- Валидация ввода — кастомные валидаторы для пользовательского текста
- Аналитика — отслеживание просмотров сцен, кликов, навигации
- Валидация схемы — обнаружение битых ссылок, недостижимых сцен, отсутствующих функций
- Горячая перезагрузка — обновление UI-схемы без перезапуска бота
- Мульти-роутер — несколько независимых UI-роутеров на одного бота
Установка
pip install aiogram-ui-router
Со структурированным логированием:
pip install aiogram-ui-router[logging]
Быстрый старт
import asyncio
from aiogram import Bot, Dispatcher
from aiogram.filters import CommandStart
from aiogram.fsm.storage.memory import MemoryStorage
from aiogram.types import Message
from ui_router import (
UIRouter, UIRouterExecutor, Scene, Handler, ActionInstruction,
ActionType, EventType, TransitionType, MessageContent,
Keyboard, Button, Flag, FlagGetter, DynamicContent, ContentTemplate,
SharedServices, InMemoryNavigationStorage, InMemoryVariableRepository,
EventBus, InMemoryEventScheduler,
)
async def get_user_name(context) -> str:
return context.event_data["event_from_user"].first_name
schema = UIRouter(
name="my_bot",
version="1.0.0",
initial_scene="welcome",
scenes=[
Scene(
id="welcome",
name="Приветствие",
flags=[
Flag(
name="name",
getter=FlagGetter(type="function", function="get_user_name"),
),
],
default_content=MessageContent(
text=DynamicContent(
type="template",
template=ContentTemplate(
template="Привет, {name}!",
flags=["name"],
),
),
),
default_keyboard=Keyboard(
is_inline=True,
buttons=[
[Button(text="О боте", callback_action="goto_about")],
],
),
handlers=[
Handler(
name="goto_about",
event_type=EventType.CALLBACK,
actions=[
ActionInstruction(
type=ActionType.GOTO_SCENE,
scene_id="about",
transition=TransitionType.EDIT_SMART,
),
],
),
],
),
Scene(
id="about",
name="О боте",
default_content=MessageContent(text="Собран с помощью aiogram-ui-router"),
default_keyboard=Keyboard(
buttons=[[Button(text="Назад", callback_action="go_back")]],
),
handlers=[
Handler(
name="go_back",
event_type=EventType.CALLBACK,
actions=[ActionInstruction(type=ActionType.BACK)],
),
],
),
],
)
async def main() -> None:
bot = Bot(token="YOUR_TOKEN")
dp = Dispatcher(storage=MemoryStorage())
shared = SharedServices(
navigation_storage=InMemoryNavigationStorage(),
variable_repository=InMemoryVariableRepository(),
event_bus=EventBus(),
event_scheduler=InMemoryEventScheduler(event_callback=lambda e: None),
)
executor = UIRouterExecutor(schema=schema, shared=shared)
executor.registry.getters.register("get_user_name", get_user_name)
dp.include_router(executor.get_router())
@dp.message(CommandStart())
async def start(message: Message, **kwargs) -> None:
await executor.start(message.from_user.id, message, **kwargs)
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())
Примеры
Смотрите директорию examples/:
| Пример | Описание |
|---|---|
example.py |
Базовый бот: сцены, флаги, навигация |
example_advanced.py |
Магазин с корзиной, формами, валидацией и бизнес-логикой |
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file aiogram_ui_router-0.0.11.tar.gz.
File metadata
- Download URL: aiogram_ui_router-0.0.11.tar.gz
- Upload date:
- Size: 140.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b5c1406cba4cd7af53a9ed09f949ad88581fc24d622ad3e8b4076e51207c6f64
|
|
| MD5 |
860d09f0f52faf29e6f7f2aa1c43db8b
|
|
| BLAKE2b-256 |
9f230dd3f359ac4c908d26d59736cd8a41a22cd1275bdedc274d79ed78c0a1eb
|
File details
Details for the file aiogram_ui_router-0.0.11-py3-none-any.whl.
File metadata
- Download URL: aiogram_ui_router-0.0.11-py3-none-any.whl
- Upload date:
- Size: 113.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1a54c7397524a0addbe6a1aa1eb5e9b632336500fce0c1f05c688ca47a7ad709
|
|
| MD5 |
827bb67358578cded0da93f1c5386df2
|
|
| BLAKE2b-256 |
6a588249e0eb7c0ddf174c5db7ce5fdcbf926906986e6adfccbdf92c639fc099
|