max_bot_api 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fb9285c20da62a0c1fba896afde6af5fd10dff12079de7d44878f3cd4b3aea22
4
- data.tar.gz: 11d42f73535db90bcbc32b89fa9886cc96d99222cca56e15ba3fb50eb4132ce8
3
+ metadata.gz: 97114d1ff6c7d35facca9459c0543540867c2d48c6235dd6bb204e47c31a75b3
4
+ data.tar.gz: e7dec618f4d73dae79c33e692478c9dc5c8b06fcc0d3f14949c1178083d15aa3
5
5
  SHA512:
6
- metadata.gz: b10c0eb6c1bef5eedc551fc841cb65d43b65d4d43c28941cbbdf8bb85e0245b251b512b4a9b9f476ca859a189c1967e0ac81f9b09b225569bb66afffb70cfd02
7
- data.tar.gz: 148837674accefde112f9e8195440bce6a7c6a9abd5b8243c87c076b6af72c9b1f148fe66a24104d0ecc3ac82e94011f8841ba3db900842dd8d58f183e895286
6
+ metadata.gz: 8fec44603bf0f56f43e865097608bc786a3549ffa6a754d87803eeb0ada8f6851a074d44469c1e9f9e8629d342af908d252d30e0c576a8d701649af7ffdb2022
7
+ data.tar.gz: 7ac3fe1abc5125e7e63d610506ddf100c77b47c40c5bfe5c50ead48eea04b14cd79fc8f2a86e8073ee82eaa8f85212fcf30b2adea1a8fdbdfd56a9ff9694a99c
data/README.md CHANGED
@@ -33,10 +33,10 @@ client.messages.send(
33
33
 
34
34
  ## Features
35
35
 
36
- - Full API coverage: Bots, Chats, Messages, Subscriptions, Uploads, Debugs.
36
+ - Broad API coverage: Bots, Chats, Messages, Subscriptions, Uploads, Debugs.
37
37
  - Builder helpers for messages and keyboards.
38
38
  - Long polling updates with retry and backoff.
39
- - Webhook parsing helper.
39
+ - Webhook parsing and secret validation helpers.
40
40
  - Upload helpers for files, photos, audio, video.
41
41
 
42
42
  ## Configuration
@@ -54,11 +54,10 @@ Notes:
54
54
  - The client uses `Authorization: <token>` (no `Bearer`).
55
55
  - Every request includes `v=<version>` query param.
56
56
 
57
- ## 0.2.0 updates
57
+ ## Changelog
58
58
 
59
- - Default API host is now `https://platform-api.max.ru/` (override with `base_url:` if you need the legacy host).
60
- - Message send/edit retries automatically when the API responds with `attachment.not.ready`.
61
- - New helpers: `MessageBuilder#add_photo_by_token`, `KeyboardRowBuilder#add_message`.
59
+ - See `CHANGELOG.md` for version-by-version release notes.
60
+ - See `BREAKING_CHANGES_0.1.0_to_0.2.0.md` for the detailed `0.2.0` migration notes.
62
61
 
63
62
  Example:
64
63
 
@@ -125,9 +124,29 @@ end
125
124
 
126
125
  ```ruby
127
126
  body = request.body.read
127
+ halt 401 unless client.webhook_secret_valid?(headers: request.env, secret: ENV.fetch("WEBHOOK_SECRET"))
128
128
  update = client.parse_webhook(body)
129
129
  ```
130
130
 
131
+ ### Disable link previews
132
+
133
+ ```ruby
134
+ message = MaxBotApi::Builders::MessageBuilder.new
135
+ .set_chat(12345)
136
+ .set_text("https://max.ru")
137
+ .set_disable_link_preview(true)
138
+
139
+ client.messages.send(message)
140
+ ```
141
+
142
+ ### Pin and unpin chat messages
143
+
144
+ ```ruby
145
+ client.chats.pin_message(chat_id: 12345, message_id: "mid123")
146
+ client.chats.get_pinned_message(chat_id: 12345)
147
+ client.chats.unpin_message(chat_id: 12345)
148
+ ```
149
+
131
150
  ## Builders
132
151
 
133
152
  ### Keyboard
@@ -35,6 +35,7 @@ end
35
35
 
36
36
  ```ruby
37
37
  post "/webhook" do
38
+ halt 401 unless client.webhook_secret_valid?(headers: request.env, secret: ENV.fetch("WEBHOOK_SECRET"))
38
39
  update = client.parse_webhook(request.body.read)
39
40
 
40
41
  case update[:update_type]
data/docs/04-keyboard.md CHANGED
@@ -20,6 +20,10 @@ keyboard
20
20
  .add_callback("Picture", "positive", "picture")
21
21
  .add_message("Continue")
22
22
 
23
+ keyboard
24
+ .add_row
25
+ .add_clipboard("Copy docs", "https://dev.max.ru/docs-api/methods/POST/messages")
26
+
23
27
  message = MaxBotApi::Builders::MessageBuilder.new
24
28
  .set_chat(12345)
25
29
  .add_keyboard(keyboard)
@@ -36,3 +40,4 @@ client.messages.send(message)
36
40
  - `add_geolocation(text, quick)`
37
41
  - `add_open_app(text, app, payload, contact_id)`
38
42
  - `add_message(text)`
43
+ - `add_clipboard(text, payload)`
@@ -14,6 +14,8 @@ client.subscriptions.subscribe(
14
14
  )
15
15
  ```
16
16
 
17
+ When handling webhook requests, validate the `X-Max-Bot-Api-Secret` header before parsing the body.
18
+
17
19
  ## Unsubscribe
18
20
 
19
21
  ```ruby
@@ -0,0 +1,59 @@
1
+ # Методы API
2
+
3
+ Источник: https://dev.max.ru/docs-api
4
+
5
+ Клиент: MaxBotApi Ruby (`lib/max_bot_api/`).
6
+
7
+ ## bots
8
+
9
+ | Метод | Путь | Описание | Клиент |
10
+ | --- | --- | --- | --- |
11
+ | `GET` | `/me` | Получение информации о боте | да |
12
+
13
+ ## chats
14
+
15
+ | Метод | Путь | Описание | Клиент |
16
+ | --- | --- | --- | --- |
17
+ | `GET` | `/chats` | Получение списка всех групповых чатов | да |
18
+ | `DELETE` | `/chats/{chatId}` | Удаление группового чата | нет |
19
+ | `GET` | `/chats/{chatId}` | Получение информации о групповом чате | да |
20
+ | `PATCH` | `/chats/{chatId}` | Изменение информации о групповом чате | да |
21
+ | `POST` | `/chats/{chatId}/actions` | Отправка действия бота в групповой чат | да |
22
+ | `DELETE` | `/chats/{chatId}/members` | Удаление участника из группового чата | частично (нет block) |
23
+ | `GET` | `/chats/{chatId}/members` | Получение участников группового чата | да |
24
+ | `POST` | `/chats/{chatId}/members` | Добавление участников в групповой чат | да |
25
+ | `GET` | `/chats/{chatId}/members/admins` | Получение списка администраторов группового чата | да |
26
+ | `POST` | `/chats/{chatId}/members/admins` | Назначить администратора группового чата | нет |
27
+ | `DELETE` | `/chats/{chatId}/members/admins/{userId}` | Отменить права администратора в групповом чате | нет |
28
+ | `DELETE` | `/chats/{chatId}/members/me` | Удаление бота из группового чата | да |
29
+ | `GET` | `/chats/{chatId}/members/me` | Получение информации о членстве бота в групповом чате | да |
30
+ | `DELETE` | `/chats/{chatId}/pin` | Удаление закреплённого сообщения в групповом чате | да |
31
+ | `GET` | `/chats/{chatId}/pin` | Получение закреплённого сообщения в групповом чате | да |
32
+ | `PUT` | `/chats/{chatId}/pin` | Закрепление сообщения в групповом чате | да |
33
+
34
+ ## subscriptions
35
+
36
+ | Метод | Путь | Описание | Клиент |
37
+ | --- | --- | --- | --- |
38
+ | `DELETE` | `/subscriptions` | Отписка от обновлений | да |
39
+ | `GET` | `/subscriptions` | Получение подписок | да |
40
+ | `POST` | `/subscriptions` | Подписка на обновления | да (есть version) |
41
+ | `GET` | `/updates` | Получение обновлений | да |
42
+
43
+ ## upload
44
+
45
+ | Метод | Путь | Описание | Клиент |
46
+ | --- | --- | --- | --- |
47
+ | `POST` | `/uploads` | Загрузка файлов | да |
48
+
49
+ ## messages
50
+
51
+ | Метод | Путь | Описание | Клиент |
52
+ | --- | --- | --- | --- |
53
+ | `POST` | `/answers` | Ответ на callback | да |
54
+ | `DELETE` | `/messages` | Удалить сообщение | да |
55
+ | `GET` | `/messages` | Получение сообщений | да |
56
+ | `POST` | `/messages` | Отправить сообщение | да |
57
+ | `PUT` | `/messages` | � едактировать сообщение | да |
58
+ | `GET` | `/messages/{messageId}` | Получить сообщение | да |
59
+ | `GET` | `/videos/{videoToken}` | Получить информацио о видео | нет |
@@ -0,0 +1,134 @@
1
+ # Объекты API
2
+
3
+ Источник: https://dev.max.ru/docs-api
4
+
5
+ Всего объектов: 126
6
+
7
+ | Объект | Краткое описание |
8
+ | --- | --- |
9
+ | `ActionRequestBody` | |
10
+ | `Attachment` | Общая схема, представляющая вложение сообщения |
11
+ | `AttachmentPayload` | |
12
+ | `AttachmentRequest` | Запрос на прикрепление данных к сообщению |
13
+ | `AudioAttachment` | |
14
+ | `AudioAttachmentRequest` | Запрос на прикрепление аудио к сообщению. ДОЛЖЕН быть единственным вложением в сообщении |
15
+ | `BotAddedToChatUpdate` | Вы получите этот update, как только бот будет добавлен в чат |
16
+ | `BotCommand` | до 32 элементов<br/>Команды, поддерживаемые ботом |
17
+ | `BotInfo` | Объект включает общую информацию о боте, URL аватара и описание. Дополнительно содержит список команд, поддерживаемых… |
18
+ | `BotPatch` | |
19
+ | `BotRemovedFromChatUpdate` | Вы получите этот update, как только бот будет удалён из чата |
20
+ | `BotStartedUpdate` | Бот получает этот тип обновления, как только пользователь нажал кнопку `Start` |
21
+ | `BotStoppedUpdate` | Бот получает этот тип обновления, как только пользователь останавливает бота |
22
+ | `Button` | |
23
+ | `Callback` | Объект, отправленный боту, когда пользователь нажимает кнопку |
24
+ | `CallbackAnswer` | Отправьте этот объект, когда ваш бот хочет отреагировать на нажатие кнопки |
25
+ | `CallbackButton` | После нажатия на такую кнопку клиент отправляет на сервер полезную нагрузку, которая содержит |
26
+ | `Chat` | |
27
+ | `ChatAdmin` | |
28
+ | `ChatAdminPermission` | Права администратора чата |
29
+ | `ChatAdminsList` | |
30
+ | `ChatButton` | Кнопка, которая создает новый чат, как только первый пользователь на нее нажмёт. Бот будет добавлен в участники чата… |
31
+ | `ChatList` | |
32
+ | `ChatMember` | Объект включает общую информацию о пользователе или боте, URL аватара и описание (при наличии). Дополнительно содержит… |
33
+ | `ChatMembersList` | |
34
+ | `ChatPatch` | |
35
+ | `ChatStatus` | Статус чата для текущего бота |
36
+ | `ChatTitleChangedUpdate` | Бот получит это обновление, когда будет изменено название чата |
37
+ | `ChatType` | Тип чата: диалог, чат |
38
+ | `ContactAttachment` | |
39
+ | `ContactAttachmentPayload` | |
40
+ | `ContactAttachmentRequest` | Запрос на прикрепление карточки контакта к сообщению. ДОЛЖЕН быть единственным вложением в сообщении |
41
+ | `ContactAttachmentRequestPayload` | |
42
+ | `DataAttachment` | Attachment contains payload sent through `SendMessageButton` |
43
+ | `DialogClearedUpdate` | Бот получает этот тип обновления сразу после очистки истории диалога. |
44
+ | `DialogMutedUpdate` | Вы получите этот update, когда пользователь заглушит диалог с ботом |
45
+ | `DialogRemovedUpdate` | Вы получите этот update, когда пользователь удаляет чат |
46
+ | `DialogUnmutedUpdate` | Вы получите этот update, когда пользователь включит уведомления в диалоге с ботом |
47
+ | `EmphasizedMarkup` | Представляет *курсив* |
48
+ | `Error` | Сервер возвращает это, если возникло исключение при вашем запросе |
49
+ | `FailedUserDetails` | Подробное описание, почему пользователь не был добавлен в чат |
50
+ | `FileAttachment` | |
51
+ | `FileAttachmentPayload` | |
52
+ | `FileAttachmentRequest` | Запрос на прикрепление файла к сообщению. ДОЛЖЕН быть единственным вложением в сообщении |
53
+ | `GetPinnedMessageResult` | |
54
+ | `GetSubscriptionsResult` | Список всех WebHook подписок |
55
+ | `HeadingMarkup` | Представляет заголовок текста |
56
+ | `HighlightedMarkup` | Представляет выделенную часть текста |
57
+ | `Image` | Общая схема, описывающая объект изображения |
58
+ | `InlineKeyboardAttachment` | Кнопки в сообщении |
59
+ | `InlineKeyboardAttachmentRequest` | Запрос на прикрепление клавиатуры к сообщению |
60
+ | `InlineKeyboardAttachmentRequestPayload` | |
61
+ | `Intent` | Намерение кнопки |
62
+ | `Keyboard` | Клавиатура - это двумерный массив кнопок |
63
+ | `LinkButton` | После нажатия на такую кнопку пользователь переходит по ссылке, которую она содержит |
64
+ | `LinkMarkup` | Представляет ссылку в тексте |
65
+ | `LinkedMessage` | |
66
+ | `LocationAttachment` | |
67
+ | `LocationAttachmentRequest` | Запрос на прикрепление клавиатуры к сообщению |
68
+ | `MarkupElement` | |
69
+ | `MediaAttachmentPayload` | |
70
+ | `Message` | Сообщение в чате |
71
+ | `MessageBody` | Схема, представляющая тело сообщения |
72
+ | `MessageButton` | Кнопка для запуска мини-приложения |
73
+ | `MessageCallbackUpdate` | Вы получите этот `update` как только пользователь нажмёт кнопку |
74
+ | `MessageChatCreatedUpdate` | Бот получит это обновление, когда чат будет создан, как только первый пользователь нажмёт кнопку чата |
75
+ | `MessageCreatedUpdate` | ы получите этот `update`, как только сообщение будет создано |
76
+ | `MessageEditedUpdate` | Вы получите этот `update`, как только сообщение будет отредактировано |
77
+ | `MessageLinkType` | Тип связанного сообщения |
78
+ | `MessageList` | Пагинированный список сообщений |
79
+ | `MessageRemovedUpdate` | Вы получите этот `update`, как только сообщение будет удалено |
80
+ | `MessageStat` | Статистика сообщения |
81
+ | `ModifyMembersResult` | � езультат запроса на изменение списка участников |
82
+ | `MonospacedMarkup` | Представляет `моноширинный` или блок ```код``` в тексте |
83
+ | `NewMessageBody` | |
84
+ | `NewMessageLink` | |
85
+ | `OpenAppButton` | Кнопка для запуска мини-приложения |
86
+ | `PhotoAttachment` | Вложение изображения |
87
+ | `PhotoAttachmentPayload` | |
88
+ | `PhotoAttachmentRequest` | |
89
+ | `PhotoAttachmentRequestPayload` | Запрос на прикрепление изображения (все поля являются взаимоисключающими) |
90
+ | `PhotoToken` | |
91
+ | `PhotoTokens` | Это информация, которую вы получите, как только изображение будет загружено |
92
+ | `PinMessageBody` | |
93
+ | `Recipient` | Новый получатель сообщения. Может быть пользователем или чатом |
94
+ | `ReplyButton` | After pressing this type of button client will send a message on behalf of user with given payload |
95
+ | `ReplyKeyboardAttachment` | Custom reply keyboard in message |
96
+ | `ReplyKeyboardAttachmentRequest` | Request to attach reply keyboard to message |
97
+ | `RequestContactButton` | AПосле нажатия на такую кнопку клиент отправляет новое сообщение с вложением текущего контакта пользователя |
98
+ | `RequestGeoLocationButton` | После нажатия на такую кнопку клиент отправляет новое сообщение с вложением текущего географического положения… |
99
+ | `SendContactButton` | AПосле нажатия на такую кнопку клиент отправляет новое сообщение с вложением текущего контакта пользователя |
100
+ | `SendGeoLocationButton` | После нажатия на такую кнопку клиент отправляет новое сообщение с вложением текущего географического положения… |
101
+ | `SendMessageButton` | After pressing this type of button client will send a message on behalf of user with given payload |
102
+ | `SendMessageResult` | |
103
+ | `SenderAction` | Действие, отправляемое участникам чата. Возможные значения: - `"typing_on"` — Бот набирает сообщение. -… |
104
+ | `ShareAttachment` | |
105
+ | `ShareAttachmentPayload` | Полезная нагрузка запроса ShareAttachmentRequest |
106
+ | `ShareAttachmentRequest` | Запрос на прикрепление предпросмотра медиафайла по внешнему URL |
107
+ | `SimpleQueryResult` | Простой ответ на запрос |
108
+ | `StickerAttachment` | |
109
+ | `StickerAttachmentPayload` | |
110
+ | `StickerAttachmentRequest` | Запрос на прикрепление стикера. ДОЛЖЕН быть единственным вложением в сообщении |
111
+ | `StickerAttachmentRequestPayload` | |
112
+ | `StrikethroughMarkup` | Представляет ~зачекрнутый~ текст |
113
+ | `StrongMarkup` | Представляет **жирный** текст |
114
+ | `Subscription` | Схема для описания подписки на WebHook |
115
+ | `SubscriptionRequestBody` | Запрос на настройку подписки WebHook |
116
+ | `TextFormat` | Формат текста сообщения |
117
+ | `UnderlineMarkup` | Представляет <ins>подчеркнутый</ins> текст |
118
+ | `Update` | Объект`Update` представляет различные типы событий, произошедших в чате. См. его наследников > Чтобы получать события… |
119
+ | `UpdateList` | Список всех обновлений в чатах, в которых ваш бот участвовал |
120
+ | `UploadEndpoint` | Точка доступа, куда следует загружать ваши бинарные файлы |
121
+ | `UploadType` | Тип загружаемого файла Поддерживаемые форматы: - `image`: JPG, JPEG, PNG, GIF, TIFF, BMP, HEIC - `video`: MP4, MOV,… |
122
+ | `UploadedInfo` | Это информация, которую вы получите, как только аудио/видео будет загружено |
123
+ | `User` | Объект, описывающий один из вариантов наследования: - [`User`](/docs-api/objects/User) — объект содержит общую… |
124
+ | `UserAddedToChatUpdate` | Вы получите это обновление, когда пользователь будет добавлен в чат, где бот является администратором |
125
+ | `UserIdsList` | |
126
+ | `UserMentionMarkup` | Представляет упоминание пользователя в тексте. Упоминание может быть как по имени пользователя, так и по ID, если у… |
127
+ | `UserRemovedFromChatUpdate` | Вы получите это обновление, когда пользователь будет удалён из чата, где бот является администратором |
128
+ | `UserWithPhoto` | Объект с общей информацией о пользователе или боте, дополнительно содержит URL аватара и описание |
129
+ | `VideoAttachment` | |
130
+ | `VideoAttachmentDetails` | |
131
+ | `VideoAttachmentRequest` | Запрос на прикрепление видео к сообщению |
132
+ | `VideoThumbnail` | |
133
+ | `VideoUrls` | |
134
+ | `bigint` | |
@@ -0,0 +1,73 @@
1
+ # Обзор API MAX
2
+
3
+ Источник: https://dev.max.ru/docs-api
4
+
5
+ ## Что такое API MAX
6
+
7
+ API MAX — это интерфейс для взаимодействия ботов с платформой через HTTPS-запросы к `platform-api.max.ru`. Он позволяет получать данные и выполнять действия (получение, создание, редактирование, удаление ресурсов).
8
+
9
+ ## HTTP методы
10
+
11
+ - `GET` — получить ресурсы
12
+ - `POST` — создать ресурсы (например, отправить сообщения)
13
+ - `PUT` — редактировать ресурсы
14
+ - `DELETE` — удалить ресурсы
15
+ - `PATCH` — исправить ресурсы
16
+
17
+ Параметры передаются в пути, query-строке или теле запроса.
18
+
19
+ ## Авторизация
20
+
21
+ Передавайте токен в заголовке:
22
+
23
+ ```
24
+ Authorization: <token>
25
+ ```
26
+
27
+ Передача токена через query-параметры не поддерживается.
28
+
29
+ ## Ответы и коды ошибок
30
+
31
+ Сервер возвращает JSON и HTTP-код:
32
+
33
+ - `200` — успешная операция
34
+ - `400` — недействительный запрос
35
+ - `401` — ошибка аутентификации
36
+ - `404` — ресурс не найден
37
+ - `405` — метод не допускается
38
+ - `429` — превышено количество запросов
39
+ - `503` — сервис недоступен
40
+
41
+ ## Рекомендации по получению обновлений
42
+
43
+ - Long Polling — для разработки и тестирования
44
+ - Webhook — для production-окружения
45
+
46
+ Ограничение по нагрузке: до **30 rps** на `platform-api.max.ru`.
47
+
48
+ ## Клавиатуры
49
+
50
+ Поддерживаются inline-клавиатуры с ограничениями:
51
+
52
+ - до `210` кнопок
53
+ - до `30` рядов
54
+ - до `7` кнопок в ряду (до `3` для кнопок `link`, `open_app`, `request_geo_location`, `request_contact`)
55
+ - для `link` максимальный размер ссылки — `2048` символов
56
+
57
+ Основные типы кнопок:
58
+
59
+ - `callback` — событие `message_callback`
60
+ - `link` — ссылка
61
+ - `request_contact` — запрос контакта
62
+ - `request_geo_location` — запрос геолокации
63
+ - `open_app` — открытие мини-приложения
64
+ - `message` — отправка текста
65
+
66
+ ## Форматирование текста
67
+
68
+ Поддерживаются `markdown` и `html` в поле `format` объекта `NewMessageBody`.
69
+
70
+ - Markdown: `*курсив*`, `**жирный**`, `~~зачерк~~`, `++подчерк++`, `` `code` ``
71
+ - HTML: `<i>`, `<b>`, `<del>`, `<ins>`, `<pre>/<code>`, `<a href="...">`
72
+
73
+ Для упоминаний используйте `max://user/user_id` и полное имя пользователя.
data/examples/keyboard.rb CHANGED
@@ -17,6 +17,10 @@ keyboard
17
17
  .add_callback('Audio', 'negative', 'audio')
18
18
  .add_message('Continue')
19
19
 
20
+ keyboard
21
+ .add_row
22
+ .add_clipboard('Copy docs', 'https://dev.max.ru/docs-api/methods/POST/messages')
23
+
20
24
  message = MaxBotApi::Builders::MessageBuilder.new
21
25
  .set_chat(chat_id)
22
26
  .add_keyboard(keyboard)
@@ -8,6 +8,10 @@ client = MaxBotApi::Client.new(token: ENV.fetch('TOKEN'))
8
8
  app = lambda do |env|
9
9
  req = Rack::Request.new(env)
10
10
  if req.post? && req.path == '/webhook'
11
+ unless client.webhook_secret_valid?(headers: env, secret: ENV.fetch('WEBHOOK_SECRET'))
12
+ return [401, { 'Content-Type' => 'text/plain' }, ['secret not allowed']]
13
+ end
14
+
11
15
  update = client.parse_webhook(req.body.read)
12
16
 
13
17
  if update[:update_type] == 'message_created'
@@ -94,6 +94,16 @@ module MaxBotApi
94
94
  add_button(button)
95
95
  end
96
96
 
97
+ # Add a clipboard button.
98
+ def add_clipboard(text, payload)
99
+ button = {
100
+ type: 'clipboard',
101
+ text: text,
102
+ payload: payload
103
+ }
104
+ add_button(button)
105
+ end
106
+
97
107
  # Build row payload.
98
108
  def build
99
109
  @cols
@@ -7,7 +7,7 @@ module MaxBotApi
7
7
  FORMAT_HTML = 'html'
8
8
  FORMAT_MARKDOWN = 'markdown'
9
9
 
10
- attr_reader :user_id, :chat_id, :reset
10
+ attr_reader :user_id, :chat_id, :reset, :disable_link_preview
11
11
 
12
12
  # Create a builder from an existing hash payload.
13
13
  # @param hash [Hash]
@@ -19,6 +19,9 @@ module MaxBotApi
19
19
  builder.set_user(hash[:user_id] || hash['user_id']) if hash.key?(:user_id) || hash.key?('user_id')
20
20
  builder.set_chat(hash[:chat_id] || hash['chat_id']) if hash.key?(:chat_id) || hash.key?('chat_id')
21
21
  builder.set_reset(hash[:reset] || hash['reset']) if hash.key?(:reset) || hash.key?('reset')
22
+ if hash.key?(:disable_link_preview) || hash.key?('disable_link_preview')
23
+ builder.set_disable_link_preview(hash[:disable_link_preview] || hash['disable_link_preview'])
24
+ end
22
25
 
23
26
  payload = hash[:message] || hash['message'] || hash
24
27
  builder.apply_payload(payload)
@@ -30,6 +33,7 @@ module MaxBotApi
30
33
  @user_id = nil
31
34
  @chat_id = nil
32
35
  @reset = false
36
+ @disable_link_preview = false
33
37
  @message = {
34
38
  attachments: []
35
39
  }
@@ -53,6 +57,12 @@ module MaxBotApi
53
57
  self
54
58
  end
55
59
 
60
+ # Toggle link previews in sent messages.
61
+ def set_disable_link_preview(disable_link_preview)
62
+ @disable_link_preview = !!disable_link_preview
63
+ self
64
+ end
65
+
56
66
  # Set message text.
57
67
  def set_text(text)
58
68
  @message[:text] = text
@@ -177,6 +187,11 @@ module MaxBotApi
177
187
  @reset
178
188
  end
179
189
 
190
+ # Whether link previews are disabled for message delivery.
191
+ def disable_link_preview?
192
+ @disable_link_preview
193
+ end
194
+
180
195
  # Return the message payload hash.
181
196
  def to_h
182
197
  @message
@@ -9,6 +9,8 @@ module MaxBotApi
9
9
  DEFAULT_BASE_URL = 'https://platform-api.max.ru/'
10
10
  # Default API version appended as query param.
11
11
  DEFAULT_VERSION = '1.2.5'
12
+ # Webhook secret header name.
13
+ SECRET_HEADER = 'X-Max-Bot-Api-Secret'
12
14
  # Default pause between update polling loops.
13
15
  DEFAULT_PAUSE = 1
14
16
  # Default limit for updates requests.
@@ -131,6 +133,13 @@ module MaxBotApi
131
133
  Updates::Parser.parse_update(body.to_s, debug: debug)
132
134
  end
133
135
 
136
+ # Validates the webhook secret header against the expected secret.
137
+ # @param headers [Hash]
138
+ # @param secret [String]
139
+ def webhook_secret_valid?(headers:, secret:)
140
+ header_value(headers, SECRET_HEADER) == secret.to_s
141
+ end
142
+
134
143
  # Perform an HTTP request.
135
144
  # @param method [Symbol]
136
145
  # @param path [String]
@@ -192,6 +201,18 @@ module MaxBotApi
192
201
  body.values.any? { |value| value.is_a?(Faraday::Multipart::FilePart) }
193
202
  end
194
203
 
204
+ def header_value(headers, name)
205
+ target = name.to_s.downcase
206
+
207
+ headers.each do |key, value|
208
+ normalized = key.to_s.downcase.tr('_', '-')
209
+ normalized = normalized.sub(/\Ahttp-/, '')
210
+ return value if normalized == target
211
+ end
212
+
213
+ nil
214
+ end
215
+
195
216
  def handle_response(response)
196
217
  return parse_body(response) if response.status == 200
197
218
 
@@ -86,6 +86,25 @@ module MaxBotApi
86
86
  def send_action(chat_id:, action:)
87
87
  @client.request(:post, "chats/#{chat_id}/actions", body: { action: action })
88
88
  end
89
+
90
+ # Pin a message in chat.
91
+ # @param chat_id [Integer]
92
+ # @param message_id [String]
93
+ def pin_message(chat_id:, message_id:)
94
+ @client.request(:put, "chats/#{chat_id}/pin", body: { message_id: message_id })
95
+ end
96
+
97
+ # Fetch the currently pinned message.
98
+ # @param chat_id [Integer]
99
+ def get_pinned_message(chat_id:)
100
+ @client.request(:get, "chats/#{chat_id}/pin")
101
+ end
102
+
103
+ # Remove the currently pinned message.
104
+ # @param chat_id [Integer]
105
+ def unpin_message(chat_id:)
106
+ @client.request(:delete, "chats/#{chat_id}/pin")
107
+ end
89
108
  end
90
109
  end
91
110
  end
@@ -110,6 +110,7 @@ module MaxBotApi
110
110
  query = {}
111
111
  query['chat_id'] = message.chat_id if message.chat_id && message.chat_id.to_i != 0
112
112
  query['user_id'] = message.user_id if message.user_id && message.user_id.to_i != 0
113
+ query['disable_link_preview'] = 'true' if message.disable_link_preview?
113
114
 
114
115
  response = @client.request(:post, 'messages', query: query, body: message_payload(message),
115
116
  reset: message.reset?)
@@ -2,5 +2,5 @@
2
2
 
3
3
  module MaxBotApi
4
4
  # Gem version.
5
- VERSION = '0.2.0'
5
+ VERSION = '0.3.0'
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: max_bot_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ChatGPT Codex
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2026-03-05 00:00:00.000000000 Z
12
+ date: 2026-06-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: base64
@@ -95,6 +95,9 @@ files:
95
95
  - docs/04-keyboard.md
96
96
  - docs/05-uploads.md
97
97
  - docs/06-subscriptions.md
98
+ - docs/api/methods.md
99
+ - docs/api/objects.md
100
+ - docs/api/overview.md
98
101
  - examples/attachments.rb
99
102
  - examples/basic_send.rb
100
103
  - examples/keyboard.rb