Собрать бота для MAX в конструкторе несложно, сложнее пережить первый живой тест. Мой вариант на 9 блоков сломался на шаге с телефоном: клиент нажал «Отправить мой номер», MAX прислал карточку контакта, и бот ответил приветствием из старой схемы, которой в проекте уже не должно было быть. За этот заказ на два бота, для MAX и Telegram, я нашёл в Salebot несколько вещей, которых нет в документации или которые описаны там неочевидно.
Кто может создать бота в MAX
Организация, ИП или самозанятый, физлицу нельзя. Бизнес сначала проходит верификацию на платформе для партнёров business.max.ru: самозанятые через Госуслуги, ИП и организации ещё и через СберБизнес или Т-Банк. По данным ppc.world, бот после этого проходит модерацию до 48 часов по рабочим дням.
Условия уже менялись. Раньше в документации Salebot речь шла об ограниченном круге юрлиц, а самозанятых на платформу пустили только в июне 2026 года. Перед стартом правила стоит перепроверить. Собирать бота можно в любом конструкторе с поддержкой MAX, я работал в Salebot.
Что нужно было собрать
Заказчик, сеть офлайн-школ английского для детей, запускал рекламу в MAX и Telegram. Бот должен был собирать заявку прямо в переписке. Первое ТЗ описывало оффер со скидкой на первый месяц: четыре экрана (возраст ребёнка, телефон с согласием на обработку персональных данных, email, финал), догоняющее сообщение через 24 часа для тех, кто бросил на телефоне, и хранение заявок внутри бота без внешней CRM.
Посреди работы оффер сменился на трёхмесячный курс, и сценарий пришёл уже в Google-таблице. Воронка выросла: возраст, цель на три месяца из пяти вариантов, имя, телефон, согласие, предложение подписаться на канал, приглашение на день открытых дверей и напоминание о подписке через час. Потом выяснилось, что ботов нужно два.
Почему схему от нейросети пришлось выбросить
До меня клиент сгенерировал схему в DeepSeek: 8 блоков и 6 стрелок. Файл был технически валидным, импорт в Salebot проходил, но по смыслу собирал другую воронку:
- в текстах пробный урок вместо скидки на первый месяц;
- бот спрашивал «Сколько вам лет?» вместо возраста ребёнка и собирал имя там, где нужен возраст;
- не было согласия на обработку персональных данных, проверки телефона и догоняющего сообщения;
- кнопки «Как добраться» и «Отзывы» стояли отдельными блоками на стрелках без условий и сработали бы сами, без нажатия;
- стартовый блок был с пустым условием.
На вид схема правдоподобная. Без построчной сверки с ТЗ эти расхождения всплыли бы только на живом тесте. Последний пункт списка в итоге и всплыл, хотя схему к тому моменту уже выбросили.
Как загрузить схему в Salebot из файла
В Salebot есть импорт схемы из JSON: в разделе резервного копирования проекта вкладка «Восстановление из файла». Файл состоит из трёх массивов: messages (блоки), connections (стрелки) и sheets (листы). Основной сайт документации docs.salebot.pro в момент работы систематически отдавал ошибку 502, читать приходилось зеркало на salebot.pro/docs.
Импорт позволяет собирать бота как код. Схему генерирует скрипт на Python. Тексты лежат в одном месте, версии хранятся, и при смене оффера схема пересобирается целиком, без перекликивания блоков руками. Когда ботов стало два, один генератор выдавал схемы для обоих мессенджеров, и тексты не разъезжались.
| Версия схемы | Блоков | Стрелок |
|---|---|---|
| Схема от DeepSeek | 8 | 6 |
| Оффер со скидкой, после исправлений | 9 | 15 |
| Трёхмесячный курс, первая сборка | 14 | 23 |
| Курс с проверкой подписки, одна площадка | 17 | 28 |
| Курс, один проект на оба мессенджера | 34 | 56 |
Расшифровки числовых полей файла в документации нет. По экспорту из живого проекта я выяснил четыре значения:
| Поле | Значение | Что значит |
|---|---|---|
message_type |
0 | блок «Состояние», обычный шаг воронки |
message_type |
4 | стартовый блок |
comparision_method |
0 | «Игнорируя ошибки и неточности», по умолчанию |
comparision_method |
4 | «Регулярное выражение» |
Остальные значения я не проверял. Слово comparision с опечаткой так и записано в самой платформе.
Почему первый тест сломался
Клиент загрузил схему и прошёл бота в MAX сам. Первые три экрана отработали чисто: приветствие с именем из профиля, четыре кнопки возраста, согласие, экран телефона с маской. На телефоне он нажал «📱 Отправить мой номер». MAX отправил карточку контакта, и бот ответил приветствием про пробный урок с кнопками «Как добраться» и «Отзывы». Текст и кнопки дословно совпали с блоком из файла старой схемы.
Сработали две причины разом.
Первая: импорт из файла добавляет блоки к тем, что уже лежат в проекте. Новая схема легла рядом со старой, и работали обе. Про восстановление из другого проекта документация Salebot прямо предупреждает, что вся схема будет удалена. Про файл написано «добавить в проект», и понимать это надо буквально.
Вторая: стартовый блок с пустым полем «Условие» ловит любое сообщение, которое не подошло ни под одну стрелку текущего блока, и выбрасывает человека в начало воронки. Старый стартовый блок от DeepSeek был именно таким. Карточку контакта не поймала ни одна стрелка новой схемы, и её подхватил он.
После теста я поменял три вещи:
- перед импортом конструктор очищается вручную;
- стартовому блоку всегда задаются явные условия:
/start, «привет», «меню»; - на шаге телефона теперь две стрелки вместо одной.
Что присылает кнопка «Отправить мой номер» в MAX
Карточку контакта с именем. В документации Salebot написано, что в чат придёт сообщение с номером телефона и появится переменная phone. На живом тесте пришла карточка, и регулярное выражение, которое ждало строку вида +79991234567, её не увидело. А переменную phone MAX заполнил.
Поэтому стрелок на шаге телефона две. Первая проверяет текст сообщения регуляркой на 11 цифр, если человек ввёл номер руками. Вторая проверяет переменную phone через поле «Переменная для сравнения», если он нажал кнопку. В обоих случаях номер приводится к единому виду функцией normalizePhone().
Из той же серии: имя из профиля MAX лежит в системной переменной name. Если бот спрашивает имя сам и пишет ответ туда же, имя из профиля затрётся. Для ответа я завёл отдельную переменную client_name.
Можно ли в MAX отправлять сообщения по таймеру
Однозначного ответа у меня нет. Раньше в документации Salebot по MAX было прямо написано, что рассылки и любые сообщения по таймеру в мессенджере запрещены. На актуальных страницах этой фразы уже нет. По обзору ppc.world, MAX запрещает рассылки с рекламой и маркетинговыми материалами, а правил для отложенных сообщений внутри одного диалога я не нашёл. Проверить это можно только боевым тестом.
Вырезать таймеры я не стал. Догоняющее сообщение через 24 часа и напоминание о подписке через час стоят на выключателях, константах followup_enabled и sub_reminder_enabled. Если MAX их не пропустит, в настройках меняется одна цифра, а схема остаётся как есть. В Telegram таймеры работают штатно.
Как проверить подписку на канал в MAX и Telegram
Через функции Salebot: max_get_chat_member(ID канала, ID пользователя) для MAX и tg_get_chat_member с теми же аргументами для Telegram. Ведут они себя по-разному. Функция MAX возвращает данные участника или None. Telegram для отписавшегося возвращает статус left, для заблокированного kicked, и таких надо отсеивать отдельно. В документации Salebot возвращаемое значение tg_get_chat_member не описано, это поведение самого Telegram.
Бот должен быть администратором канала: Telegram гарантирует ответ о других пользователях только администратору. ID канала отрицательный и передаётся вместе с минусом.
В схеме проверку делают два «молчаливых» блока с ответом #{none}, клиент их не видит. Первый спрашивает у мессенджера состав канала, второй разбирает ответ и разводит стрелками на «подписан» и «не найден». Ненайденному человеку бот показывает экран «Пока не вижу вас в канале» с кнопками «Подписаться», «Проверить ещё раз» и «Пока не буду». Тупика нет: при отказе приглашение на день открытых дверей всё равно уходит.
Проверку я собрал по документации. До боевого теста с настоящими ID каналов и правами администратора у бота её работа не подтверждена.
Почему нельзя проверить подписку в соседнем мессенджере
Сценарий клиента в финале звал подписаться и на Telegram, и на MAX. Бот в MAX подписку на Telegram-канал проверить не может: это другой мессенджер, у бота там нет ни доступа, ни ID пользователя. Обратное тоже не работает.
Промежуточный вариант был с кнопкой «Я подписался в Telegram», которая засчитывала подписку на доверии. Клиент от неё отказался: каждый бот зовёт только в свой канал и только его проверяет. Упоминание соседнего мессенджера из текстов ушло.
Один проект Salebot на два мессенджера
Можно, но через листы не получится. Клиент спросил, нельзя ли привязать каждый лист проекта к своему боту. По документации листы только визуально делят поле: «При этом сам бот работает в едином поле».
Работает системная переменная client_type, в которой лежит мессенджер, откуда пришёл человек. У стартового блока есть поле «Переменная для сравнения», и оно работает вместе с текстовым условием: выполниться должны оба. Поэтому два стартовых блока с одинаковым условием не конфликтуют:
Стартовый блок MAX: условие /start, client_type == 20
Стартовый блок Telegram: условие /start, client_type == 1
Коды взяты из таблицы в документации Salebot. Значение 20 для MAX я запуском пока не подтверждал.
Объединённая схема вышла на 34 блока и 56 стрелок. У одного проекта одна база клиентов, одни метки, один набор констант и одна выгрузка. Цена за это: схема вдвое больше, и каждую правку текста приходится вносить в две ветки. Константы клиент завёл сам, по шесть на площадку: ссылки на канал, на запись на день открытых дверей и на политику обработки персональных данных, ID канала и два выключателя таймеров. При сверке их имена совпали со схемами без расхождений.
Как перевести ТЗ лендинга на язык мессенджера
Оба ТЗ были написаны как для веб-формы: чекбоксы, маски, неактивные кнопки. В мессенджере половины этого нет, и каждый элемент пришлось переводить отдельным решением. Все решения я записал в инструкцию для клиента.
| В ТЗ | В боте |
|---|---|
| Чекбокс согласия, без него кнопка «Отправить» неактивна | отдельный шаг с кнопкой согласия; без нажатия бот возвращает на тот же экран |
Маска ввода +7 (___) ___-__-__ |
маска показана текстом, ввод проверяется регулярным выражением на 11 цифр |
| Обязательное поле | при ошибке подсказка и возврат на тот же шаг |
| Необязательный email | кнопка «Пропустить» и пометка лида «полный» или «неполный» |
| Админ-панель с адресом, дедлайном и ссылками | константы проекта, меняются в настройках без правки схемы |
| Список заявок с фильтром и выгрузкой в CSV | метки и переменные в карточке клиента, штатный раздел «Клиенты» |
| Склеивать дубли по телефону | в MAX одна карточка на аккаунт, повторный заход её обновляет |
Лимиты кнопок у мессенджеров тоже разные. По документации Salebot текст кнопки в Telegram обрезается после 32 символов, в ВКонтакте предел 40, в Одноклассниках 42, а inline-клавиатура MAX вмещает до 210 кнопок в 30 рядах. Кнопка «🎓 Записаться на День открытых дверей» на 36 символов в Telegram обрезалась бы, поэтому я переименовал её в «🎓 Хочу на День открытых дверей», 30 символов.
Что пока не проверено
Цифр по заявкам пока нет, поэтому про результат я ничего не пишу. Под вопросом три вещи: пропустит ли MAX сообщения по таймеру, сработает ли проверка подписки с настоящими ID каналов и подтвердится ли код 20 для MAX на запуске. Ответ на все три даст боевой тест.
Смену оффера и второй мессенджер схема из скрипта пережила без ручной перекладки блоков: менялись тексты, генератор пересобирал всё целиком. Сломал её один забытый стартовый блок с пустым условием, поэтому импорт у меня теперь начинается с чистого конструктора.