Бот в MAX за день: пошаговый гайд с кодом
В прошлой статье про 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 за пару дней, запустите на реальных пользователях, посмотрите, как они на самом деле с ним общаются. И уже по данным решайте, что надстраивать дальше. Это экономит и нервы, и деньги.