Ruby-клиент для Bot API мессенджера MAX: отправка сообщений, получение обновлений через long polling или HTTPS-вебхуки, список групповых чатов.
Официальная документация API: dev.max.ru/docs-api.
История изменений: CHANGELOG.md (английский и русский).
- Ruby ≥ 3.2
- Токен бота в кабинете MAX (Чат-боты → Интеграция → Получить токен)
В Gemfile:
gem 'max_bot', '~> 0.3'Локально из этого репозитория:
bundle installПо желанию: таймауты и адаптер Faraday.
require 'max_bot'
Max::Bot.configure do |config|
config.connection_timeout = 95 # секунды (должно быть > long-poll timeout)
config.connection_open_timeout = 20
config.adapter = Faraday.default_adapter
endДля GET /updates таймаут чтения HTTP должен быть больше, чем параметр long polling timeout (в Max::Bot::Client по умолчанию 30 секунд).
Long polling рассчитан на разработку и тесты. Для продакшена MAX рекомендует HTTPS-вебхуки (POST /subscriptions).
-
Задайте токен:
export MAX_BOT_TOKEN="ваш_токен"
-
Из корня репозитория:
bundle exec ruby examples/polling_bot.rb
Пример делает long polling GET /updates с types: %w[message_created], разбирает апдейты через Max::Bot::UpdateHelpers и отвечает эхом (POST /messages).
require 'max_bot'
api = Max::Bot::Api.new(ENV.fetch('MAX_BOT_TOKEN'))
Max::Bot::Client.new(
api.token,
timeout: 30,
limit: 100,
types: %w[message_created],
error_backoff: 5,
logger: Logger.new($stdout) # по желанию
).run do |update|
# update — Hash с символичными ключами (объект Update)
next unless Max::Bot::UpdateHelpers.message_created?(update)
message = Max::Bot::UpdateHelpers.message_from(update)
text = Max::Bot::UpdateHelpers.message_text(message)
dest = Max::Bot::UpdateHelpers.message_destination(message)
next if text.to_s.empty? || dest.nil?
kind, id = dest # :chat_id или :user_id
api.send_message("Вы написали: #{text}", kind => id)
endЧерез метод класса:
Max::Bot::Client.run(ENV.fetch('MAX_BOT_TOKEN'), timeout: 30) { |u| ... }При сетевых или API-ошибках клиент ждёт error_backoff (по умолчанию 3 с) и повторяет запрос. Interrupt (Ctrl+C) прерывает цикл. Вместо logger можно передать on_error: ->(e) { ... }.
api = Max::Bot::Api.new(ENV.fetch('MAX_BOT_TOKEN'))
# Группа / канал
api.send_message('Привет', chat_id: 123)
# Личка
api.send_message('Привет', user_id: 456)
# Markdown / HTML (см. раздел «Форматирование» в доке API)
api.send_message('_cursive_', chat_id: 123, format: 'markdown')send_message соответствует POST /messages: в query — chat_id / user_id, в теле — JSON. Подробнее: Отправить сообщение.
Типизированные хелперы под API: image, video, audio, file, sticker, contact, inline_keyboard, location, share. Примеры клавиатуры — в документации MAX.
Кнопки: callback_button, link_button, request_contact_button, request_geo_location_button, chat_button, message_button, clipboard_button.
api.send_message(
'Выберите',
chat_id: chat_id,
attachment: Max::Bot::Attachments.inline_keyboard([
Max::Bot::Attachments.callback_button('A', 'choice_a'),
Max::Bot::Attachments.callback_button('B', 'choice_b')
])
)
api.send_message(
'Тут',
user_id: user_id,
attachments: [
Max::Bot::Attachments.location(latitude: 55.75, longitude: 37.61),
Max::Bot::Attachments.share(url: 'https://example.com/article')
]
)
api.send_message('Стикер', chat_id: cid, attachment: Max::Bot::Attachments.sticker(code: 'код_стикера'))Можно передать attachment: (один Hash или массив) и/или attachments: — порядок сохраняется.
Для image, video, audio, file: слот загрузки → multipart/form-data на выданный URL → в сообщении использовать token из ответа. См. Загрузка файлов.
# Низкоуровнево
slot = api.create_upload(type: :video) # => { url: "https://..." } и др. поля
meta = api.upload_file(type: :video, path: '/path/to/clip.mp4')
api.send_message('Видео', chat_id: id, attachment: Max::Bot::Attachments.video(token: meta[:token]))
# Одной строкой
api.send_media(type: :image, path: '/path/to/photo.jpg', text: 'Фото', chat_id: id)После загрузки больших файлов перед POST /messages может понадобиться пауза (в доке — ошибка attachment.not.ready).
-
Поднимите HTTPS-endpoint (порт 443, валидная цепочка TLS — см. требования MAX).
-
Подписка:
api.set_webhook( url: 'https://your.domain/max/webhook', update_types: %w[message_created bot_started], secret: 'your_secret' # по желанию, 5–256 символов [a-zA-Z0-9_-] )
-
В веб-приложении проверьте секретный заголовок и разберите JSON:
require 'max_bot' secret = ENV.fetch('WEBHOOK_SECRET') header = Max::Bot::Webhook.extract_secret_header(env) # Rack env halt 401 unless Max::Bot::Webhook.secret_valid?(header, secret) update = Max::Bot::Webhook.parse_json(request_body) # обработка update (тот же формат, что и при long polling)
Заголовок: X-Max-Bot-Api-Secret. Подробнее: Подписка на обновления.
-
Отключение вебхука (снова доступен long polling):
api.delete_webhook('https://your.domain/max/webhook')
# Информация о боте (проверка токена)
bot = api.me
puts bot[:name] # => "My Bot"
# Информация о чате
chat = api.chat(chat_id)
puts chat[:name]
# Получить конкретное сообщение
msg = api.get_message(message_id)
# Редактировать сообщение
api.edit_message(message_id, 'Новый текст', format: 'markdown')
# Удалить сообщение
api.delete_message(message_id)
# Ответ на callback (inline-кнопки)
api.answer_callback(callback_query_id, text: 'Выбрано!', show_alert: true)
# Кнопка clipboard — копирует текст в буфер обмена
api.send_message(
'Нажмите, чтобы скопировать',
chat_id: chat_id,
attachment: Max::Bot::Attachments.inline_keyboard([
[Max::Bot::Attachments.clipboard_button('Копировать', 'секретный_код')]
])
)| Метод | HTTP |
|---|---|
api.chats |
GET /chats |
api.chat(chat_id) |
GET /chats/{chatId} |
api.me |
GET /bots |
api.get_message(message_id) |
GET /messages/{messageId} |
api.edit_message(message_id, ...) |
PUT /messages/{messageId} |
api.delete_message(message_id) |
DELETE /messages/{messageId} |
api.answer_callback(...) |
POST /messages/callback |
api.subscriptions |
GET /subscriptions |
Неуспешные HTTP-ответы вызывают Max::Bot::ApiError: #status, #body (Hash с символичными ключами, если пришёл JSON).
Типичный layout RubyGem: файл гема совпадает с именем, код — в lib/max/bot/.
| Путь | Назначение |
|---|---|
lib/max_bot.rb |
require "max_bot" → подключает max/bot |
lib/max/bot.rb |
Загрузка зависимостей, Max::Bot.configure |
lib/max/bot/api.rb |
Публичные методы Bot API |
lib/max/bot/api/request_builders.rb |
Сборка query/body для {Api} |
lib/max/bot/http.rb |
Faraday, JSON-тело, заголовок Authorization |
lib/max/bot/json.rb |
Разбор ответа, deep_symbolize |
lib/max/bot/attachments.rb |
Билдеры вложений и клавиатуры |
lib/max/bot/multipart_upload.rb |
Multipart-загрузка на CDN-URL |
lib/max/bot/client.rb |
Цикл long polling GET /updates |
Для кастомного транспорта или стабов можно подставить объект как Max::Bot::Http:
api = Max::Bot::Api.new(token, http: my_http_client)В тестах: Max::Bot::Client.new(..., api: fake_api, sleep: ->(_) {}) — без реального sleep и с подменённым API.
bundle install
bundle exec rake test # задача по умолчанию
gem build max_bot.gemspecMIT — см. LICENSE.txt.