Бот записи на услуги ВКонтакте: FSM, слоты и панель администратора
Рабочий бот записи для ВК: выбор специалиста, свободные слоты кнопками, SQLite, подтверждение от админа. Код, скриншоты, деплой на сервер.
Содержание статьи
Клиент выбирает мастера, видит только свободное время, вводит телефон — и через секунду администратору уходит уведомление с кнопками «Подтвердить» и «Отклонить». Клиент получает ответ автоматически. Без звонков, без «напишем позже», без потерянных заявок.
Это не концепт. Это рабочий бот, который мы собрали и запустили — со скриншотами реальной переписки.
Это третья статья в цепочке:
- Где живут боты: пошаговый гайд по запуску VPS для новичков — выбираем и настраиваем сервер
- Деплой VK-бота на сервер: systemd, nginx и работа 24/7 — запускаем бота в продакшн
- Бот онлайн-записи: FSM, специалисты, подтверждение ← вы здесь
После первых двух статей у вас уже есть VPS и работающий systemd. Теперь кладём на него реальный бот.
Как выглядит бот в работе
Сначала картинка — потом код.
Клиент нажимает «Начать» и видит меню услуг. Никакого ввода текста, только кнопки:
После выбора услуги — только те специалисты, которые её ведут:
Дальше — доступные слоты. Занятые не показываются:
Затем бот спрашивает имя, телефон и комментарий текстом:
Администратор получает карточку записи и принимает решение кнопкой:
Клиент получает подтверждение и кнопку для повторной записи:
У администратора в любой момент доступна панель с расписанием:
Логика универсальна: барбершоп, салон красоты, автосервис, стоматология — меняется только список услуг и специалистов.
Хотите попробовать сами? Бот запущен в демо-группе — записаться можно прямо сейчас.
Архитектура проекта
vk-booking-bot/
├── bot.py # весь код бота
├── data.json # специалисты и расписание слотов
├── bookings.db # SQLite-база (создаётся автоматически)
├── requirements.txt
└── .env # токен и ID администратора
Зависимости:
pip install vkbottle==4.6.2 python-dotenv
Важно: vkbottle 4.6.x несовместима с Python 3.12+. Используйте Python 3.11.
Переменные окружения
VK_TOKEN=vk1.a.ваш_токен
MANAGER_PEER_ID=123456789
MANAGER_PEER_ID — числовой VK ID администратора. Узнать свой ID можно на странице vk.com/id0: вас перенаправит на вашу страницу, а в адресной строке будет ваш числовой ID.
Конфигурация без кода
Специалистов, рабочие часы и горизонт записи выносим в data.json — администратор меняет это без Python:
{
"specialists": {
"anna": {
"name": "Анна С.",
"services": ["haircut", "coloring", "styling"]
},
"igor": {
"name": "Игорь М.",
"services": ["haircut", "barber"]
},
"maria": {
"name": "Мария К.",
"services": ["coloring", "styling", "haircut"]
}
},
"slot_times": ["10:00", "12:00", "14:00", "16:00"],
"slot_days_ahead": 4
}
Что можно менять:
| Поле | Что это |
|---|---|
specialists | добавить/убрать мастера, изменить имя, привязать услуги |
slot_times | рабочие часы (например ["09:00", "11:00", "15:00", "17:00"]) |
slot_days_ahead | на сколько дней вперёд показывать запись |
После изменения — перезапуск:
sudo systemctl restart vk-booking-bot
Ограничение VK: в клавиатуре максимум 10 строк. При 4 днях × 4 слота = 16 кнопок → 8 строк + строка отмены = 9. Это предел. При
slot_days_ahead: 5бот упадёт с ошибкойbuttons contain too much rows.
FSM: как устроен сценарий
Бот ведёт пользователя по шагам через конечный автомат (FSM). Каждое состояние — ожидание конкретного ввода:
class BookingState(BaseStateGroup):
WAIT_SPECIALIST = "wait_specialist"
WAIT_DATE = "wait_date"
WAIT_NAME = "wait_name"
WAIT_PHONE = "wait_phone"
WAIT_COMMENT = "wait_comment"
запись / привет / кнопка «Начать»
│
▼
Выбор услуги (кнопки)
│
▼ WAIT_SPECIALIST
Выбор специалиста (фильтр по услуге)
│
▼ WAIT_DATE
Выбор слота (только свободные)
│
▼ WAIT_NAME → WAIT_PHONE → WAIT_COMMENT
Имя, телефон, комментарий (текст)
│
▼
✅ Запись → уведомление администратору
В любой момент — «отмена» или кнопка ❌ — сценарий сбрасывается.
Ключевое: порядок регистрации хендлеров
Это самая частая ловушка в vkbottle. Хендлеры проверяются строго в порядке регистрации, и catch-all без правил должен быть последним. Если поставить его раньше FSM-состояний — он поглотит все сообщения, и бот зависнет:
# ✅ Правильный порядок
# 1. Конкретные текстовые команды
@bot.on.message(text=["запись", "записаться", "отмена", ...])
# 2. Payload системной кнопки «Начать»
@bot.on.message(payload={"command": "start"})
# 3. FSM-состояния (текстовый ввод от пользователя)
@bot.on.message(StateRule(BookingState.WAIT_NAME))
@bot.on.message(StateRule(BookingState.WAIT_PHONE))
@bot.on.message(StateRule(BookingState.WAIT_COMMENT))
# 4. Catch-all для payload-кнопок — строго последним
@bot.on.message()
async def route_payload(message: Message):
if not message.payload:
return
# обработка кнопок услуги, специалиста, даты и действий админа
Фильтр специалистов по услуге
После выбора услуги бот показывает только подходящих специалистов:
def build_specialist_keyboard(service_key: str) -> str:
kb = Keyboard()
specialists = [
(k, v) for k, v in SPECIALISTS.items()
if service_key in v["services"]
]
for i, (key, spec) in enumerate(specialists):
kb.add(
Text(f"👤 {spec['name']}",
payload={"cmd": "specialist", "value": key}),
color=KeyboardButtonColor.PRIMARY,
)
if i % 2 == 1 and i < len(specialists) - 1:
kb.row()
kb.row()
kb.add(Text("❌ Отмена", payload={"cmd": "cancel"}),
color=KeyboardButtonColor.NEGATIVE)
return kb.get_json()
«Барбер» — увидит только Игорь М. «Окрашивание» — Анна и Мария. Случайный выбор исключён.
Свободные слоты в реальном времени
Занятые слоты запрашиваются из БД и автоматически исключаются из клавиатуры:
def get_booked_slots(specialist_key: str) -> set[str]:
with closing(sqlite3.connect(DB_PATH)) as conn:
rows = conn.execute(
"SELECT booking_datetime FROM bookings "
"WHERE specialist = ? AND status != 'cancelled'",
(specialist_key,),
).fetchall()
return {row[0] for row in rows}
Если Игорь занят в пятницу в 14:00 — этот слот просто не появится. Двойные записи физически невозможны.
Панель администратора
После подтверждения или отклонения у администратора остаётся постоянная панель с двумя кнопками. Обычные пользователи её никогда не видят — все admin-команды защищены проверкой peer_id == MANAGER_PEER_ID:
admin_menu = (
Keyboard()
.add(Text("📋 Расписание", payload={"cmd": "schedule"}),
color=KeyboardButtonColor.PRIMARY)
.add(Text("📊 Все записи", payload={"cmd": "all_bookings"}),
color=KeyboardButtonColor.PRIMARY)
.get_json()
)
Чтобы сразу получить панель без ожидания первой записи, администратор пишет боту панель.
Защита от зависших сессий
Если пользователь нажимает кнопку со старой клавиатуры после перезапуска бота — контекст не сохранился. Бот перехватывает это:
if cmd == "date":
buf = booking_buffer.get(peer_id, {})
if "service" not in buf or "specialist" not in buf:
await delete_state_safe(peer_id)
await message.answer("Сессия устарела. Начните заново 👇",
keyboard=main_menu)
return
Деплой на сервер
Весь процесс — создание пользователя, виртуальное окружение, .env, systemd — подробно разобран в предыдущей статье. Для этого бота в .service файле меняются только пути:
WorkingDirectory=/home/user/vk-booking-bot
ExecStart=/home/user/vk-booking-bot/.venv/bin/python bot.py
Остальное — один в один. После первого запуска:
sudo systemctl status vk-booking-bot
sudo journalctl -u vk-booking-bot -n 50
Частые ошибки
buttons contain too much rows
VK разрешает максимум 10 строк. Уменьшите slot_days_ahead или сократите slot_times в data.json.
KeyError при завершении записи
Пользователь нажал кнопку из старой сессии. Добавьте проверку обязательных ключей в буфере перед сохранением — пример в разделе выше.
FSM не реагирует на ввод
Catch-all хендлер стоит раньше FSM-состояний. Смотрите раздел про порядок регистрации.
RuntimeError: There is no current event loop
Python 3.12+ несовместим с vkbottle 4.6.x. Используйте Python 3.11.
Что улучшить в продакшн-версии
- Напоминания клиенту за 24 часа и за 2 часа до записи
- Возможность переноса или отмены уже подтверждённой записи
- Веб-панель администратора с полной историей и фильтрами
- Redis для хранения FSM-состояний — тогда бот будет помнить сессии после перезапуска
Итог цепочки
Если вы прошли все три статьи:
- У вас есть VPS на Ubuntu с SSH-доступом
- Бот запущен через systemd и поднимается при перезагрузке
- На нём работает реальный бот записи с FSM, базой данных и панелью администратора
Это уже полноценная инфраструктура, с которой можно идти к клиенту.
Скачать исходный код
Архив содержит bot.py, data.json, requirements.txt и .env.example. База данных создаётся автоматически при первом запуске бота — файл bookings.db появится сам.
Нужна версия под ваш бизнес?
Если хотите запустить бот записи с реальным расписанием, напоминаниями и поддержкой после запуска — оставьте заявку на разработку бота под ключ.
Что читать дальше
- Long Poll vs Callback API для бота ВК — выбор архитектуры под нагрузку
- vkbottle или vk_api: что выбрать — сравнение библиотек
- Сетевые запросы в VK-боте: timeout, retry, backoff — чтобы бот не падал на сетевых сбоях
Реклама
Комментарии
Загрузка...