Конструктор меню
Меню бота собирается в кабинете, экран «Конструктор меню»: слева дерево экранов, в центре редактор кнопки, справа живое превью чата. Сохранили — бот уже отвечает новым меню: дерево читается из БД при каждом рендере, рестарты не нужны.

Дерево экранов
Меню — дерево. Кнопки верхнего уровня — главное меню; кнопка типа «экран» открывает подменю со своим текстом сообщения, картинкой и собственными кнопками. Вложенность не ограничена, удаление узла убирает всё его поддерево. Порядок кнопок меняется стрелками ↑/↓ в редакторе. Рендер поддерживает ряды: кнопки с одинаковым row_index встают рядом, а не столбиком — так, например, свёрстано дефолтное меню.
Типы кнопок
| Тип | Что делает | Payload |
|---|---|---|
Экран (screen) | Открывает подменю со своим текстом (до 4096 символов) и опциональной картинкой | Текст экрана |
Действие (action) | Вызывает встроенный экран бота | Код действия |
Ссылка (link) | Открывает URL | https://… |
Мини-аппа (miniapp) | Открывает Mini App нативной web_app-кнопкой | — (URL берётся из настройки SUBSCRIPTION_MINI_APP_URL) |
Назад (back) | Поднимает на уровень вверх | — |
Коды действий бот отдаёт кабинету сам (GET /api/admin/bot-menu/actions), выбирать можно из полного каталога: buy, subscription, connect, devices, balance, history, promocode, referral, trial, cabinet, nodes, proxy, support. Часть действий (подписка, подключение, устройства) требует активной подписки — бот сам разберётся, что показать юзеру без неё.
Экранам можно загрузить картинку или анимацию (jpg/png/webp, а также gif/mp4) — она показывается над текстом экрана. Анимация отправляется как animation (живой GIF), а не застывшим кадром. Подпись к медиа Telegram ограничивает 1024 символами; если файл недоступен, бот покажет экран текстом.
Цвета кнопок
Telegram Bot API поддерживает три фиксированных стиля кнопок, поэтому любой выбранный HEX-цвет мапится на ближайший стиль:
| Цвет | Стиль Bot API |
|---|---|
| Преобладает зелёный | success |
| Преобладает красный | danger |
| Остальные | primary |
| Пусто | стандартная кнопка |
В редакторе — палитра пресетов (#31A24C, #2E63E7, #E53935, #F59E0B, #7C5CFF, #111111) плюс поле для произвольного HEX (#RGB / #RRGGBB / #RRGGBBAA). Цвет всем кнопкам сразу задаёт настройка BUTTON_COLOR_DEFAULT; цвет конкретной кнопки её перекрывает.
Превью ≠ клиент Telegram
Превью в кабинете красит кнопку ровно в выбранный HEX. В самом Telegram кнопка получит один из трёх стилей выше — точный оттенок клиент не отобразит.
Эмодзи и стикеры на кнопках
Telegram не разрешает ставить стикеры или премиум-эмодзи внутрь inline-кнопок — в подписи кнопки только текст и обычные эмодзи (это ограничение платформы, обойти его нельзя). Обычный эмодзи ставится прямо в подпись кнопки — вставь любой в поле «Текст кнопки». Для «живого» брендинга над кнопками используй GIF/MP4-баннер (см. «Картинка / GIF экрана» выше и баннер главного меню через /setbanner ответом на GIF).
Живое превью и сохранение
Правая колонка — превью чата: текст выбранного экрана, его картинка и кнопки в заданных цветах. Клик по кнопке в превью выбирает её для редактирования. Кнопка «Сохранить» отправляет всё дерево целиком; сервер валидирует ссылки на родителей и отсутствие циклов, после чего меню сразу живое.
Дефолтное меню
Свежий магазин стартует не с пустого экрана: при первом запуске в конструктор сеется встроенное меню из трёх кнопок — «🛒 Купить VPN» (зелёная), «👤 Личный кабинет» и «🔌 Подключить» во втором ряду. Оно полностью редактируемое. Если владелец удалил все кнопки, бот показывает то же дефолтное меню как fallback. Вернуть исходный вариант можно в любой момент — действие «сбросить к дефолту» (POST /api/admin/bot-menu/reset-default) заменяет текущее дерево стартовым.
Кастомное меню не перетирается
При обновлениях платформа апгрейдит только нетронутый дефолт прежних версий. Меню, которое владелец менял, ни один деплой не трогает.
Кнопки «Личного кабинета»
Экран «Личный кабинет» (открывается кнопкой cabinet) — отдельный встроенный экран: сверху живой профиль (имя, подписка, баланс, дни, трафик), ниже — кнопки аккаунта. Раньше этот набор был фиксированным; теперь он редактируется прямо под конструктором меню, в блоке «Кнопки Личного кабинета»: выключи лишние тумблером, поменяй порядок стрелками, сохрани. Каталог: subscription, balance, history, referral, promocode, support.
Хранится в настройке CABINET_BUTTONS (список через запятую — как CONNECTION_APPS), так что то же самое можно править и в настройках текстом. Кнопки balance и referral дополнительно скрываются, если фича выключена (BALANCE_ENABLED / REFERRAL_ENABLED) — так отключённая функция не оставляет «мёртвую» кнопку. «Открыть приложение» и «‹ Меню» бот добавляет к экрану сам.
API: GET /api/admin/bot-menu/cabinet (каталог + включённые), PUT /api/admin/bot-menu/cabinet ({"order": [...]}).
Умные кнопки
Часть кнопок бот добавляет сам, только когда они применимы — их не нужно (и не получится) держать в дереве постоянно:
- 🎁 Попробовать бесплатно — если триал включён и ещё доступен юзеру, а кнопки
trialнет в дереве; - 🔌 MTProto-прокси — если включены
MTPROTO_PROXY_ENABLEDи заданMTPROTO_PROXY_URL; - 🌍 Статус серверов — если включён
NODE_STATUS_ENABLED; - 📱 Открыть приложение — заметная кнопка мини-аппы сверху меню, если задан
SUBSCRIPTION_MINI_APP_URLи владелец не поставил свою miniapp-кнопку; - 🛠 Админка — только персоналу.
Inline или reply
Настройка MAIN_MENU_MODE переключает главное меню между двумя режимами:
| Значение | Поведение |
|---|---|
inline (дефолт) | Кнопки под сообщением-баннером |
reply | Постоянная клавиатура-панель под полем ввода: кнопки верхнего уровня + умные кнопки + кнопка мини-аппы. Вложенные экраны остаются inline. Кнопки-ссылки в reply-клавиатуре становятся обычными текстовыми |
Связанные настройки
| Ключ | Дефолт | Что делает |
|---|---|---|
MAIN_MENU_MODE | inline | Режим главного меню: inline / reply |
BUTTON_COLOR_DEFAULT | пусто | Цвет всех кнопок по умолчанию (#HEX) |
SUBSCRIPTION_MINI_APP_URL | пусто | URL мини-аппы для web_app-кнопок |
CABINET_BUTTONS | все | Кнопки экрана «Личный кабинет» (порядок + какие показывать) |
WELCOME_STICKER | пусто | Стикер над /start (задаётся из бота командой /setsticker ответом на стикер) |
START_MESSAGE | «👋 Это твой личный VPN…» | Текст главного экрана (/start) |
Все они живут в настройках кабинета и применяются на лету.
