max_bot_api 0.1.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 +4 -4
- data/README.md +44 -3
- data/docs/01-your-first-bot.md +3 -1
- data/docs/02-listen-and-respond.md +1 -0
- data/docs/03-attachments.md +10 -0
- data/docs/04-keyboard.md +7 -0
- data/docs/06-subscriptions.md +2 -0
- data/docs/api/methods.md +59 -0
- data/docs/api/objects.md +134 -0
- data/docs/api/overview.md +73 -0
- data/examples/attachments.rb +8 -2
- data/examples/keyboard.rb +5 -0
- data/examples/webhook_rack.rb +4 -0
- data/lib/max_bot_api/builders/keyboard_builder.rb +19 -0
- data/lib/max_bot_api/builders/message_builder.rb +24 -1
- data/lib/max_bot_api/client.rb +33 -9
- data/lib/max_bot_api/errors.rb +4 -0
- data/lib/max_bot_api/resources/chats.rb +19 -0
- data/lib/max_bot_api/resources/messages.rb +27 -9
- data/lib/max_bot_api/resources/uploads.rb +34 -2
- data/lib/max_bot_api/version.rb +1 -1
- metadata +9 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 97114d1ff6c7d35facca9459c0543540867c2d48c6235dd6bb204e47c31a75b3
|
|
4
|
+
data.tar.gz: e7dec618f4d73dae79c33e692478c9dc5c8b06fcc0d3f14949c1178083d15aa3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
-
|
|
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
|
|
39
|
+
- Webhook parsing and secret validation helpers.
|
|
40
40
|
- Upload helpers for files, photos, audio, video.
|
|
41
41
|
|
|
42
42
|
## Configuration
|
|
@@ -44,7 +44,7 @@ client.messages.send(
|
|
|
44
44
|
```ruby
|
|
45
45
|
client = MaxBotApi::Client.new(
|
|
46
46
|
token: ENV.fetch("TOKEN"),
|
|
47
|
-
base_url: "https://
|
|
47
|
+
base_url: "https://platform-api.max.ru/",
|
|
48
48
|
version: "1.2.5"
|
|
49
49
|
)
|
|
50
50
|
```
|
|
@@ -54,6 +54,27 @@ Notes:
|
|
|
54
54
|
- The client uses `Authorization: <token>` (no `Bearer`).
|
|
55
55
|
- Every request includes `v=<version>` query param.
|
|
56
56
|
|
|
57
|
+
## Changelog
|
|
58
|
+
|
|
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.
|
|
61
|
+
|
|
62
|
+
Example:
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
message = MaxBotApi::Builders::MessageBuilder.new
|
|
66
|
+
.set_chat(12345)
|
|
67
|
+
.set_text("Hello")
|
|
68
|
+
.add_photo_by_token("photo-token")
|
|
69
|
+
|
|
70
|
+
keyboard = MaxBotApi::Builders::KeyboardBuilder.new
|
|
71
|
+
row = keyboard.add_row
|
|
72
|
+
row.add_message("Continue")
|
|
73
|
+
|
|
74
|
+
message.add_keyboard(keyboard)
|
|
75
|
+
client.messages.send(message)
|
|
76
|
+
```
|
|
77
|
+
|
|
57
78
|
## Common tasks
|
|
58
79
|
|
|
59
80
|
### Send messages
|
|
@@ -103,9 +124,29 @@ end
|
|
|
103
124
|
|
|
104
125
|
```ruby
|
|
105
126
|
body = request.body.read
|
|
127
|
+
halt 401 unless client.webhook_secret_valid?(headers: request.env, secret: ENV.fetch("WEBHOOK_SECRET"))
|
|
106
128
|
update = client.parse_webhook(body)
|
|
107
129
|
```
|
|
108
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
|
+
|
|
109
150
|
## Builders
|
|
110
151
|
|
|
111
152
|
### Keyboard
|
data/docs/01-your-first-bot.md
CHANGED
|
@@ -38,7 +38,9 @@ client.messages.send(message)
|
|
|
38
38
|
```ruby
|
|
39
39
|
client = MaxBotApi::Client.new(
|
|
40
40
|
token: ENV.fetch("TOKEN"),
|
|
41
|
-
base_url: "https://
|
|
41
|
+
base_url: "https://platform-api.max.ru/",
|
|
42
42
|
version: "1.2.5"
|
|
43
43
|
)
|
|
44
44
|
```
|
|
45
|
+
|
|
46
|
+
Note: if you still use the legacy host, set `base_url: "https://botapi.max.ru/"`.
|
data/docs/03-attachments.md
CHANGED
|
@@ -47,3 +47,13 @@ message = MaxBotApi::Builders::MessageBuilder.new
|
|
|
47
47
|
|
|
48
48
|
client.messages.send(message)
|
|
49
49
|
```
|
|
50
|
+
|
|
51
|
+
You can also attach a photo directly by token:
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
message = MaxBotApi::Builders::MessageBuilder.new
|
|
55
|
+
.set_chat(12345)
|
|
56
|
+
.add_photo_by_token("photo-token")
|
|
57
|
+
|
|
58
|
+
client.messages.send(message)
|
|
59
|
+
```
|
data/docs/04-keyboard.md
CHANGED
|
@@ -18,6 +18,11 @@ keyboard
|
|
|
18
18
|
keyboard
|
|
19
19
|
.add_row
|
|
20
20
|
.add_callback("Picture", "positive", "picture")
|
|
21
|
+
.add_message("Continue")
|
|
22
|
+
|
|
23
|
+
keyboard
|
|
24
|
+
.add_row
|
|
25
|
+
.add_clipboard("Copy docs", "https://dev.max.ru/docs-api/methods/POST/messages")
|
|
21
26
|
|
|
22
27
|
message = MaxBotApi::Builders::MessageBuilder.new
|
|
23
28
|
.set_chat(12345)
|
|
@@ -34,3 +39,5 @@ client.messages.send(message)
|
|
|
34
39
|
- `add_contact(text)`
|
|
35
40
|
- `add_geolocation(text, quick)`
|
|
36
41
|
- `add_open_app(text, app, payload, contact_id)`
|
|
42
|
+
- `add_message(text)`
|
|
43
|
+
- `add_clipboard(text, payload)`
|
data/docs/06-subscriptions.md
CHANGED
data/docs/api/methods.md
ADDED
|
@@ -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}` | Получить информацио о видео | нет |
|
data/docs/api/objects.md
ADDED
|
@@ -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/attachments.rb
CHANGED
|
@@ -5,11 +5,17 @@ require 'max_bot_api'
|
|
|
5
5
|
client = MaxBotApi::Client.new(token: ENV.fetch('TOKEN'))
|
|
6
6
|
chat_id = ENV.fetch('CHAT_ID').to_i
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
photo_token = ENV['PHOTO_TOKEN']
|
|
9
9
|
|
|
10
10
|
message = MaxBotApi::Builders::MessageBuilder.new
|
|
11
11
|
.set_chat(chat_id)
|
|
12
|
-
.add_photo(photo)
|
|
13
12
|
.set_text('Photo attached')
|
|
14
13
|
|
|
14
|
+
if photo_token && !photo_token.empty?
|
|
15
|
+
message.add_photo_by_token(photo_token)
|
|
16
|
+
else
|
|
17
|
+
photo = client.uploads.upload_photo_from_file(path: './image.png')
|
|
18
|
+
message.add_photo(photo)
|
|
19
|
+
end
|
|
20
|
+
|
|
15
21
|
client.messages.send(message)
|
data/examples/keyboard.rb
CHANGED
|
@@ -15,6 +15,11 @@ keyboard
|
|
|
15
15
|
.add_row
|
|
16
16
|
.add_link('Open MAX', 'positive', 'https://max.ru')
|
|
17
17
|
.add_callback('Audio', 'negative', 'audio')
|
|
18
|
+
.add_message('Continue')
|
|
19
|
+
|
|
20
|
+
keyboard
|
|
21
|
+
.add_row
|
|
22
|
+
.add_clipboard('Copy docs', 'https://dev.max.ru/docs-api/methods/POST/messages')
|
|
18
23
|
|
|
19
24
|
message = MaxBotApi::Builders::MessageBuilder.new
|
|
20
25
|
.set_chat(chat_id)
|
data/examples/webhook_rack.rb
CHANGED
|
@@ -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'
|
|
@@ -85,6 +85,25 @@ module MaxBotApi
|
|
|
85
85
|
add_button(button)
|
|
86
86
|
end
|
|
87
87
|
|
|
88
|
+
# Add a message button.
|
|
89
|
+
def add_message(text)
|
|
90
|
+
button = {
|
|
91
|
+
type: 'message',
|
|
92
|
+
text: text
|
|
93
|
+
}
|
|
94
|
+
add_button(button)
|
|
95
|
+
end
|
|
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
|
+
|
|
88
107
|
# Build row payload.
|
|
89
108
|
def build
|
|
90
109
|
@cols
|
|
@@ -4,7 +4,10 @@ module MaxBotApi
|
|
|
4
4
|
module Builders
|
|
5
5
|
# Builder for message payloads.
|
|
6
6
|
class MessageBuilder
|
|
7
|
-
|
|
7
|
+
FORMAT_HTML = 'html'
|
|
8
|
+
FORMAT_MARKDOWN = 'markdown'
|
|
9
|
+
|
|
10
|
+
attr_reader :user_id, :chat_id, :reset, :disable_link_preview
|
|
8
11
|
|
|
9
12
|
# Create a builder from an existing hash payload.
|
|
10
13
|
# @param hash [Hash]
|
|
@@ -16,6 +19,9 @@ module MaxBotApi
|
|
|
16
19
|
builder.set_user(hash[:user_id] || hash['user_id']) if hash.key?(:user_id) || hash.key?('user_id')
|
|
17
20
|
builder.set_chat(hash[:chat_id] || hash['chat_id']) if hash.key?(:chat_id) || hash.key?('chat_id')
|
|
18
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
|
|
19
25
|
|
|
20
26
|
payload = hash[:message] || hash['message'] || hash
|
|
21
27
|
builder.apply_payload(payload)
|
|
@@ -27,6 +33,7 @@ module MaxBotApi
|
|
|
27
33
|
@user_id = nil
|
|
28
34
|
@chat_id = nil
|
|
29
35
|
@reset = false
|
|
36
|
+
@disable_link_preview = false
|
|
30
37
|
@message = {
|
|
31
38
|
attachments: []
|
|
32
39
|
}
|
|
@@ -50,6 +57,12 @@ module MaxBotApi
|
|
|
50
57
|
self
|
|
51
58
|
end
|
|
52
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
|
+
|
|
53
66
|
# Set message text.
|
|
54
67
|
def set_text(text)
|
|
55
68
|
@message[:text] = text
|
|
@@ -106,6 +119,11 @@ module MaxBotApi
|
|
|
106
119
|
add_attachment(type: 'image', payload: { photos: payload[:photos] || payload['photos'] })
|
|
107
120
|
end
|
|
108
121
|
|
|
122
|
+
# Attach a photo payload by token.
|
|
123
|
+
def add_photo_by_token(token)
|
|
124
|
+
add_attachment(type: 'image', payload: { token: token })
|
|
125
|
+
end
|
|
126
|
+
|
|
109
127
|
# Attach audio payload.
|
|
110
128
|
def add_audio(uploaded_info)
|
|
111
129
|
add_attachment(type: 'audio', payload: uploaded_info)
|
|
@@ -169,6 +187,11 @@ module MaxBotApi
|
|
|
169
187
|
@reset
|
|
170
188
|
end
|
|
171
189
|
|
|
190
|
+
# Whether link previews are disabled for message delivery.
|
|
191
|
+
def disable_link_preview?
|
|
192
|
+
@disable_link_preview
|
|
193
|
+
end
|
|
194
|
+
|
|
172
195
|
# Return the message payload hash.
|
|
173
196
|
def to_h
|
|
174
197
|
@message
|
data/lib/max_bot_api/client.rb
CHANGED
|
@@ -6,9 +6,11 @@ module MaxBotApi
|
|
|
6
6
|
# Main API client. Holds auth config and provides resource accessors.
|
|
7
7
|
class Client
|
|
8
8
|
# Default API base URL.
|
|
9
|
-
DEFAULT_BASE_URL = 'https://
|
|
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,11 +201,23 @@ 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
|
|
|
198
|
-
|
|
199
|
-
raise ApiError.new(code: response.status, message:
|
|
219
|
+
error_payload = parse_error_payload(response)
|
|
220
|
+
raise ApiError.new(code: response.status, message: error_payload[:message], details: error_payload[:details])
|
|
200
221
|
end
|
|
201
222
|
|
|
202
223
|
def parse_body(response)
|
|
@@ -209,17 +230,20 @@ module MaxBotApi
|
|
|
209
230
|
JSON.parse(body, symbolize_names: true)
|
|
210
231
|
end
|
|
211
232
|
|
|
212
|
-
def
|
|
233
|
+
def parse_error_payload(response)
|
|
213
234
|
body = response.body.to_s
|
|
214
|
-
return response.reason_phrase.to_s if body.empty?
|
|
235
|
+
return { message: response.reason_phrase.to_s, details: nil } if body.empty?
|
|
215
236
|
|
|
216
237
|
json = JSON.parse(body)
|
|
217
|
-
|
|
218
|
-
|
|
238
|
+
if json.is_a?(Hash)
|
|
239
|
+
return { message: json['code'], details: json['message'] } if json['code'] && json['message']
|
|
240
|
+
return { message: json['error'], details: json['details'] || json['message'] } if json['error']
|
|
241
|
+
return { message: json['message'], details: json['details'] } if json['message']
|
|
242
|
+
end
|
|
219
243
|
|
|
220
|
-
response.reason_phrase.to_s
|
|
244
|
+
{ message: response.reason_phrase.to_s, details: nil }
|
|
221
245
|
rescue JSON::ParserError
|
|
222
|
-
response.reason_phrase.to_s
|
|
246
|
+
{ message: response.reason_phrase.to_s, details: nil }
|
|
223
247
|
end
|
|
224
248
|
end
|
|
225
249
|
end
|
data/lib/max_bot_api/errors.rb
CHANGED
|
@@ -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
|
|
@@ -14,9 +14,11 @@ module MaxBotApi
|
|
|
14
14
|
def get_messages(chat_id: nil, message_ids: nil, from: nil, to: nil, count: nil)
|
|
15
15
|
query = {}
|
|
16
16
|
query['chat_id'] = chat_id if chat_id && chat_id.to_i != 0
|
|
17
|
-
Array(message_ids).
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
query['message_ids'] = Array(message_ids).join(',') if message_ids && !Array(message_ids).empty?
|
|
18
|
+
if from && to
|
|
19
|
+
from_i = from.to_i
|
|
20
|
+
to_i = to.to_i
|
|
21
|
+
from, to = to, from if from_i > to_i
|
|
20
22
|
end
|
|
21
23
|
query['from'] = from if from && from.to_i != 0
|
|
22
24
|
query['to'] = to if to && to.to_i != 0
|
|
@@ -36,11 +38,13 @@ module MaxBotApi
|
|
|
36
38
|
# @param message_id [String]
|
|
37
39
|
# @param message [MaxBotApi::Builders::MessageBuilder, Hash]
|
|
38
40
|
def edit_message(message_id:, message:)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
with_attachment_retry do
|
|
42
|
+
body = message_payload(message)
|
|
43
|
+
result = @client.request(:put, 'messages', query: { 'message_id' => message_id }, body: body)
|
|
44
|
+
return true if result.is_a?(Hash) && result[:success]
|
|
42
45
|
|
|
43
|
-
|
|
46
|
+
raise Error, (result[:message] || 'message update failed')
|
|
47
|
+
end
|
|
44
48
|
end
|
|
45
49
|
|
|
46
50
|
# Delete a message by ID.
|
|
@@ -64,13 +68,13 @@ module MaxBotApi
|
|
|
64
68
|
# Send a message builder without returning the created message.
|
|
65
69
|
# @param message [MaxBotApi::Builders::MessageBuilder, Hash]
|
|
66
70
|
def send(message)
|
|
67
|
-
send_message(message, with_result: false)
|
|
71
|
+
with_attachment_retry { send_message(message, with_result: false) }
|
|
68
72
|
end
|
|
69
73
|
|
|
70
74
|
# Send a message builder and return the created message hash.
|
|
71
75
|
# @param message [MaxBotApi::Builders::MessageBuilder, Hash]
|
|
72
76
|
def send_with_result(message)
|
|
73
|
-
send_message(message, with_result: true)
|
|
77
|
+
with_attachment_retry { send_message(message, with_result: true) }
|
|
74
78
|
end
|
|
75
79
|
|
|
76
80
|
# Check if a message can be sent to the provided phone numbers.
|
|
@@ -106,6 +110,7 @@ module MaxBotApi
|
|
|
106
110
|
query = {}
|
|
107
111
|
query['chat_id'] = message.chat_id if message.chat_id && message.chat_id.to_i != 0
|
|
108
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?
|
|
109
114
|
|
|
110
115
|
response = @client.request(:post, 'messages', query: query, body: message_payload(message),
|
|
111
116
|
reset: message.reset?)
|
|
@@ -133,6 +138,19 @@ module MaxBotApi
|
|
|
133
138
|
end
|
|
134
139
|
end
|
|
135
140
|
end
|
|
141
|
+
|
|
142
|
+
def with_attachment_retry
|
|
143
|
+
attempts = 0
|
|
144
|
+
begin
|
|
145
|
+
yield
|
|
146
|
+
rescue ApiError => e
|
|
147
|
+
attempts += 1
|
|
148
|
+
raise e unless e.attachment_not_ready? && attempts < Client::MAX_RETRIES
|
|
149
|
+
|
|
150
|
+
sleep(2**(attempts - 1))
|
|
151
|
+
retry
|
|
152
|
+
end
|
|
153
|
+
end
|
|
136
154
|
end
|
|
137
155
|
end
|
|
138
156
|
end
|
|
@@ -32,8 +32,12 @@ module MaxBotApi
|
|
|
32
32
|
# @param url [String]
|
|
33
33
|
def upload_media_from_url(type:, url:)
|
|
34
34
|
response = Faraday.get(url.to_s)
|
|
35
|
+
raise Error, "fetch URL failed: HTTP #{response.status}" unless response.status.between?(200, 299)
|
|
36
|
+
|
|
35
37
|
name = attachment_name(response.headers)
|
|
36
38
|
upload_media_from_reader_with_name(type: type, io: StringIO.new(response.body), name: name)
|
|
39
|
+
rescue Faraday::Error => e
|
|
40
|
+
raise NetworkError.new(op: 'GET upload source', original_error: e)
|
|
37
41
|
end
|
|
38
42
|
|
|
39
43
|
# Upload media from an IO object.
|
|
@@ -89,20 +93,48 @@ module MaxBotApi
|
|
|
89
93
|
upload_type = UPLOAD_TYPES.fetch(type.to_sym) { type.to_s }
|
|
90
94
|
endpoint = @client.request(:post, 'uploads', query: { 'type' => upload_type })
|
|
91
95
|
|
|
92
|
-
file_name = name.to_s.empty? ? 'file' : name.to_s
|
|
96
|
+
file_name = name.to_s.empty? ? 'file' : File.basename(name.to_s)
|
|
93
97
|
file_part = Faraday::Multipart::FilePart.new(io, nil, file_name)
|
|
94
98
|
|
|
95
99
|
response = Faraday.post(endpoint[:url]) do |req|
|
|
96
100
|
req.body = { 'data' => file_part }
|
|
97
101
|
end
|
|
98
102
|
|
|
99
|
-
raise
|
|
103
|
+
raise upload_api_error(response) unless response.status.between?(200, 299)
|
|
104
|
+
|
|
105
|
+
return { token: endpoint[:token] } if %w[audio video].include?(upload_type) && endpoint[:token]
|
|
100
106
|
|
|
101
107
|
JSON.parse(response.body, symbolize_names: true)
|
|
102
108
|
rescue Faraday::Error => e
|
|
103
109
|
raise NetworkError.new(op: 'POST uploads', original_error: e)
|
|
104
110
|
end
|
|
105
111
|
|
|
112
|
+
def upload_api_error(response)
|
|
113
|
+
body = response.body.to_s
|
|
114
|
+
return Error.new("upload failed: HTTP #{response.status}") if body.empty?
|
|
115
|
+
|
|
116
|
+
json = begin
|
|
117
|
+
JSON.parse(body)
|
|
118
|
+
rescue StandardError
|
|
119
|
+
nil
|
|
120
|
+
end
|
|
121
|
+
if json.is_a?(Hash)
|
|
122
|
+
if json['code'] && json['message']
|
|
123
|
+
return ApiError.new(code: response.status, message: json['code'], details: json['message'])
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
if json['error']
|
|
127
|
+
return ApiError.new(code: response.status, message: json['error'],
|
|
128
|
+
details: json['details'] || json['message'])
|
|
129
|
+
end
|
|
130
|
+
if json['message']
|
|
131
|
+
return ApiError.new(code: response.status, message: json['message'], details: json['details'])
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
Error.new("upload failed: HTTP #{response.status}")
|
|
136
|
+
end
|
|
137
|
+
|
|
106
138
|
def attachment_name(headers)
|
|
107
139
|
disposition = headers['content-disposition'].to_s
|
|
108
140
|
return '' if disposition.empty?
|
data/lib/max_bot_api/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: max_bot_api
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ChatGPT Codex
|
|
8
8
|
- Dmitry Merkushin
|
|
9
|
+
autorequire:
|
|
9
10
|
bindir: bin
|
|
10
11
|
cert_chain: []
|
|
11
|
-
date:
|
|
12
|
+
date: 2026-06-05 00:00:00.000000000 Z
|
|
12
13
|
dependencies:
|
|
13
14
|
- !ruby/object:Gem::Dependency
|
|
14
15
|
name: base64
|
|
@@ -94,6 +95,9 @@ files:
|
|
|
94
95
|
- docs/04-keyboard.md
|
|
95
96
|
- docs/05-uploads.md
|
|
96
97
|
- docs/06-subscriptions.md
|
|
98
|
+
- docs/api/methods.md
|
|
99
|
+
- docs/api/objects.md
|
|
100
|
+
- docs/api/overview.md
|
|
97
101
|
- examples/attachments.rb
|
|
98
102
|
- examples/basic_send.rb
|
|
99
103
|
- examples/keyboard.rb
|
|
@@ -120,6 +124,7 @@ licenses:
|
|
|
120
124
|
metadata:
|
|
121
125
|
homepage_uri: https://github.com/creogen/max_bot_api
|
|
122
126
|
source_code_uri: https://github.com/creogen/max_bot_api/tree/main
|
|
127
|
+
post_install_message:
|
|
123
128
|
rdoc_options: []
|
|
124
129
|
require_paths:
|
|
125
130
|
- lib
|
|
@@ -134,7 +139,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
134
139
|
- !ruby/object:Gem::Version
|
|
135
140
|
version: '0'
|
|
136
141
|
requirements: []
|
|
137
|
-
rubygems_version: 3.
|
|
142
|
+
rubygems_version: 3.4.10
|
|
143
|
+
signing_key:
|
|
138
144
|
specification_version: 4
|
|
139
145
|
summary: Ruby client for MAX Bot API
|
|
140
146
|
test_files: []
|