Skip to main content

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

MIT


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 Магазин с корзиной, формами, валидацией и бизнес-логикой

Metadata

Release files for aiogram-ui-router 0.0.11

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for aiogram-ui-router 0.0.11
File Size Uploaded
aiogram_ui_router-0.0.11.tar.gz 140.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for aiogram-ui-router 0.0.11
File Interpreter ABI Platform
aiogram_ui_router-0.0.11-py3-none-any.whl Python 3 none any Details

Total release size: 253.9 kB

Release files / aiogram_ui_router-0.0.11.tar.gz

Download URL aiogram_ui_router-0.0.11.tar.gz
Size 140.8 kB
Tags Source
SHA-256 checksum
How to use checksums
b5c1406cba4cd7af53a9ed09f949ad88581fc24d622ad3e8b4076e51207c6f64
BLAKE2b-256 checksum
How to use checksums
9f230dd3f359ac4c908d26d59736cd8a41a22cd1275bdedc274d79ed78c0a1eb
Upload date
Uploaded using Trusted Publishing?
What is 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}

Release files / aiogram_ui_router-0.0.11-py3-none-any.whl

Download URL aiogram_ui_router-0.0.11-py3-none-any.whl
Size 113.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1a54c7397524a0addbe6a1aa1eb5e9b632336500fce0c1f05c688ca47a7ad709
BLAKE2b-256 checksum
How to use checksums
6a588249e0eb7c0ddf174c5db7ce5fdcbf926906986e6adfccbdf92c639fc099
Upload date
Uploaded using Trusted Publishing?
What is 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}

Release history Release notifications | RSS feed

This release

0.0.11 This release

2 release files

0.0.10

2 release files

0.0.9

2 release files

0.0.8

2 release files

0.0.7

2 release files

0.0.6

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page