В прошлой статье про MAX мы разбирали, можно ли перенести Telegram-бота в российский мессенджер и кому это реально надо в 2026. Теперь спустимся на один этаж ниже и покажем, как руками собрать первого бота в MAX. Без магии, без маркетингового буллшита, с рабочим кодом.

Если вы никогда не писали ботов - сможете повторить за выходные. Если у вас есть опыт с Telegram-ботами - пройдёте за полдня. В конце будет работающий бот, который принимает сообщения, отвечает меню, складывает заявки в базу и дёргает вашу CRM.

Что соберём и что понадобится

Минимальный бот для бизнеса: пользователь пишет команду /start, видит меню из кнопок ("Оставить заявку", "Каталог", "Связаться с менеджером"), выбирает пункт, вводит данные, получает подтверждение. Заявка летит в базу данных и в CRM. Для менеджера есть команда /admin с выгрузкой статистики за день.

Этого достаточно, чтобы закрывать 60-70% сценариев лидогена в среднем b2c-бизнесе. Дальше сверху уже навешивается RAG, оплаты, уведомления и всё остальное - но это надстройки. Без скелета - нечего нарастить.

Из инструментов понадобится:

  • Аккаунт в MAX (обычный, тот же, которым вы пользуетесь).
  • Python 3.10+ или Node.js 18+ на выбор. В статье будем на Python - он короче для примеров.
  • VPS с доступом по SSH и публичным IP. Подойдёт любой за 300-500 ₽ в месяц (Selectel, Timeweb, Beget, RuVDS).
  • Доменное имя с HTTPS-сертификатом для webhook. Free-вариант через Let's Encrypt.
  • SQLite или PostgreSQL для хранения заявок. Для старта хватит SQLite.

Если VPS и домена нет - на этапе разработки сгодится туннель через Cloudflare Tunnel или ngrok, они выдадут HTTPS-URL для вашего локального сервера.

Регистрация бота и получение токена

В MAX есть специальный служебный бот, который создаёт других ботов - аналог BotFather в Telegram. Открываете поиск, находите его (название менялось пару раз, в 2026 это @MasterBot), пишете /newbot, проходите мастер: имя, аватарка, описание.

На выходе получаете токен вида eyJhbG.... Это ваш ключ доступа к API. Храните его в .env, не в коде, не в гите, никогда не в скриншотах.

Сразу же настройте команды меню бота через /setcommands. Это то, что пользователь увидит в выпадающем списке при нажатии на "/":

start - начать работу
menu - главное меню
help - как пользоваться
contact - связаться с менеджером

Плюс добавьте описание и аватарку - без них бот выглядит как недоделка, и конверсия в первый диалог падает процентов на 30.

Скелет приложения

Ставим зависимости:

pip install fastapi uvicorn httpx python-dotenv sqlalchemy

FastAPI - для приёма webhook, httpx - чтобы слать запросы в API MAX, sqlalchemy - для базы. Минимальный файл bot.py:

import os, httpx
from fastapi import FastAPI, Request
from dotenv import load_dotenv

load_dotenv()
TOKEN = os.getenv("MAX_BOT_TOKEN")
API = f"https://botapi.max.ru/bot{TOKEN}"

app = FastAPI()

async def send(chat_id: int, text: str, keyboard=None):
    payload = {"chat_id": chat_id, "text": text}
    if keyboard:
        payload["reply_markup"] = {"inline_keyboard": keyboard}
    async with httpx.AsyncClient() as c:
        await c.post(f"{API}/sendMessage", json=payload)

@app.post("/webhook")
async def webhook(req: Request):
    update = await req.json()
    msg = update.get("message", {})
    chat_id = msg.get("chat", {}).get("id")
    text = msg.get("text", "")

    if text == "/start":
        await send(chat_id, "Привет. Чем помочь?", [
            [{"text": "Оставить заявку", "callback_data": "lead"}],
            [{"text": "Каталог", "callback_data": "catalog"}],
            [{"text": "Связаться", "callback_data": "contact"}]
        ])
    return {"ok": True}

Запустить: uvicorn bot:app --host 0.0.0.0 --port 8000. Проверить: открыть в браузере http://ваш_ip:8000/docs.

Дальше привязываем webhook. Один раз вызываем:

curl -X POST "https://botapi.max.ru/bot${TOKEN}/setWebhook" \
  -d '{"url": "https://ваш_домен.ru/webhook"}'

С этого момента MAX будет слать вам POST-запросы на каждое сообщение. Если юзер напишет /start, бот ответит меню с тремя кнопками.

Обработка нажатий на кнопки

Когда пользователь жмёт inline-кнопку, в webhook прилетает не message, а callback_query. Обрабатываем:

if "callback_query" in update:
    cq = update["callback_query"]
    data = cq["data"]
    chat_id = cq["message"]["chat"]["id"]

    if data == "lead":
        await send(chat_id, "Отправьте ваше имя и номер телефона одним сообщением")
        set_state(chat_id, "awaiting_lead")
    elif data == "catalog":
        await send(chat_id, "Каталог: https://ваш_сайт.ru/catalog")
    elif data == "contact":
        await send(chat_id, "Напишите свой вопрос - ответим в течение часа")

set_state - это простейшая FSM: храните в базе или в памяти, в каком состоянии сейчас пользователь, чтобы понимать, что делать с его следующим сообщением. Для простых ботов хватает словаря в памяти, для серьёзных - Redis или отдельная таблица в БД.

Когда пользователь ответил текстом "Иван Петров, +7 999 123 45 67", мы смотрим состояние, парсим, сохраняем в базу и сбрасываем состояние.

Сохранение заявок в базу

Модель на SQLAlchemy в 15 строк:

from sqlalchemy import create_engine, Column, Integer, String, DateTime
from sqlalchemy.orm import declarative_base, Session
from datetime import datetime

engine = create_engine("sqlite:///bot.db")
Base = declarative_base()

class Lead(Base):
    __tablename__ = "leads"
    id = Column(Integer, primary_key=True)
    chat_id = Column(Integer)
    name = Column(String)
    phone = Column(String)
    source = Column(String, default="max")
    created = Column(DateTime, default=datetime.utcnow)

Base.metadata.create_all(engine)

При получении заявки:

with Session(engine) as s:
    s.add(Lead(chat_id=chat_id, name=name, phone=phone))
    s.commit()

Всё. Дальше - прокидываете эту же заявку в CRM.

Интеграция с CRM

Допустим, у вас Битрикс24. У него есть webhook-интеграция, которая позволяет одним POST-запросом создать лид:

async def push_to_bitrix(name: str, phone: str):
    url = os.getenv("BITRIX_WEBHOOK")
    payload = {
        "fields": {
            "TITLE": f"Заявка из MAX: {name}",
            "NAME": name,
            "PHONE": [{"VALUE": phone, "VALUE_TYPE": "WORK"}],
            "SOURCE_ID": "SELF",
            "COMMENTS": "Источник: MAX-бот"
        }
    }
    async with httpx.AsyncClient() as c:
        await c.post(f"{url}/crm.lead.add", json=payload)

Для AmoCRM логика та же, меняется только endpoint и структура полей. Подробно про архитектуру таких интеграций мы разбирали в разборе API-интеграций - там же есть про типичные косяки: гонки, дубли, ретраи.

Если вы НЕ хотите писать код для каждого сервиса, поверх бота ставится n8n или Make, и боту отправляется только внутренний POST, а дальше уже n8n делает маршрутизацию. Про связку n8n + Make + мессенджер мы писали отдельно, паттерн идентичный для MAX.

Админка и уведомления

Менеджеру надо видеть, сколько заявок и кто их оставил. Самое простое - команда /admin для конкретных chat_id, которые вы занесли в белый список:

ADMINS = [123456, 789012]  # ваши chat_id

if text == "/admin" and chat_id in ADMINS:
    with Session(engine) as s:
        today_leads = s.query(Lead).filter(
            Lead.created >= datetime.utcnow().date()
        ).all()
    report = f"Заявок за сегодня: {len(today_leads)}\n\n"
    for l in today_leads:
        report += f"• {l.name} - {l.phone}\n"
    await send(chat_id, report)

Плюс можно слать уведомление в отдельный чат руководителя при каждой новой заявке. Это 5 строк: после session.commit() вызываете send(MANAGER_CHAT_ID, f"Новая заявка: ...").

Деплой и продакшн-готовность

Для реального боевого использования нужно ещё несколько вещей, которые в гайдах любят пропускать.

HTTPS с валидным сертификатом. MAX не шлёт webhook на HTTP и не дружит с самоподписанными сертификатами. Ставите Caddy или nginx + certbot, настраиваете Let's Encrypt, проверяете в ssllabs.com, что рейтинг А или выше.

Systemd-сервис, а не python bot.py в tmux. Нормальный юнит с автоперезапуском, логами в journalctl и ограничениями по памяти. Пара строк в /etc/systemd/system/maxbot.service:

[Unit]
After=network.target

[Service]
User=botuser
WorkingDirectory=/opt/maxbot
ExecStart=/opt/maxbot/venv/bin/uvicorn bot:app --host 127.0.0.1 --port 8000
Restart=always

[Install]
WantedBy=multi-user.target

Мониторинг. Элементарный - хелсчек через Uptime Kuma или просто curl по cron раз в 5 минут. Упал бот - вам телега (или MAX, лол) пишет.

Ретраи и очереди. Если CRM лежит и ваш push_to_bitrix упал, заявка в базе есть, но в CRM не попала. Для нормального прода нужна очередь: сохраняем заявку, ставим задачу в очередь, обработчик пытается запушить, при ошибке - ретрай с бэкоффом. Самое простое - Redis Queue или Celery, средний уровень - отдельный воркер на том же сервере.

Бэкапы базы. Ежедневный дамп SQLite/Postgres в S3-совместимое хранилище (Selectel, Timeweb, VK Cloud). Без этого в один прекрасный день вы узнаете, что все заявки за полгода потеряли, потому что диск на VPS заглючил.

Сколько это заняло по времени

Реальные цифры на типичный кейс:

  • Регистрация бота и настройка: 20 минут.
  • Скелет FastAPI + вебхук + ответ на /start: 1 час.
  • Меню с кнопками + FSM + сохранение в базу: 2-3 часа.
  • Интеграция с CRM: 1-2 часа (если есть документация) или день (если документации нет и приходится реверсить).
  • Админка, уведомления, мелочи UX: 2 часа.
  • Деплой на VPS с HTTPS и systemd: 1-2 часа.

Итого: от одного рабочего дня до двух - если вы работаете один, без отвлечений, и ваша CRM стандартная. Если нужна нестандартная логика, RAG по базе знаний, голос или мини-приложение - смело умножайте на три.

Типичные косяки на первом боте

По опыту разборов чужих ботов - вот что чаще всего ломается.

FSM в памяти при нескольких инстансах. Запустили два процесса uvicorn за балансером, у пользователя состояние хранится в словаре процесса А, а его следующее сообщение прилетело в процесс Б. Пользователь видит "я не понял вас" и отваливается. Лечение - Redis или БД для состояния с первого дня.

Синхронные запросы внутри webhook. MAX ждёт ответа 200 OK от вашего webhook за несколько секунд. Если вы внутри делаете long-running интеграцию (послали в CRM, ждёте ответа 10 секунд), MAX начинает повторять запросы, и пользователь получает дублированные сообщения. Правильно - сразу вернуть 200, а тяжёлую работу в фоне.

Хардкод токена в коде. Классика. Закоммитили, забыли удалить, через месяц кто-то нашёл на GitHub. Правильно - .env в .gitignore сразу, ротация токенов раз в квартал, секреты в Vault или хотя бы в переменных окружения на VPS.

Игнор rate limit. API MAX, как и Telegram, лимитит запросы. Если бот популярный и шлёт 100+ сообщений в секунду, часть вылетит с ошибкой 429. Правильно - очередь исходящих и respect Retry-After.

Отсутствие логирования. Бот работает, но непонятно, почему у одного клиента пропала заявка. Без нормальных логов вы не найдёте проблему. Правильно - структурированные логи (JSON), уровни, сохранение на 30+ дней.

Что нарастить сверху, когда базовый бот работает

Дальше идёт уровень, где бот превращается из формы в ассистента. Коротко, направления:

  • RAG по базе знаний - бот отвечает на вопросы клиентов, используя ваши документы, статьи, инструкции. Отдельная статья про то, как это реально работает и где разочаровывает.
  • Распознавание намерений - бот понимает не только кнопки, но и свободный текст ("хочу вернуть заказ", "когда привезёте").
  • Платежи - MAX Pay, эквайринг, выставление счетов прямо из диалога.
  • Голос - пользователь надиктовал, бот распознал, ответил текстом или голосом. Про это был отдельный разбор по голосовым ИИ-агентам.
  • Мульти-канальность - тот же серверный код, адаптеры под Telegram, MAX, ВКонтакте, WhatsApp Business.

Каждый из этих уровней добавляет работы не на пару часов, а на пару недель минимум, иногда на месяц. Заранее это закладывайте в план, если собираетесь идти дальше MVP.

Итого

Бот в MAX - не rocket science. Токен, webhook, пара сотен строк Python, база, интеграция с CRM, деплой. За день-два поднимается человеком средней технической квалификации, который раньше писал веб-сервисы.

Сложности начинаются не с самого бота, а с того, что вы хотите на нём построить. Если задача "принимать заявки" - оно реально за день. Если задача "заменить первую линию поддержки 24/7 с RAG по базе знаний и интеграцией в 1С" - это уже проект на пару месяцев, команда, отдельный бюджет и инженер, который знает, куда копать.

Главное - не начинайте сразу со сложного. Соберите MVP за пару дней, запустите на реальных пользователях, посмотрите, как они на самом деле с ним общаются. И уже по данным решайте, что надстраивать дальше. Это экономит и нервы, и деньги.