Skip to content

Syabr/max_bot

Repository files navigation

max_bot

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 timeoutMax::Bot::Client по умолчанию 30 секунд).

Запуск бота (long polling)

Long polling рассчитан на разработку и тесты. Для продакшена MAX рекомендует HTTPS-вебхуки (POST /subscriptions).

  1. Задайте токен:

    export MAX_BOT_TOKEN="ваш_токен"
  2. Из корня репозитория:

    bundle exec ruby examples/polling_bot.rb

Пример делает long polling GET /updates с types: %w[message_created], разбирает апдейты через Max::Bot::UpdateHelpers и отвечает эхом (POST /messages).

Минимальный цикл polling в своём коде

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. Подробнее: Отправить сообщение.

Вложения (Max::Bot::Attachments)

Типизированные хелперы под 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: — порядок сохраняется.

Загрузка медиа (POST /uploads)

Для 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).

Вебхуки (продакшен)

  1. Поднимите HTTPS-endpoint (порт 443, валидная цепочка TLS — см. требования MAX).

  2. Подписка:

    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_-]
    )
  3. В веб-приложении проверьте секретный заголовок и разберите 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. Подробнее: Подписка на обновления.

  1. Отключение вебхука (снова доступен 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('Копировать', 'секретный_код')]
  ])
)

Другие методы API

Метод 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.gemspec

Лицензия

MIT — см. LICENSE.txt.

About

Bot of MAX messenger

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages