✉️ Сообщения
Отправка, редактирование, удаление, реакции, уведомления о наборе.
send_message(chat_id, text, reply_to=None)
MSG_SEND · opcode 64
Отправляет текстовое сообщение. Подтверждено в бою ✅
| Параметр | Тип | Описание |
|---|---|---|
chat_id | int | ID чата (из списка чатов) |
text | str | Текст сообщения |
reply_to | int | Необязательно. ID сообщения для ответа (цитата) |
chatId, message (полный объект), unread, markСтруктура wire-запроса
Сервер ожидает вложенный объект message, а не плоские параметры:
{
"chatId": 34540359,
"message": {
"cid": 1774194113898, # уникальный client ID = int(time.time()*1000)
"text": "Привет!",
"detectShare": false,
"isLive": false
}
}
cid — клиентский ID для дедупликации. Если передавать одно и то же значение
(например chatId), сервер вернёт старое сообщение не отправляя новое.
Библиотека использует int(time.time() * 1000) — уникальный на каждый вызов.
Форматирование текста (elements)
Параметр text принимает как строку, так и объект FormattedText.
При передаче FormattedText библиотека автоматически формирует массив elements.
from maxapi.formatting import FormattedText fmt = (FormattedText() .bold("Жирный") .add(" и ") .italic("курсив") .add(" + ") .link("ссылка", "https://example.com") ) await client.send_message(chat_id, fmt)
Поддерживаемые типы форматирования (подтверждено в бою ✅):
| Метод | Тип элемента | Описание |
|---|---|---|
.bold(text) | STRONG | Жирный текст |
.italic(text) | EMPHASIZED | Курсив |
.underline(text) | UNDERLINE | Подчёркнутый |
.strike(text) | STRIKETHROUGH | Зачёркнутый |
.code(text) | MONOSPACED | Моноширинный (код) |
.heading(text) | HEADING | Заголовок |
.quote(text) | QUOTE | Цитата-блок |
.link(text, url) | LINK | Гиперссылка |
.add(text) | — | Обычный текст (без форматирования) |
Избранное подтвердила не только отдельные типы, но и смешанное форматирование по нескольким кускам текста. Сервер сохранил elements и в прямом ответе send_message(), и в get_chat_history().Wire-формат elements
Массив elements передаётся внутри объекта message. Каждый элемент — это объект с полями:
{
"type": "STRONG", # тип форматирования
"from": 15, # смещение в символах (опускается если 0)
"length": 6, # длина диапазона
"attributes": { ... } # только для LINK (содержит url)
}
"from" не передаётся если смещение = 0. Сервер не принимает
"from": 0 — форматирование не отображается. Библиотека обрабатывает это автоматически.
from, а сервер всё равно возвращает рабочую разметку.forward_message(to_chat_id, from_chat_id, message_id)
MSG_SEND · opcode 64 (link.type=FORWARD)
Пересылает сообщение в другой (или тот же) чат. Подтверждено в бою ✅
| Параметр | Тип | Описание |
|---|---|---|
to_chat_id | int | ID чата-получателя |
from_chat_id | int | ID чата-источника |
message_id | int | ID пересылаемого сообщения |
text вместе с link.type="FORWARD", сервер возвращает errors.forward.text.not-empty. Поэтому библиотека всегда шлёт пустой текст.await client.forward_message( to_chat_id=34540359, from_chat_id=63530148, message_id=116278582161789412 )
Wire-формат пересылки
{
"chatId": 34540359,
"message": {
"cid": 1774269905000,
"text": "",
"link": {
"type": "FORWARD",
"messageId": 116278505421542491,
"chatId": 63530148
}
}
}
send_photo(chat_id, image, caption="", reply_to=None)
PHOTO_UPLOAD + MSG_SEND · opcode 80/64
Загружает изображение на сервер и отправляет его в чат. Двухшаговый процесс: сначала получает одноразовый URL загрузки (opcode 80), потом делает HTTP multipart POST, и наконец отправляет MSG_SEND со вложением. Подтверждено в бою ✅
| Параметр | Тип | Описание |
|---|---|---|
chat_id | int | ID чата |
image | str | bytes | Путь к файлу или байты изображения (JPEG/PNG) |
caption | str | Необязательно. Подпись под фото |
reply_to | int | Необязательно. ID сообщения для ответа |
Протокол загрузки (два шага)
Шаг 1 — запрос одноразового URL (opcode 80 PHOTO_UPLOAD):
# Запрос { "chatId": 55474168, "count": 1 } # Ответ сервера { "url": "https://iu.oneme.ru/uploadImage?X-Amz-Algorithm=...", "photoIds": "8361541005" }
Шаг 2 — HTTP multipart POST на полученный URL:
# Content-Type: multipart/form-data # поле: file = <байты JPEG>, filename=photo.jpg, content_type=image/jpeg # Ответ { "photos": { "8361541005": { "token": "bso0sDwO...==" } } }
Шаг 3 — MSG_SEND с attaches:
{
"chatId": 55474168,
"message": {
"cid": 1774199151982,
"text": "Привет от MAX",
"detectShare": false,
"isLive": false,
"attaches": [
{ "_type": "PHOTO", "photoToken": "bso0sDwO...==" }
],
"link": { "type": "REPLY", "messageId": 116273915609219087 }
}
}
"_type": "PHOTO" (с подчёркиванием), а токен передаётся как "photoToken".
Поля "type" (без подчёркивания) или "token" сервер не принимает и возвращает
Invalid attachment _type: null.
Ответ сервера (MSG_SEND)
{
"chatId": 55474168,
"message": {
"id": 116273915651779748,
"time": 1774199152401,
"type": "USER",
"sender": 61091213,
"text": "Привет от MAX",
"attaches": [
{
"_type": "PHOTO",
"photoId": 8361541005,
"photoToken": "bso0sDwO...==",
"baseUrl": "https://i.oneme.ru/i?r=BTExG6...",
"width": 1,
"height": 1
}
],
"link": {
"type": "REPLY",
"message": { "id": 116273915609219087, "text": "Привет", /* ... */ },
"chatId": 0
}
},
"unread": 0
}
Пример — юзербот: отвечаем картинкой на любое сообщение
from maxapi import MaxClient, Message client = MaxClient("my_session") @client.on_message async def on_message(msg: Message): if msg.is_outgoing: return await client.send_photo( msg.chat_id, "banner.jpg", caption="Привет!", reply_to=msg.id, )
upload_photo(chat_id, image_data)
PHOTO_UPLOAD + HTTP · opcode 80
Нижнеуровневый метод: загружает байты изображения и возвращает photoToken — строку, пригодную для вставки в attaches вручную. Используется внутри send_photo(), но может вызываться напрямую. Подтверждено в бою ✅
| Параметр | Тип | Описание |
|---|---|---|
chat_id | int | ID чата (используется при получении URL загрузки) |
image_data | bytes | Байты JPEG или PNG изображения |
photoToken)with open("photo.jpg", "rb") as f: data = f.read() token = await client.upload_photo(chat_id=55474168, image_data=data) # Вставить токен вручную в attaches await client.send_message( chat_id=55474168, text="Смотри:", attaches=[{ "_type": "PHOTO", "photoToken": token }], )
Для ответа (reply) добавляется поле link:
{
"chatId": 34540359,
"message": {
"cid": 1774194901424, # int(time.time()*1000)
"text": "Ответ на твоё сообщение",
"detectShare": false,
"isLive": false,
"link": { "type": "REPLY", "messageId": 116273064558352190 }
}
}
Пример
# Обычное сообщение result = await client.send_message(chat_id=34540359, text="Тест") msg = result["message"] msg_id = msg.get("msgId") or msg.get("id") # Ответ (цитата) await client.send_message(chat_id=34540359, text="Ответ!", reply_to=msg_id)
id и msgId. Это касается не только истории, но и результатов send_message() / forward_message().Ответ сервера
{
"chatId": 34540359,
"message": {
"id": 116273301732920444, # назначенный id
"time": 1774189784743, # Unix ms
"type": "USER",
"sender": 61091213,
"cid": 34540359,
"text": "Тест",
"attaches": []
},
"unread": 0,
"mark": 1774189784743
}
edit_message(chat_id, msg_id, text)
MSG_EDIT · opcode 67
Редактирует текст уже отправленного сообщения.
| Параметр | Тип | Описание |
|---|---|---|
chat_id | int | ID чата |
msg_id | int | ID сообщения |
text | str | Новый текст |
await client.edit_message( chat_id=34540359, msg_id=116273301732920444, text="Исправленный текст" )
delete_messages(chat_id, msg_ids, delete_for_all=False)
MSG_DELETE · opcode 66
Удаляет одно или несколько сообщений. Подтверждены оба режима: локальное удаление и удаление у всех ✅
| Параметр | Тип | Описание |
|---|---|---|
chat_id | int | ID чата |
msg_ids | list[int] | Список ID сообщений |
delete_for_all | bool | Если True, удаляет сообщение у всех участников. По умолчанию False — удалить только у текущего аккаунта |
{"chatId": 63530148, "messageIds": [116279557369692993]}: сообщение исчезает из истории текущего аккаунта.delete_for_all=True тоже подтверждён в живой проверке: сообщение было удалено у обеих сторон.await client.delete_messages( chat_id=34540359, msg_ids=[116273301732920444, 116273301732920445] )
await client.delete_messages( chat_id=34540359, msg_ids=[116273301732920444], delete_for_all=True )
send_typing(chat_id)
MSG_TYPING · opcode 65
Отправляет уведомление «печатает…». Запрос без ожидания ответа (wait_response=False).
| Параметр | Тип |
|---|---|
chat_id | int |
await client.send_typing(chat_id=34540359)
mark_read(chat_id, msg_id)
CHAT_MARK · opcode 50
Помечает сообщения как прочитанные (до указанного ID включительно).
| Параметр | Тип | Описание |
|---|---|---|
chat_id | int | ID чата |
msg_id | int | ID последнего прочитанного сообщения |
react(chat_id, msg_id, reaction)
MSG_REACTION · opcode 178
Ставит реакцию на сообщение. Fire-and-forget (ответ не ожидается).
| Параметр | Тип | Описание |
|---|---|---|
chat_id | int | ID чата |
msg_id | int | ID сообщения |
reaction | str | Эмодзи реакции, например "👍" |
await client.react(chat_id=63530148, msg_id=1234, reaction="🤣")
cancel_reaction(chat_id, msg_id)
MSG_CANCEL_REACTION · opcode 179
Убирает реакцию с сообщения. Fire-and-forget.
| Параметр | Тип | Описание |
|---|---|---|
chat_id | int | ID чата |
msg_id | int | ID сообщения |
await client.cancel_reaction(chat_id=63530148, msg_id=1234)
send_sticker(chat_id, sticker_id)
MSG_SEND · opcode 64
Удобная обёртка для отправки стикера. Эквивалентна send_message(chat_id, "", attaches=[…]). Подтверждено в бою ✅
| Параметр | Тип | Описание |
|---|---|---|
chat_id | int | ID чата |
sticker_id | int | ID стикера |
messagesticker_id=1083866299 вернула сообщение со вложением attaches[0]._type="STICKER" и тем же stickerId.r = await client.send_sticker(chat_id=63530148, sticker_id=1083866299)
get_link_info(url)
LINK_INFO · opcode 89
Получает превью ссылки (title, description, thumbnail).
link, хотя Python-аргумент называется url. Если превью не найдено, сервер обычно возвращает обычный ответ с error="link.not.found", а не исключение.info = await client.get_link_info("https://example.com")
📡 Входящие уведомления (NOTIF)
Сервер присылает push-уведомления без запроса. Коды из OpCode:
| Константа | Opcode | Событие |
|---|---|---|
| NOTIF_MESSAGE | 128 | Новое входящее сообщение |
| NOTIF_TYPING | 129 | Собеседник набирает текст |
| NOTIF_MARK | 130 | Собеседник прочитал сообщения |
| NOTIF_MSG_DELETE | 142 | Сообщение удалено |
| NOTIF_MSG_DELETE_RANGE | 140 | Удалён диапазон сообщений |
| NOTIF_MSG_REACTIONS_CHANGED | 155 | Реакции изменились |
| NOTIF_MSG_YOU_REACTED | 156 | Реакция поставлена |
| NOTIF_DRAFT | 152 | Черновик сохранён |
| NOTIF_DRAFT_DISCARD | 153 | Черновик сброшен |
| NOTIF_MSG_DELAYED | 154 | Отложенное сообщение |
| NOTIF_CALLBACK_ANSWER | 143 | Ответ на callback (боты) |