Skip to content

Latest commit

History

34 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

MaxBot

DonateApps ScriptjavascriptGitHub code size in bytesGitHub last commit

Google Apps Script библиотека для MAX Bot API: сообщения, команды, webhook-обработчики, inline-клавиатуры и загрузка медиа — на обычном JavaScript.

📦 ID библиотеки

1dUzlK1qVuXOB8OOKgWazEBtCSbFRqIUv9F0-yQXM4K9xk6UlH-Ks2aOU

📋 Содержание

🚀 Подключение

1. Добавьте библиотеку

  1. Откройте Google Apps Script и создайте проект.
  2. Нажмите + рядом с пунктом Библиотеки.
  3. Вставьте ID MaxBot из раздела выше.
  4. Выберите опубликованную версию 4 и идентификатор MaxBot.
  5. Нажмите Добавить.

Все примеры ниже используют именно этот идентификатор:

vartoken=PropertiesService.getScriptProperties().getProperty("MAX_BOT_TOKEN");varbot=MaxBot.create(token);

2. Сохраните секреты в Script Properties

Откройте Настройки проекта → Свойства скрипта и добавьте:

СвойствоЧто записать
MAX_BOT_TOKENтокен бота из кабинета MAX
MAX_WEBHOOK_SECRETдлинную случайную строку для защиты Web App URL

В коде значения читаются так:

varproperties=PropertiesService.getScriptProperties();vartoken=properties.getProperty("MAX_BOT_TOKEN");varwebhookSecret=properties.getProperty("MAX_WEBHOOK_SECRET");

Токен и секрет не нужно вставлять прямо в исходный код.

⚡ Готовый webhook-бот

Ниже — цельный пример: создание клиента, команды, обработчики сообщений и кнопок, doPost(e), регистрация команд и подписка на webhook.

varproperties=PropertiesService.getScriptProperties();varbot=MaxBot.create(properties.getProperty("MAX_BOT_TOKEN"),{apiVersion: "v1",webhookSecret: properties.getProperty("MAX_WEBHOOK_SECRET"),parseMode: "markdown",});functionwelcome(ctx){returnctx.reply("**Привет!** Я уже работаю 👋",{keyboard: bot.keyboard.callback("Помощь","help").link("Открыть MAX","https://max.ru").build(),});}bot.on("bot_started",welcome).command("start",welcome);bot.command("echo",function(ctx){vartext=ctx.match&&ctx.match[1];ctx.reply(text||"Напишите текст после команды: /echo привет");});bot.hears(/привет/i,function(ctx){ctx.reply("Привет! Нажмите /start, чтобы открыть меню.");});bot.action("help",function(ctx){ctx.answerCallback({notification: "Команды: /start и /echo"});});bot.on("message_edited",function(ctx){console.log("Изменено сообщение: "+ctx.messageId);});functiondoPost(e){returnbot.handleWebhook(e);}functiongetWebhookUrl_(){vardeploymentId="<DEPLOYMENT_ID>";varsecret=properties.getProperty("MAX_WEBHOOK_SECRET");return"https://script.google.com/macros/s/"+deploymentId+"/exec?secret="+encodeURIComponent(secret);}functionsetupBot(){bot.setMyCommands([{name: "start",description: "Открыть меню"},{name: "echo",description: "Повторить текст"},]);returnbot.subscribe(getWebhookUrl_(),{updateTypes: ["bot_started","message_created","message_edited","message_callback",],});}functionremoveWebhook(){returnbot.unsubscribe(getWebhookUrl_());}

Опубликуйте Web App

  1. Выберите Развернуть → Новое развертывание → Веб-приложение.
  2. Укажите запуск от своего имени и доступ для всех, затем создайте развертывание.
  3. Замените <DEPLOYMENT_ID> в getWebhookUrl_().
  4. Один раз запустите setupBot() из редактора Apps Script и подтвердите разрешения.
  5. Напишите боту /start.

Секрет находится в query-параметре URL:

https://script.google.com/macros/s/<DEPLOYMENT_ID>/exec?secret=<MAX_WEBHOOK_SECRET>

handleWebhook(e) сравнивает e.parameter.secret со значением webhookSecret, затем разбирает e.postData.contents, запускает обработчики и возвращает ответ OK. Если секрет отсутствует или не совпадает, update не обрабатывается.

Параметр secret метода subscribe(url, options) — отдельное поле подписки MAX. В примере для Apps Script защита doPost(e) выполняется именно через query-параметр URL и настройку webhookSecret клиента.

🤖 Создание клиента

Основная сигнатура:

varbot=MaxBot.create(token,{apiVersion: "v1",webhookSecret: "QUERY_SECRET",parseMode: "markdown",});
НастройкаЗначенияНазначение
apiVersion"v1" или "v2"выбирает версию API; по умолчанию используется v1
webhookSecretнепустая строкаожидаемое значение e.parameter.secret
parseMode"markdown" или "html"формат сообщений по умолчанию

Версии MAX API

  • apiVersion: "v1" использует https://platform-api.max.ru с обычной проверкой HTTPS-сертификата и выбран по умолчанию для совместимости с GAS.
  • apiVersion: "v2" использует https://platform-api2.max.ru. Библиотека передаёт validateHttpsCertificates: false, но Google Apps Script всё равно может завершить запрос с SSL Error из-за сертификата Минцифры.

Если v2 недоступен из GAS, явно используйте apiVersion: "v1".

Настройки необязательны:

varbot=MaxBot.create(properties.getProperty("MAX_BOT_TOKEN"));

Формат конкретного сообщения переопределяет parseMode:

bot.sendMessage({chatId: 123,text: "<b>HTML для одного сообщения</b>",options: {format: "html"},});

💬 Сообщения

sendMessage() принимает один объект. Укажите ровно одного адресата: chatId или userId.

varresult=bot.sendMessage({chatId: 123,text: "Выберите действие",options: {notify: false,disableLinkPreview: true,keyboard: bot.keyboard.callback("Готово","done").link("Сайт","https://max.ru").build(),},});console.log(JSON.stringify(result,null,2));

Личное сообщение пользователю отправляется тем же методом:

bot.sendMessage({userId: 456,text: "Это личное сообщение",});

В одном вызове нельзя одновременно передать chatId и userId. Результат каждого API-метода — разобранный JSON-ответ MAX; пустой успешный HTTP-ответ возвращается как null.

Сообщения также можно получать, редактировать и удалять:

varmessages=bot.getMessages({chatId: 123,count: 20});varmessage=bot.getMessage("MESSAGE_ID");bot.editMessage("MESSAGE_ID",{text: "Исправленный текст"});bot.deleteMessage("MESSAGE_ID");

🎯 Обработчики и ctx

MaxBot предоставляет четыре вида обработчиков:

bot.on("message_created",function(ctx){console.log(ctx.updateType);});bot.command("start",function(ctx){ctx.reply("Команда /start");});bot.hears(/^заказ(\d+)$/i,function(ctx){ctx.reply("Ищу заказ №"+ctx.match[1]);});bot.action(/^item:(\d+)$/,function(ctx){ctx.answerCallback({notification: "Выбран товар "+ctx.match[1]});});
  • on(updateType, handler) реагирует на указанный update_type; "*" подходит для любого типа.
  • command(name, handler) ищет команду в начале сообщения без учёта регистра. Текст после команды доступен в ctx.match[1].
  • hears(stringOrRegExp, handler) проверяет текст события message_created. Строка должна совпасть целиком, регулярное выражение работает как обычно.
  • action(stringOrRegExp, handler) проверяет callback.payload события message_callback.

Регистрации можно объединять в цепочку. Все совпавшие обработчики выполняются синхронно в порядке регистрации:

bot.on("message_created",logMessage).hears("ping",function(ctx){ctx.reply("pong");}).command("start",showMenu);

В контексте обработчика доступны:

Поле или методЧто содержит
ctx.updateисходный update MAX
ctx.updateTypeзначение update.update_type
ctx.messageсообщение или null
ctx.callbackcallback или null
ctx.userпользователь callback, отправитель сообщения или update.user
ctx.chatIdID текущего чата или null
ctx.messageIdmessage.body.mid или null
ctx.matchрезультат RegExp либо null
ctx.reply(text, options)отправляет сообщение в текущий чат
ctx.answerCallback(answer)отвечает на текущий callback

Для уже разобранного update можно вызвать диспетчер напрямую:

varctx=bot.handleUpdate(update);console.log(ctx.updateType);

⌨️ Клавиатуры

Fluent-builder начинает с первой строки, а .row() переносит следующие кнопки на новую:

varkeyboard=bot.keyboard.callback("Да","confirm").callback("Нет","cancel").row().requestContact("Отправить контакт").requestGeoLocation("Отправить геопозицию",true).row().openApp("Открыть приложение","https://example.com/app").message("Написать оператору").row().clipboard("Скопировать код","MAX-2026").link("Открыть MAX","https://max.ru").build();bot.sendMessage({chatId: 123,text: "Что сделать?",options: {keyboard: keyboard},});

Какая кнопка нажата

Команда /numbers показывает пять кнопок. Значение после number: передаётся в callback.payload, а ctx.match[1] содержит выбранную цифру:

bot.command("numbers",function(ctx){returnctx.reply("Выберите цифру:",{keyboard: bot.keyboard.callback("1","number:1").callback("2","number:2").callback("3","number:3").callback("4","number:4").callback("5","number:5").build(),});});bot.action(/^number:([1-5])$/,function(ctx){varnumber=ctx.match[1];ctx.answerCallback({notification: "Вы нажали "+number});returnctx.reply("Вы выбрали цифру: "+number);});

Чтобы получать нажатия, добавьте message_callback в updateTypes webhook-подписки.

Опрос с сохранением состояния

В GAS каждый webhook запускается отдельно, поэтому текущий шаг и ответы сохраняются в Script Properties. Ключ содержит chatId и userId: пользователи могут проходить опрос параллельно в разных чатах.

varsurveyProperties=PropertiesService.getScriptProperties();functionsurveyKey_(ctx){return"survey:"+ctx.chatId+":"+ctx.user.user_id;}functionreadSurvey_(ctx){varvalue=surveyProperties.getProperty(surveyKey_(ctx));returnvalue ? JSON.parse(value) : null;}functionwriteSurvey_(ctx,survey){surveyProperties.setProperty(surveyKey_(ctx),JSON.stringify(survey));}bot.command("survey",function(ctx){writeSurvey_(ctx,{step: "name"});returnctx.reply("Как вас зовут?");});bot.command("cancel",function(ctx){surveyProperties.deleteProperty(surveyKey_(ctx));returnctx.reply("Опрос отменён.");});bot.on("message_created",function(ctx){varsurvey=readSurvey_(ctx);vartext=ctx.message&&ctx.message.body&&ctx.message.body.text;if(!survey||!text||/^\//.test(text))return;text=text.trim();if(!text)return;if(survey.step==="name"){survey.name=text;survey.step="gender";writeSurvey_(ctx,survey);returnctx.reply("Укажите пол:",{keyboard: bot.keyboard.callback("Мужской","survey:gender:male").callback("Женский","survey:gender:female").callback("Не указывать","survey:gender:none").build(),});}if(survey.step==="age"){varage=Number(text);if(!Number.isInteger(age)||age<1||age>120){returnctx.reply("Введите возраст целым числом от 1 до 120.");}survey.age=age;survey.step="country";writeSurvey_(ctx,survey);returnctx.reply("Выберите страну:",{keyboard: bot.keyboard.callback("Россия","survey:country:russia").callback("США","survey:country:usa").callback("Италия","survey:country:italy").callback("Другое","survey:country:other").build(),});}});bot.action(/^survey:gender:(male|female|none)$/,function(ctx){varsurvey=readSurvey_(ctx);vargenders={male: "Мужской",female: "Женский",none: "Не указывать",};if(!survey||survey.step!=="gender"){returnctx.answerCallback({notification: "Этот шаг уже завершён"});}survey.gender=genders[ctx.match[1]];survey.step="age";writeSurvey_(ctx,survey);ctx.answerCallback({notification: "Выбрано: "+survey.gender});returnctx.reply("Сколько вам лет?");});bot.action(/^survey:country:(russia|usa|italy|other)$/,function(ctx){varsurvey=readSurvey_(ctx);varcountries={russia: "Россия",usa: "США",italy: "Италия",other: "Другое",};if(!survey||survey.step!=="country"){returnctx.answerCallback({notification: "Опрос уже завершён"});}survey.country=countries[ctx.match[1]];surveyProperties.deleteProperty(surveyKey_(ctx));ctx.answerCallback({notification: "Страна: "+survey.country});returnctx.reply(["Анкета заполнена:","Имя: "+survey.name,"Пол: "+survey.gender,"Возраст: "+survey.age,"Страна: "+survey.country,].join("\n"),{format: null});});

Для этого примера подпишите webhook на message_created и message_callback. Команды /survey и /cancel также можно добавить через bot.setMyCommands().

Доступные методы builder: callback, link, requestContact, requestGeoLocation, openApp, message, clipboard, row и build.

Для ручной сборки используйте MaxBot.inlineKeyboard() и MaxBot.button.*:

varkeyboard=MaxBot.inlineKeyboard([[MaxBot.button.callback("ОК","ok"),MaxBot.button.link("Документация","https://dev.max.ru/docs-api"),],]);

Готовые вложения создаются через MaxBot.attachment.image(), .video(), .audio(), .file(), .sticker(), .contact(), .location() и .share().

🖼️ Фото, видео, аудио и файлы

Media builder возвращает фрагмент { text, options }, который удобно добавить в единственный объект sendMessage().

Изображение по URL

bot.sendMessage({chatId: 123,
...bot.photo.url("https://example.com/photo.jpg").caption("**Красивое фото!**").format("markdown").keyboard(bot.keyboard.callback("Нравится","like").build()).build(),});

Изображение с внешним URL передаётся в MAX напрямую. Для видео, аудио и файла библиотека сначала скачивает URL как GAS Blob, затем загружает его в MAX:

bot.sendMessage({chatId: 123,
...bot.video.url("https://example.com/video.mp4").caption("Видео").build(),});

Можно передать готовый Blob из Google Drive:

varfileBlob=DriveApp.getFileById("GOOGLE_DRIVE_FILE_ID").getBlob();bot.sendMessage({chatId: 123,
...bot.file.blob(fileBlob).caption("Документ").build(),});

Или повторно использовать полученный ранее upload token:

bot.sendMessage({chatId: 123,
...bot.audio.token("UPLOAD_TOKEN").caption("Аудио").build(),});

Для ручной загрузки Blob доступен bot.uploadMedia(type, blob), где type"image", "video", "audio" или "file".

🛠️ Команды и подписки

Команды бота

functioninstallCommands(){returnbot.setMyCommands([{name: "start",description: "Открыть меню"},{name: "help",description: "Показать помощь"},]);}functionshowCommands(){console.log(JSON.stringify(bot.getMyCommands(),null,2));}functionremoveCommands(){returnbot.deleteMyCommands();}

Webhook-подписки

varurl=getWebhookUrl_();bot.subscribe(url,{updateTypes: ["bot_added","bot_started","bot_stopped","bot_removed","chat_title_changed","dialog_cleared","dialog_muted","dialog_unmuted","dialog_removed","message_callback","message_created","message_edited","message_removed","user_added","user_removed",],});console.log(JSON.stringify(bot.getSubscriptions(),null,2));bot.unsubscribe(url);

Передавайте только нужные вашему боту события. На 12 августа 2026 года официальный объект Update содержит такие типы:

ТипКогда приходит
bot_addedбот добавлен в чат или канал
bot_startedпользователь начал или возобновил общение с ботом
bot_stoppedпользователь остановил или удалил бота в настройках
bot_removedбот удалён из чата или канала
chat_title_changedизменено название чата или канала
dialog_clearedпользователь очистил историю диалога с ботом
dialog_mutedпользователь отключил уведомления в диалоге
dialog_unmutedпользователь включил уведомления в диалоге
dialog_removedпользователь удалил диалог с ботом
message_callbackнажата callback-кнопка
message_createdотправлено сообщение или опубликован пост
message_editedсообщение или пост отредактирован
message_removedсообщение или пост удалён
user_addedпользователь добавлен в чат или канал либо перешёл по ссылке
user_removedпользователь удалён или вышел из чата или канала

subscribe() преобразует updateTypes в поле API update_types и принимает отдельный параметр secret, если он нужен вашему сценарию. Для диагностического получения обновлений без webhook доступен:

varupdates=bot.getUpdates({limit: 20,timeout: 0,marker: null,types: ["message_created"],});

⚠️ Ошибки

HTTP-ответ вне диапазона 2xx превращается в MaxBot.MaxError:

try{bot.sendMessage({chatId: 123,text: "Проверка"});}catch(error){if(errorinstanceofMaxBot.MaxError){console.error("HTTP status: "+error.status);console.error("MAX code: "+error.code);console.error("Описание: "+error.description);console.error("Метод и путь: "+error.method+" "+error.path);console.error("Ответ: "+error.responseText);}else{throwerror;}}

У MaxError есть поля status, code, description, response, responseText, method и path. Сетевые ошибки, ошибки JSON и исключения из обработчиков передаются вызывающему коду без автоматических повторов.

🧩 Свой класс бота

MaxBot.Bot можно наследовать и добавлять методы своего приложения:

classShopBotextendsMaxBot.Bot{sendOrder(chatId,orderId){returnthis.sendMessage({chatId: chatId,text: "Заказ №"+orderId+" принят",});}}varproperties=PropertiesService.getScriptProperties();varshopBot=newShopBot(properties.getProperty("MAX_BOT_TOKEN"),{apiVersion: "v1",webhookSecret: properties.getProperty("MAX_WEBHOOK_SECRET"),parseMode: "markdown",});shopBot.command("order",function(ctx){varorderId=ctx.match&&ctx.match[1];if(orderId)shopBot.sendOrder(ctx.chatId,orderId);});

Наследуемый класс получает тот же конструктор, API-методы, handlers и builders, что и клиент из MaxBot.create().

📚 Документация

Готовые примеры в репозитории:

🧪 Проверка проекта

Тесты запускаются локально на Node.js без обращения к настоящему API:

npm test

📄 Лицензия

Проект распространяется по лицензии MIT и не является официальной библиотекой MAX.

About

Google Apps Script библиотека для работы с методами API MAX с поддержкой вебхуков, созданием медиафайлов и клавиатур.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages