Привет, Хабр! С выходом платформы MAX у разработчиков появилось новое игровое поле. Пока комьюнити спорит о шансах на победу в гонке мессенджеров, маркетологи уже начали переливать туда трафик.

Самая типовая задача для бизнеса сейчас — бот обратной связи. В Telegram эту нишу давно занял Olgram, а вот в Max — чистый лист. Давайте вместе напишем свой аналог. Это отличный кейс, чтобы разобраться с новым API, не углубляясь в лишнюю инфраструктуру.

Кнопка MAX на сайте — "точка входа" для клиентов.
Кнопка MAX на сайте — "точка входа" для клиентов.

Стек: Почему все оказалось проще, чем кажется

Для MVP (Minimum Viable Product) мы будем использовать Node.js и официальную библиотеку @maxhub/max-bot-api.

Здесь важно отметить один момент, который сэкономит вам кучу времени. В документации Max упор делается на Webhooks. Это значит, что по классике вам нужен сервер с "белым" IP, HTTPS-сертификат и, скорее всего, танцы с ngrok или Cloudflare Tunnel для локальной разработки.

Но официальная JS-библиотека поддерживает Long Polling "из коробки". Бот сам опрашивает сервера Max. Это позволяет запустить проект на локалке без единого открытого порта. Никаких ngrok, никаких настроек серверов. Просто npm start и работает.

База данных: SQLite

Второй камень преткновения — выбор БД. Разработчики частенько тянут в простые проекты PostgreSQL или MySQL. Для пет-проекта или MVP это overkill. Мы берем SQLite.

  • Это просто файл в папке. Нет отдельного процесса сервера.

  • Нет сетевых настроек и задержек (база лежит рядом с кодом).

  • Идеально подходит для чат-ботов, где запросы идут последовательно.

Если ваш бот вдруг станет популярным и упрется в конкуренцию записи — тогда вы переедете на Postgres. А пока не тратим время на DevOps.

Архитектура: Проблема идентификации

Логика бота кажется тривиальной:

  1. Клиент пишет -> Бот пересылает Админу.

  2. Админ отвечает -> Бот пересылает Клиенту.

Сложность кроется во втором пункте. Когда Админ нажимает кнопку "Ответить" (Reply) в своем клиенте, бот видит входящее сообщение от Админа. Но как узнать, какому именно клиенту адресован ответ? В самом тексте сообщения ID клиента нет.

Нам нужно построить Карту сообщений (Message Map). Мы будем запоминать ID каждого сообщения, которое бот шлет Админу, и связывать его с ID Клиента.

Алгоритм:

  1. Бот получает сообщение от Клиента.

  2. Пересылает его Админу.

  3. Запоминает в БД: ID_этого_сообщения -> ID_клиента.

  4. Админ делает Reply на это сообщение.

  5. Бот видит ID, на который ответили, находит в БД клиента и шлет ответ.

Администратор отвечает через Reply. Бот подтверждает отправку.
Администратор отвечает через Reply. Бот подтверждает отправку.

Пишем код

Подготовка

Создаем проект и устанавливаем зависимости. Нам понадобятся сама библиотека бота, драйвер SQLite и пакет для переменных окружения.

npm init -y
npm install @maxhub/max-bot-api sqlite sqlite3 dotenv

Создаем файл .env. Токен бота берем в интеграциях на business.max.ru, а OWNER_ID — это ваш личный ID в Max (его можно узнать в логах при первом запуске).

BOT_TOKEN=ваш_токен_здесь
OWNER_ID=12345678
Получение токена для ботов в MAX
Получение токена для ботов в MAX

Настройка БД

Подключаем SQLite и создаем таблицу для маппинга. Обратите внимание на использование async/await — библиотека sqlite отлично с ним дружит.

import { open } from 'sqlite';
import sqlite3 from 'sqlite3';

let db;
(async () => {
  db = await open({ filename: './database.sqlite', driver: sqlite3.Database });
  
  // Таблица для связи ID сообщения -> ID клиента
  await db.exec(`
    CREATE TABLE IF NOT EXISTS reply_map (
      owner_msg_mid TEXT PRIMARY KEY,
      client_user_id INTEGER
    )
  `);
  console.log('База данных готова');
})();

Входящий поток (Клиент -> Админ)

Главный нюанс: метод sendMessageToUser возвращает объект отправленного сообщения. Нам нужно достать из него mid (Message ID), чтобы сохранить в базу. Без этого мы не сможем "привязать" ответ админа к конкретному пользователю.

const ownerId = Number(process.env.OWNER_ID); // Ваш ID

bot.on('message_created', async (ctx) => {
  const msg = ctx.message;
  const senderId = msg.sender.user_id;
  const text = msg.body.text;

  // Если пишет КЛИЕНТ (не админ)
  if (senderId !== ownerId) {
    
    const forwardText = `? **Сообщение от ${msg.sender.first_name}** (ID: ${senderId}):\n\n${text}`;
    
    // Отправляем админу с Markdown-разметкой
    const sentMsg = await bot.api.sendMessageToUser(ownerId, forwardText, { format: 'markdown' });

    // ГЛАВНЫЙ МОМЕНТ: Сохраняем связь
    if (sentMsg && sentMsg.body && sentMsg.body.mid) {
        await db.run('INSERT OR REPLACE INTO reply_map (owner_msg_mid, client_user_id) VALUES (?, ?)', 
          [sentMsg.body.mid, senderId]);
    }
    return;
  }

  // ... здесь будет логика ответов
});

Исходящий поток (Админ -> Клиент)

Теперь самое интересное. Как понять, что Админ нажал кнопку "Ответить"? Изучая объект сообщения (msg), который присылает API, можно заметить поле link. Оно находится в корне объекта, на уровне с body и sender.

Структура JSON при Reply выглядит так:

{
  "body": { "text": "ответ", ... },
  "sender": { ... },
  "link": {              // <-- Идентификатор ответа
    "type": "reply",
    "message": {
      "mid": "mid.исходного_сообщения..." // ID сообщения, на которое ответили
    }
  }
}

Реализуем поиск получателя. Если админ просто пишет текст (не Reply), отправляем последнему активному клиенту (fallback).

// Продолжение внутри bot.on...

  // Если пишет ВЛАДЕЛЕЦ
  if (senderId === ownerId) {
    
    // Проверяем, что это именно Reply
    if (msg.link && msg.link.type === 'reply') {
      
      // Достаем ID сообщения, на которое ответили
      const repliedMsgMid = msg.link.message.mid;
      
      // Ищем в нашей базе клиента
      const target = await db.get('SELECT client_user_id FROM reply_map WHERE owner_msg_mid = ?', repliedMsgMid);

      if (target) {
        await bot.api.sendMessageToUser(target.client_user_id, text);
        await ctx.reply(`✅ Ответ отправлен пользователю ID: ${target.client_user_id}`);
      } else {
        await ctx.reply('⚠️ Пользователь не найден в базе (возможно, старое сообщение).');
      }
      return;
    }
    
    // Fallback: Если Админ просто пишет текст, отправляем последнему активному
    // (Для простоты MVP сохраняем lastClient в глобальной переменной)
  }

Итог

У нас на руках рабочий MVP бота обратной связи.

Пример блока для связи на сайте. Кнопка MAX позволяет клиентам писать напрямую, минуя лишние формы.
Пример блока для связи на сайте. Кнопка MAX позволяет клиентам писать напрямую, минуя лишние формы.
  • Запуск за 5 минут. Не нужны серверы, настройки портов и SSL-сертификаты. Скачал, вставил токен, работает.

  • Удобство для админа. Вы общаетесь с клиентами привычным способом — через кнопку "Ответить" (Reply), как в обычном чате. Бот сам разберется, кому отправить текст.

  • Надежность. Вся база контактов хранится в одном файле. Проще всего в мире бэкапить и переносить.

Код проекта доступен на GitHub: mikhail-klimenko/max-feedback-bot

Это только начало. Я буду рад, если вы присоединитесь к разработке — предлагайте пул-реквесты, заводите Issues с идеями или багами. Давайте сделаем инструмент для обратной связи в Max вместе!

В следующих частях добавим поддержку медиа и админку.

Комментарии (15)


  1. ICS_2017
    28.03.2026 05:38


  1. bighorik
    28.03.2026 05:38

    Извините, коллега, но вы упомянули макса не в строго негативном ключе, а значит вы будете отменены


  1. janus4x
    28.03.2026 05:38

    А в Максе стало можно получить api для бота простым людям ?


    1. K0Jlya9
      28.03.2026 05:38

      Правильный вопрос - а разрешено ли в скаме продавать свои токены от ботов другим людям или за это предусмотрен расстрел.


  1. mazahaka1488
    28.03.2026 05:38

    И опять никто не сказал про то, что для создания бота в MAX нужно ИП


    1. K0Jlya9
      28.03.2026 05:38

      Не только юрлицо но еще и высочайшее дозволение выпросить надо. Если скажите что ваш бот будет продавать резиновые изделия с радужной раскраской - гарантировано получите отказ.


  1. NanoVHF
    28.03.2026 05:38

    Нафиг вы пиарите эту срань? Денег что-ли занесли вам за это? Похоже...


    1. makssof
      28.03.2026 05:38

      У человека первая статья, ни комментариев, ни закладок, зареган акк пару месяцев назад Абсолютно чистый автор


  1. Yaroz
    28.03.2026 05:38

    Больше спасибо за статью, надеюсь мне не понадобится эта информация


  1. exalon
    28.03.2026 05:38

    Какие все душные.


  1. SkyCat
    28.03.2026 05:38

    Рабочий способ выпила с ресурса - написать про ботов в Максе. )) Прямо стопроцентный.


  1. ignat_mineralkin
    28.03.2026 05:38

    Почему код не кириллицей? Где же наши духовные скрепы, суверенитет в конце концов?


  1. rabotash
    28.03.2026 05:38

    Господа, как поднять карму чтобы ставить минусы? Исключительно для борьбы с максобесами


    1. SkyCat
      28.03.2026 05:38

      Для этого надо статьи писать. Например, про ботов в Максе...


  1. Prox88
    28.03.2026 05:38

    Надо же, человек терпел с 2016 года и спустя 10 лет написал свою первую статью и она про Макс