Заказать бота

Бот записи на услуги ВКонтакте: FSM, слоты и панель администратора

Рабочий бот записи для ВК: выбор специалиста, свободные слоты кнопками, SQLite, подтверждение от админа. Код, скриншоты, деплой на сервер.

Содержание статьи

Клиент выбирает мастера, видит только свободное время, вводит телефон — и через секунду администратору уходит уведомление с кнопками «Подтвердить» и «Отклонить». Клиент получает ответ автоматически. Без звонков, без «напишем позже», без потерянных заявок.

Это не концепт. Это рабочий бот, который мы собрали и запустили — со скриншотами реальной переписки.

Это третья статья в цепочке:

  1. Где живут боты: пошаговый гайд по запуску VPS для новичков — выбираем и настраиваем сервер
  2. Деплой VK-бота на сервер: systemd, nginx и работа 24/7 — запускаем бота в продакшн
  3. Бот онлайн-записи: 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-состояний — тогда бот будет помнить сессии после перезапуска

Итог цепочки

Если вы прошли все три статьи:

  1. У вас есть VPS на Ubuntu с SSH-доступом
  2. Бот запущен через systemd и поднимается при перезагрузке
  3. На нём работает реальный бот записи с FSM, базой данных и панелью администратора

Это уже полноценная инфраструктура, с которой можно идти к клиенту.

Скачать исходный код

Архив содержит bot.py, data.json, requirements.txt и .env.example. База данных создаётся автоматически при первом запуске бота — файл bookings.db появится сам.

⬇ Скачать vk-booking-bot.zip

Нужна версия под ваш бизнес?

Если хотите запустить бот записи с реальным расписанием, напоминаниями и поддержкой после запуска — оставьте заявку на разработку бота под ключ.

Что читать дальше

Реклама

Комментарии

Загрузка...