Long Polling — это технология, которая позволяет получать данные о новых событиях с помощью "длинных запросов".
Сервер получает запрос, но отправляет ответ на него не сразу, а лишь тогда, когда произойдет какое-либо событие (например, придёт новое сообщение),
либо истечет заданное время ожидания. После ответа клиент сразу же делает повторный запрос на сервер.
Таким образом поддерживается постоянное соединение с сервером, что позволяет получать новые события в реальном времени.
Немного подробнее о технологии можно почитать здесь.
Документация написана для 19 версии LongPoll.
- Отказ от ответственности
- Подключение
- Получение истории событий
- Структура сообщения
- Описание событий
- Событие 10002. Установка флагов сообщения
- Событие 10003. Сброс флагов сообщения
- Событие 10004. Новое сообщение
- Событие 10005. Редактирование сообщения
- Событие 10006. Прочтение входящих сообщений
- Событие 10007. Прочтение исходящих сообщений
- Событие 8. Друг появился в сети [disabled]
- Событие 9. Друг вышел из сети [disabled]
- Событие 10. Сброс флагов беседы
- Событие 12. Установка флагов беседы
- Событие 10013. Удаление всех сообщений в диалоге
- Событие 10018. Обновление сообщения
- Событие 10019. Сброс кеша сообщения
- Событие 20. Закрепление и открепление беседы (изменился majorId)
- Событие 21. Изменился minorId
- Событие 50. Перевод сообщения
- Событие 51. Изменение данных чата [deprecated]
- Событие 52. Изменение данных чата
- Событие 63. Статус набора сообщения
- Событие 64. Статус записи голосового сообщения
- Событие 65. Статус загрузки фотографии
- Событие 66. Статус загрузки видеозаписи
- Событие 67. Статус загрузки файла
- Событие 80. Изменение количества непрочитанных диалогов
- Событие 81. Изменение состояния невидимки друга
- Событие 90. Добавление или удаление из друзей
- Событие 114. Изменение настроек пуш-уведомлений в беседе
- Событие 115. Звонок
- Событие 119. Ответ callback-кнопки
- Событие 501. Создание папки
- Событие 502. Удаление папки
- Событие 503. Переименование папки
- Событие 504. Добавление бесед в папку
- Событие 505. Удаление бесед из папки
- Событие 506. Изменение порядка папок
- Событие 507. Изменение количества непрочитанных диалогов в папках
- Дополнительная информация
Автор не несет ответственности за точность, полноту или качество предоставленной информации. Используйте последующую информацию на свой страх и риск.
Правила пользования сайтом, пункт 6.7:
6.7. Создаваемые Пользователями приложения API должны использовать только опубликованные на Сайте методы API, а также ID, защищенный ключ и сервисный ключ доступа, указанные в настройках данных приложений. Использование других методов API, а также ID, защищенного ключа и сервисного ключа доступа приложений API третьих лиц, в т.ч. приложения API Администрации Сайта, строго запрещено. Пользователь обязуется регулярно проверять перечень разрешённых методов и незамедлительно вносить корректировки в функциональность своих приложений API в соответствии с изменениями перечня. За нарушение настоящего пункта Пользователь несет предусмотренную применимым законодательством, настоящими Правилами и иными документами Администрации Сайта ответственность. Администрация Сайта при этом оставляет за собой право на защиту собственных прав и законных интересов.
Ссылка для запроса составляется следующим образом:
https://
server
?act=a_check&key=key
&ts=ts
&wait=wait
&mode=mode
&version=version
server
, key
и ts
получаются методом messages.getLongPollServer
server
- Домен лонгполла. Индивидуален для каждого пользователя. Может отличаться у некоторых приложений, от имени которых получался токенkey
- Ключ для идентификации активной сессии. Протухает через час и привязан к айпи адресуts
- Номер последнего события. Лонгполл будет возвращать события, следующие за нимversion
- Версия лонгполлаwait
- Время ожидания нового события в секундах, максимум90
. Рекомендуемое значение:20
mode
- Настройка формата ответа. Рекомендуемое значение:
(1 << 1) | (1 << 3) | (1 << 5) | (1 << 7) | (1 << 9) | (1 << 10)
Бит | Описание |
---|---|
1 << 1 |
Возвращать кладжи (секции additional и attachments в структуре сообщения) |
1 << 3 |
Возвращать расширенную информацию в 114, 115 и 119 событиях |
1 << 5 |
Возвращать pts |
1 << 6 |
[неактуально] Возвращать события изменения онлайна друзей: 8 и 9 |
1 << 7 |
Возвращать random_id |
1 << 9 |
Возвращать события о бизнес-уведомлениях |
1 << 10 |
Возвращать тип online для marked_users при упоминании через @online |
После выполнения запроса сервер вернет ответ следующего вида:
type LongPollResult =
// pts приходит, если в mode выставлен бит (1 << 5)
// updates - массив событий, которые описаны в разделе "Описание событий"
| { ts: number, pts: number, updates: unknown[] }
// ts слишком маленький (отстал более чем на 256 событий) или слишком большой (больше последнего существующего).
// Необходимо воспользоваться переданным ts (возвращается номер последнего события).
// Как получить пропущенные события можно узнать в разделе "Получение истории событий" чуть ниже
| { failed: 1, ts: number }
// Ключ инвалидировался. Необходимо получить новый key, используя метод messages.getLongPollServer
| { failed: 2, error: string }
// Указана неверная версия лонгполла
| { failed: 4, min_version: 0, max_version: 19 }
После обработки ответа нужно повторить запрос, но с новым значением ts
.
Если лонгполл вернул failed: 1
, у вас все еще есть возможность получить пропущенные события.
- Включите получение
pts
при подключении к лонгполлу, добавив флаг1 << 5
вmode
- Всегда сохраняйте возвращаемый лонгполлом
pts
- С использованием
pts
и некоторых других параметров, описанных в документации, вызовите методmessages.getLongPollHistory
.
Формат ответа метода описан здесь: https://github.com/VKCOM/api-schema-typescript/blob/master/src/methods/messages.ts#L773
Поле history
похоже на поле updates
из лонгполла, но в нем будут находиться только персистентные события.
Так же события из этого списка придут в урезанном виде:
3
- Сброс флагов сообщения4
- Новое сообщение5
- Редактирование сообщения18
- Обновление сообщения
Как можно заметить, вместо 10000 + eventId
событий приходят события 3, 4, 5 и 18.
Соответственно вместо conversationMessageId
приходит просто messageId
.
Сам conversationMessageId
при необходимости можно достать из поля messages
в ответе метода.
Структура этих событий выглядит так:
type LongPollHistoryMessageEvent = [
type: 3 | 4 | 5 | 18,
messageId: number,
flags: number,
peerId: number
];
Всю информацию о сообщениях и беседах можно взять из полей messages
и conversations
, где содержатся данные из API.
Если в ответе придет поле more
, то после обработки всех событий нужно будет повторить запрос, указав в поле pts
пришедший new_pts
.
Подробнее про некоторые части структуры можно почитать здесь:
type LongPollMessage = [
type: 10003 | 10004 | 10005 | 10018,
// id сообщения относительно беседы
conversationMessageId: number,
flags: number,
// Этот элемент приходит только для события 10004
minorId?: number,
peerId: number,
timestamp: number,
// Переносы строк обозначаются как <br>, а символы " & < > экранируются
text: string,
// Объект приходит только при указании флага 2 при подключении к LongPoll
additional: {
// Устаревшее поле, не следует использовать
title?: string
// Наличие emoji в сообщении
emoji?: '1'
// id автора сообщения. Приходит только в беседах
from?: string
// Наличие шаблона (для получения шаблона нужно получить сообщение из API)
has_template?: '1'
marked_users?: Array<
// Список из упомянутых людей/сообществ в сообщении. Ответ на другое сообщение так же считается как упоминание
| [1, number[]]
// @online
| [1, 'online', number[]]
// @all
| [1, 'all']
// Исчезающее сообщение в обычном чате
| [2, 'all']
>
// Клавиатура для ботов (для беседы или сообщения)
keyboard?: MessageKeyboard
// Количество секунд до исчезновения сообщения в обычном чате
expire_ttl?: string
// Количество секунд до исчезновения сообщения в фантомном чате
ttl?: number
// Сообщение исчезло, приходит в 10018 событии
is_expired?: '1'
// Приходит, если при отправке сообщения указать параметр payload.
// Возвращает всегда JSON, который может содержать любую информацию.
// Там может содержаться отладочная информация, которая может пригодиться разработчику.
payload?: string
// Так же здесь могут быть поля, описывающие сервисное сообщение
// см. ссылку выше
},
// Объект приходит только при указании флага 2 при подключении к LongPoll
attachments: {
// Есть пересланное сообщение или ответ на сообщение
fwd?: '0_0'
// Ответ на сообщение: '{"conversation_message_id":number}'
// Приходит только в 10004 и 10018 событиях
reply?: string
// Количество вложений в поле attachments
// Число в строке
attachments_count?: string
// JSON с массивом вложений
// Приходит только для некоторых типов вложений
attachments?: string
// Описание вложений вида { attach1_type, attach1, ... }
// Все значения здесь имеют тип строки (и могут быть объектом в строке по типу '{"id":1}')
// см. ссылку выше
},
// Возвращается, если в mode есть флаг 128
randomId: number,
messageId: number,
// 0 (не редактировалось) или timestamp (время редактирования в секундах)
updateTimestamp: number
];
Существует возможность возвращения укороченного кортежа с сообщением. Такое может произойти, если сообщение на момент ответа лонгполла уже было удалено для всех.
Если вы отправили сообщение с определенным random_id
и ждете ответа от лонгполла с этим же
random_id
, то с этим событием определенно возникнут проблемы, потому что в нем отсутствует
random_id
. Чтобы получить random_id
, нужно запросить сообщение через апи:
в случае 10004
события по minorId
(в данном случае это синоним message_id
),
или в случае остальных событий по conversationMessageId
и peerId
.
Далее, если сообщение было не нашим, апи вернет ошибку и мы ее проигнорируем, а если сообщение
было нашим - вернет сообщение с полем deleted: 1
и искомым random_id
.
type LongPollMessageShort = [
type: 10004,
conversationMessageId: number,
flags: number,
minorId: number
];
type LongPollMessageShort = [
type: 10003 | 10005 | 10018,
conversationMessageId: number,
flags: number,
peerId: number
];
Возможные значения флагов сообщения:
- Пометка важным (
8 important
) - Пометка как спам (
64 spam
) - Удаление сообщения (
128 deleted
) - Удаление для всех (
128 deleted
и131072 deleted_all
) - Прослушивание голосового сообщения (
4096 audio_listened
)
Событие приходит как при прослушивании собеседником вашего голосового сообщения, так и при прослушивании вами голосового сообщения собеседника.
Вручную прослушать голосовое сообщение собеседника можно с помощью метода messages.markAsListened
с параметром message_id
или cmid
. Метод вернет 1
при первом прослушивании голосового сообщения,
а 0
при последующих или при попытке прослушать свое сообщение.
type Event10002 = [
type: 10002,
messageId: number,
flags: number,
peerId: number
];
Возможные значения флагов сообщения:
- Прочитано сообщение (
1 unread
). Устаревший флаг, не следует использовать - Отмена пометки важным (
8 important
) - Отмена пометки сообщения как спам (
64 spam
+32768 cancel_spam
) - Восстановление удаленного сообщения (
128 deleted
)
В 3 и 4 случаях возвращается сообщение.
type Event10003 = [
type: 10003,
messageId: number,
flags: number,
peerId: number
] | LongPollMessage;
Данное событие возвращает новое сообщение.
type Event10004 = LongPollMessage;
Данное событие возвращает отредактированное сообщение.
type Event10005 = LongPollMessage;
Вы прочитали в диалоге peer_id
сообщения до msg_id
включительно.
count
- количество непрочитанных сообщений в диалоге.
type Event10006 = [
type: 10006,
peerId: number,
messageId: number,
count: number
];
Собеседник прочитал в диалоге peer_id
сообщения до msg_id
включительно.
count
- количество ваших непрочитанных сообщений в диалоге.
type Event10007 = [
type: 10007,
peerId: number,
messageId: number,
count: number
];
Это событие больше не приходит в LongPoll
Событие не приходит, если у друга включена невидимка хотя бы для одного пользователя.
type Event8 = [
type: 8,
// отрицательный id друга
userId: number,
// 1 - m.vk.com или неизвестное мобильное приложение
// 2 - iPhone
// 3 - iPad
// 4 - Android
// 5 - Windows Phone
// 6 - Windows 8
// 7 - vk.com или неизвестное десктопное приложение
platform: 1 | 2 | 3 | 4 | 5 | 6 | 7,
// время онлайна в секундах
timestamp: number,
// id приложения, с которого онлайн друг
// 0 если онлайн был вызван не приложением (например веб)
appId: 0 | number,
// 1 если онлайн с мобильного, 0 если нет
isMobile: 0 | 1,
// Всегда приходит 0
hasInvisibleMode: 0 | 1
];
Это событие больше не приходит в LongPoll
Событие не приходит, если у друга включена невидимка хотя бы для одного пользователя.
type Event9 = [
type: 9,
// отрицательный id друга
userId: number,
// 1 если бездействовал 5 минут, 0 если покинул сайт
isTimeout: 0 | 1,
// время наступления офлайна в секундах
timestamp: number,
// id приложения, с которого был онлайн друг
// 0 если онлайн был вызван не приложением (например веб)
appId: 0 | number,
// 1 если онлайн с мобильного, 0 если нет
isMobile: 0 | 1,
// Всегда приходит 0
hasInvisibleMode: 0 | 1
];
Флаги бесед описаны здесь.
type Event10 = [
type: 10,
peerId: number,
flags: number
];
Флаги бесед описаны здесь.
type Event12 = [
type: 12,
peerId: number,
flags: number
];
В беседе peerId
были удалены все сообщения до messageId
включительно.
type Event10013 = [
type: 10013,
peerId: number,
messageId: number
];
Приходит при следующих событиях:
- Добавился сниппет (ссылка) - к вложениям добавляется
link
. - Сообщение исчезло — удаляется текст и все вложения, добавляется ключ
is_expired: true
. - Пришел перевод голосового сообщения.
Данное событие возвращает сообщение.
type Event10018 = LongPollMessage;
По какой-либо причине изменилось сообщение (без явного редактирования). Необходимо переполучить сообщение через API.
type Event10019 = [
type: 10019,
messageId: number
];
majorId
и minorId
- это дополнительные айдишники для беседы, которые используются для ее сортировки
в списке всех бесед.
Сначала список сортируется по majorId
, а если есть беседы с одинаковыми majorId
, то они сортируются
по minorId
majorId
принимает следующие значения: 0
, 16
, 32
, 48
, 64
, 80
.
Чем больше значение, тем выше беседа в списке закрепленных.
0
означает незакрепленную беседу.
При закреплении беседы ей присваивается наибольшее доступное значение majorId
,
то есть она закрепляется в самый верх списка.
Если закрепленная беседа всего одна, то ей присваивается majorId = 16
,
если две — верхней присваивается 32
и так далее.
type Event20 = [
type: 20,
peerId: number,
majorId: number,
0
];
Для 10
версии LongPoll событие приходит только в методе
messages.getLongPollHistory
В версиях LongPoll выше 10
событие приходит еще в некоторых сценариях, но это будет подробно расписано только
при обновлении документации на новую версию LongPoll
minorId
означает id
последнего сообщения в беседе, но с одним отличием. В фантомном чате последнее сообщение
может исчезнуть, и чтобы диалог после этого полностью не пропал из списка, minorId
сохраняет в себе id
последнего даже исчезнувшего сообщения.
type Event21 = [
type: 21,
peerId: number,
minorId: number
];
Это событие приходит, если пользователь запросил перевод сообщения.
Если для определённого сообщения перевод запрашивается в первый раз, то вместе с событием 50
приходит и событие 10018, где в объекте additional
будет поле is_translated
.
type Event50 = [
type: 50,
data: {
peer_id: number,
cmid: number,
translation: string,
language: string
}
];
Поле language
представляет собой языковую пару, который выглядит так: ru-en
.
Перед дефисом прописан исходный язык переводимого текста, после дефиса — язык, на который будет переведён текст.
Список поддерживаемых языковых пар можно получить, выполнив метод account.getInfo
. В ответе будет поле messages_translation_language_pairs
.
Чтобы запросить перевод сообщения, нужно выполнить метод messages.translate
(доступен только официальным клиентам).
Параметры метода messages.translate
:
interface MessagesTranslateParams {
peer_id: number,
cmids: string,
language: string
}
Событие означает, что в беседе chatId
изменились какие-то данные.
Более подробно все расписано в 52 событии,
которое приходит одновременно с этим событии.
type Event51 = [
type: 51,
chatId: number
];
Тип | Описание | Дополнительная информация |
---|---|---|
0 | Из беседы был создан фантомный чат | 0 |
1 | Изменилось название беседы | 0 |
2 | Обновилась аватарка беседы | 0 |
3 | Назначен новый администратор | id администратора |
4 | Изменение прав доступа в беседе | mask с правами доступа |
5 | Закрепление или открепление сообщения | conversationMsgId или 0 |
6 | Вступление в беседу | id вступившего |
7 | Выход из беседы | id вышедшего |
8 | Исключение из беседы | id исключенного |
9 | Разжалован администратор | id бывшего админа |
10 | Изменился баннер | 0 |
11 | Появление или скрытие клавиатуры | peerId |
12 | Отозвано / подтверждено / отклонено / пришло приглашение в чат | 0 / 1 / 2 / 3 |
13 | Контакт был сконвертирован в юзера (contactId -> userId ) |
contactId |
14 | Произошло любое действие с бизнес-уведомлением | 0 |
15 | Вы отозвали приглашение контакта или юзера в чат | contactId или userId |
16 | Контакт или юзер отклонил приглашение в чат | contactId или userId |
17 | Контакт или юзер принял приглашение в чат | contactId или userId |
18 | Контакт или юзера пригласили в чат | contactId или userId |
19 | Начало или окончание группового звонка | 1 в начале, 0 в конце |
22 | Чат больше не новый: пришло первое сообщение (только в лс) | 0 |
23 | Изменено оформление чата | 0 |
24 | Изменилось описание чата | 0 |
25 | Изменилось состояние chat_settings.short_poll_reactions |
0 / 1 |
26 | Добавление инкогнито юзера в чат | incognitoId |
27 | Конвертация инкогнито юзера в обычного юзера | incognitoId |
28 | Удаление инкогнито юзера из чата | incognitoId |
При изменении названия (1
) и обновлении аватарки беседы (2
) нужные данные можно взять из
сервисного сообщения в 4 событии.
type Event52 = [
type: 52,
updateType: number,
peerId: number,
extra: number
];
Означает, что в беседе peerId
начали писать текст totalCount
людей. Их id
записаны в userIds
.
В массиве userIds
может появиться и ваш id
, так что нужно фильтровать этот список.
type Event63 = [
type: 63,
peerId: number,
userIds: number[],
totalCount: number,
timestamp: number
];
Вызывается при записи голосового сообщения.
Идентичен событию 63.
Вызывается при загрузке фотографии.
Идентичен событию 63.
Вызывается при загрузке видеозаписи.
Идентичен событию 63.
Вызывается при загрузке файла.
Идентичен событию 63.
Все счетчики возвращаются без учета сообщений в архиве
type Event80 = [
type: 80,
// Количество непрочитанных бесед
unreadCount: number,
// Количество непрочитанных незамьюченных бесед
unreadUnmutedCount: number,
// 0 - показывать количество всех бесед, 1 - только незамьюченные
showOnlyUnmuted: 0 | 1,
// Количество непрочитанных бизнес-уведомлений
businessNotifyUnreadCount: number,
// Количество непрочитанных бесед в мессенджере в шапке
// Эти два счетчика можно сбросить, просто открыв мессенджер в шапке на вебе
headerUnreadCount: number,
// Количество непрочитанных незамьюченных бесед в мессенджере в шапке
headerUnreadUnmutedCount: number,
// Количество непрочитанных бесед в архиве
archiveUnreadCount: number,
// Количество непрочитанных незамьюченных бесед в архиве
archiveUnreadUnmutedCount: number,
// Количество бесед с упоминаниями в архиве
archiveMentionsCount: number
];
Если невидимка будет выключена не для всех, то метод так же вернет state: 1
,
хотя для нас онлайн может быть так же виден
type Event81 = [
type: 81,
// Отрицательный id друга
userId: number,
// 0 - невидимка выключена, 1 - включена
state: 0 | 1,
// время последнего онлайна друга
timestamp: number,
-1,
// При настройке невидимки через VK Me приходит 0, в других случаях не проверял
appId: 0 | number
];
Событие возвращается только для действий с вашей стороны.
Значения actionType
:
2
- вы приняли заявку пользователя3
- вы удалили из друзей или отклонили заявку в друзья пользователя
type Event90 = [
type: 90,
actionType: 2 | 3,
userId: number
];
Для работы необходимо использовать в mode
флаг 8
при выполнении запроса.
peer_id
-id
беседы, в которой включили или выключили уведомленияsound
- нестабильный параметр, не рекомендую его обрабатыватьdisabled_until
может быть трех видов:0
- Уведомления включены-1
- Уведомления выключеныnumber, > 0
, - Уведомления выключены до указанногоtimestamp
type Event114 = [
type: 114,
data: {
peer_id: number
sound: 0 | 1
disabled_until: 0 | -1 | number
}
];
Для работы необходимо использовать в mode
флаг 8
при выполнении запроса.
¯\_(ツ)_/¯
Для работы необходимо использовать в mode
флаг 8
при выполнении запроса.
Callback-кнопки работают следующим образом:
- Бот отправляет клавиатуру (обычную или инлайн), где находится callback-кнопка;
- Пользователь нажимает на эту кнопку и клиент вызывает метод
messages.sendMessageEvent
(параметры см. ниже). Метод возвращает строку -event_id
; - Бот получает событие
message_event
и вызывает методmessages.sendMessageEventAnswer
; - Этот метод вызывает
119
событие LongPoll у пользователя, где вaction
прописано действие, которое необходимо выполнить клиенту.
Параметры метода messages.sendMessageEvent
:
interface MessagesSendMessageEventParams {
peer_id: number
// Находится в кнопке клавиатуры
payload: string
// Нужно передать один из двух параметров,
// если это инлайн клавиатура в сообщении или карусели
message_id?: number
conversation_message_id?: number
// Находится в объекте клавиатуры беседы
// Нужно передать, если это клавиатура беседы
author_id?: number
}
После получения event_id
на 2 стадии нужно начать ждать 119
событие LongPoll с нужным event_id
.
Если за минуту бот так и не пришлет событие, то ожидание ответа следует прекратить.
type Event119 = [
type: 119,
data: {
// Отрицательный ID бота, который ответил на клик по кнопке
owner_id: number
// ID беседы, в которой находится сообщение
peer_id: number
// Уникальный ID события, действующий 1 минуту.
// Нужен для идентификации кликнутой кнопки
event_id: string
// Не приходит, если не нужно выполнять никакое действие
// т.е. бот отправил пустой payload или неизвестный тип действия
action?:
// Показать снекбар с текстом `text`
| { type: 'show_snackbar', text: string }
// Открыть ссылку `link`
| { type: 'open_link', link: string }
// Открыть приложение по ссылке
// https://vk.com/app${app_id}_${owner_id}#${hash}
// https://vk.com/app${app_id}_${owner_id} (если hash = '')
// https://vk.com/app${app_id} (если owner_id = undefined и hash = '')
| { type: 'open_app', app_id: number, owner_id?: number, hash: string }
}
];
type Event501 = [
type: 501,
folderId: number,
folderName: string,
randomId: number
];
type Event502 = [
type: 502,
folderId: number
];
type Event503 = [
type: 503,
folderId: number,
newFolderName: string
];
type Event504 = [
type: 504,
folderId: number,
...addedFolderIds: number[]
];
Например, если в папку с id = 5
добавить три беседы: 88262293
, 172894294
и 2000000346
,
то событие будет выглядеть вот так:
[504, 5, 88262293, 172894294, 2000000346]
type Event505 = [
type: 505,
folderId: number,
...deletedFolderIds: number[]
];
Например, если из папки с id = 5
удалить три беседы: 88262293
, 172894294
и 2000000346
,
то событие будет выглядеть вот так:
[505, 5, 88262293, 172894294, 2000000346]
type Event506 = [
type: 506,
...folderIds: number[]
];
Например, если у нас есть три папки с id
1
, 2
и 4
, то нам может прийти такое событие:
[506, 1, 4, 2]
type Event507 = [
type: 507,
...foldersCounters: FolderCountersTuple[]
];
type FolderCountersTuple = [
folderId: number,
unreadCount: number,
unreadUnmutedCount: number
];
Событие возвращает данные только для тех папок, в которых изменились каунтеры.
Например, если в папке с id = 2
стало 3 непрочитанных замьюченных чата,
а в папке с id = 3
стало 2 непрочитанных чата: один замьючен, а другой нет,
то событие будет выглядеть так:
[507, [2, 3, 0], [3, 2, 1]]
Маской называют сумму некоторых флагов (степеней двойки), которую можно использовать как хорошую замену для объектов или массивов.
// Обычно маску записывают подобным образом:
const mask = 1 | 2 | 8 | 64 | 1024;
// Что эквивалентно сложению чисел:
const mask = 1 + 2 + 8 + 64 + 1024;
// А еще можно не писать магические цифры, а записывать степени двойки в таком формате:
const mask = (1 << 0) | (1 << 1) | (1 << 3) | (1 << 6) | (1 << 10);
Бит | Значение | Описание |
---|---|---|
1 << 0 |
1 |
Непрочитанное сообщение |
1 << 1 |
2 |
Исходящее сообщение |
1 << 3 |
8 |
Важное сообщение |
1 << 4 |
16 |
[deprecated] Сообщение из группового чата |
1 << 5 |
32 |
[deprecated] Сообщение от друга |
1 << 6 |
64 |
Сообщение помечено как спам |
1 << 7 |
128 |
Сообщение удалено локально |
1 << 12 |
4096 |
Прослушано голосовое сообщение |
1 << 13 |
8192 |
Сообщение из группового чата |
1 << 15 |
32768 |
🤔 Сообщение перемещено в список спама |
1 << 16 |
65536 |
Не обновлять minor_id , т.е. не поднимать чат в списке |
1 << 17 |
131072 |
Сообщение удалено для всех |
1 << 18 |
262144 |
[internal] |
1 << 19 |
524288 |
[internal] |
1 << 20 |
1048576 |
Не показывать пуш/уведомление для этого сообщения |
1 << 21 |
2097152 |
Сообщение с ответом на другое сообщение |
1 << 22 |
4194304 |
Сообщение будет удалено при окончании TTL |
1 << 23 |
8388608 |
Сообщение придет автоматически прочитанным, если ранее не было непрочитанных сообщений |
1 << 24 |
16777216 |
Наличие реакции у сообщения |
1 << 26 |
67108864 |
[internal] |
Бесшумное сообщение можно отправить, добавив к параметрам метода messages.send
ключ silent: true
:
- Такое сообщение отправится без уведомления пользователю, даже если у него включены уведомления
- Упоминание или ответ на сообщение тоже не отправит уведомления
- Сообщение о выходе из беседы автоматически приходит без уведомления
Пример определения наличия флага в маске:
const mask = 1 | 2 | 32; // = 35
8 & mask // вернет 0 (false)
2 & mask // вернет 2 (true)
Бит | Значение | Описание |
---|---|---|
1 << 4 |
16 |
Отключение пуш уведомлений |
1 << 5 |
32 |
Отключение звука уведомлений |
1 << 8 |
256 |
Входящий запрос на переписку / вступление в беседу |
1 << 9 |
512 |
Отклоненный запрос на переписку / вступление в беседу |
1 << 10 |
1024 |
Наличие упоминания |
1 << 11 |
2048 |
Не отображать беседу при поиске |
1 << 12 |
4096 |
[internal] |
1 << 13 |
8192 |
Беседа с бизнес-уведомлениями |
1 << 14 |
16384 |
Наличие маркированного сообщения: упоминание или исчезающее сообщение |
1 << 16 |
65536 |
Беседа, которая не пропадает из списка чатов (не сбрасывается minor_id ) |
1 << 18 |
262144 |
Не присылать уведомления об @all и @online |
1 << 19 |
524288 |
Не присылать уведомления о всех упоминаниях |
1 << 20 |
1048576 |
Беседа помечена как непрочитанная |
1 << 22 |
4194304 |
[internal] |
1 << 23 |
8388608 |
Беседа помещена в архив |
1 << 24 |
16777216 |
Беседа, в которой идет звонок |
1 << 26 |
67108864 |
Признак того, что это чат: при создании старого канала снимается этот флаг |
Сервисное сообщение описывается ключом source_act
и ключами с дополнительными данными в объекте additional
из структуры сообщения.
У некоторых сервисных сообщений в поле text
приходит сообщение вида Сообщение не поддерживается Вашим приложением.
,
поэтому при обработке сервисных сообщений не нужно обрабатывать текст сообщения.
Вместе с сервисными сообщениями chat_title_update
и chat_photo_remove
в каналах приходит поле
source_is_channel: "1"
. Сервисное сообщение chat_photo_update
для каналов не приходит
(только 52 событие).
Возможные значения source_act
:
Тип | Дополнительные ключи |
---|---|
chat_create Создание беседы |
source_text - название беседы
|
chat_photo_update Обновление фотографии беседы |
Фото можно получить во вложении сообщения |
chat_photo_remove Удаление фотографии беседы |
|
chat_title_update Обновление названия беседы |
source_old_text - старое название беседыsource_text - новое название беседы
|
chat_pin_message Закрепление сообщения |
source_mid - id закрепившего сообщениеsource_message - обрезанное закрепленное сообщениеsource_chat_local_id - локальный id сообщения
|
chat_unpin_message Открепление сообщения |
source_mid - id открепившего сообщениеsource_chat_local_id - локальный id сообщения
|
chat_invite_user Вступление в беседу |
source_mid - id вступившего в беседу
|
chat_invite_user_by_link Вступление в беседу по ссылке |
|
chat_kick_user Выход или исключение из беседы |
source_mid - id вышедшего или исключенного
|
chat_kick_don Исключение дона из беседы |
Приходит, начиная с версии API 5.154 |
chat_screenshot Создание скриншота с фантомным сообщением |
source_mid - id создавшего скриншот
|
chat_group_call_started Начало группового звонка в беседе |
Больше не приходит в LongPoll Вместо него создается сообщение с вложением group_call_in_progress Приходит для старых сообщений через API при получении через токен VK для Android |
chat_invite_user_by_call Приглашение пользователя в звонок |
source_mid - id приглашенного юзера
|
chat_invite_user_by_call_join_link Присоединение пользователя к звонку по ссылке |
|
chat_invite_user_by_message_request Запрос на добавление в беседу |
source_mid - id пригласившего в беседу
|
conversation_style_update Обновление стиля беседы |
source_style - название стиляПоле не приходит в случае сброса стиля Возможные варианты можно посмотреть здесь |
Пример описания сервисного сообщения:
const longpollServiceMessage = {
source_act: 'chat_pin_message',
source_mid: '88262293',
source_message: 'Сообщение, которое будет в закрепе',
source_chat_local_id: '5517'
}
Клавиатура для ботов представляет собой объект с описанием ее типа и кнопок. Основная структура представлена ниже, остальную информацию можно узнать в документации.
// TODO: новые тайпинги
// Типы у объекта клавиатуры:
// https://github.com/danyadev/vk-types/blob/master/src/objects/VKKeyboard.ts
interface LongPollKeyboard {
// Приходит, если это клавиатура сообщения или карусели
inline?: true
// Скрывать ли клавиатуру при клике на кнопку (не работает для inline)
one_time: boolean
buttons?: VKKeyboardButton[][]
}
Список известных на данный момент вложений:
geo
, doc
, link
, poll
, wall
, call
, gift
, story
, photo
, audio
, video
, event
, market
, artist
, widget
, sticker
,
article
, podcast
, curator
, graffiti
, mini_app
, narrative
, wall_reply
, audio_message
, money_request
, audio_playlist
,
group_call_in_progress
.
Однако названия вложений, полученных через LongPoll, могут не совпадать с теми, что приходят через API:
event
, приходящий в API, в LongPoll обозначается какgroup
graffiti
иaudio_message
из LongPoll обозначаются какdoc
, но при этом добавляется ключattach*_kind
со значениемgraffiti
илиaudiomsg
artist
,article
,narrative
иaudio_playlist
, которые приходят в LongPoll, через API отображаются какlink
mini_app
не приходит в LongPoll, вместо него приходитlink
narrative
приходят в API начиная с версии5.154
Вложения artist
, article
, mini_app
и audio_playlist
приходят в API только через токен VK для Android.
Вложение geo
(прикрепленное местоположение) приходит в виде ключей geo
и geo_provider
вместо attach*
(см. структуру). Также при получении сообщения через messages.getById
ключ geo
будет находиться не во вложениях, а в "корне" сообщения.
Сообщение с вложением group_call_in_progress
создается, когда пользователь начинает групповой звонок.
При окончании группового звонка сообщение с этим вложением удаляется и создается новое сообщение с вложением call
.
Пример вложений, состоящих из фотографии, документа и аудиозаписи:
const longpollAttachments = {
attach1: '88262293_457290160',
attach1_type: 'photo',
attach2: '88262293_532324610',
attach2_type: 'doc',
attach3: '88262293_535133534',
attach3_kind: 'audiomsg',
attach3_type: 'doc'
}
Обычно дополнительная информация о вложении не нужна, поэтому можно ограничиться получением названий вложений для предварительного отображения сообщения и анализа списка вложений, чтобы в случае необходимости получить сообщение через API.
Пример кода для получения массива с названиями вложений
function getAttachments(data) {
const attachments = [];
if (data.geo) {
attachments.push('geo');
}
for (const key in data) {
const match = key.match(/attach(\d+)$/);
if (match) {
const id = match[1];
const kind = data[`attach${id}_kind`];
let type = data[`attach${id}_type`];
if (kind === 'audiomsg') type = 'audio_message';
if (kind === 'graffiti') type = 'graffiti';
if (type === 'group') type = 'event';
attachments.push(type);
}
}
return attachments;
}
Определить наличие ответа на сообщение можно с помощью проверки наличия attachments.reply
(структура)
или флага reply_msg
.
В некоторых случаях получать сообщение через API для получения ответа на сообщение не нужно:
если сообщение, на которое пришло ответ, пришло недавно, то оно наверняка уже должно быть в локальном списке сообщений.
Найти сообщение можно с помощью conversation_message_id
из attachments.reply
.
Определить наличие пересланных сообщений можно с помощью проверки на отсутствие ответа на сообщение и на наличие attachments.fwd
.
ID пересланных сообщений, как и их количество, пока что не приходит через LongPoll.
Для получения некоторых вложений необязательно получать сообщение через API - в объект attachments
(структура)
приходят поля attachments_count
и attachments
, где и содержатся объекты вложений из API.
К примеру, в списке вложений можно найти стикер и голосовое сообщение.
При работе с вложениями можно попробовать найти необходимый элемент в документации, однако у некоторых вложений документация не обновлена или вовсе отсутствует.
В списке представлены все возможные параметры прав доступа с их флагом из 4 действия 52 события.
- Кто может приглашать участников в беседу
- Только создатель:
2097152
(1 << 21
) - Создатель и администраторы:
1
(1 << 0
) - Все участники:
0
- Только создатель:
- Кто может редактировать информацию беседы
- Только создатель:
4194304
(1 << 22
) - Создатель и администраторы:
8
(1 << 3
) - Все участники:
0
- Только создатель:
- Кто может менять закреплённое сообщение
- Только создатель:
8388608
(1 << 23
) - Создатель и администраторы:
4
(1 << 2
) - Все участники:
0
- Только создатель:
- Кто может отправлять массовые упоминания
- Только создатель:
1048576
(1 << 20
) - Создатель и администраторы:
524288
(1 << 19
) - Все участники:
0
- Только создатель:
- Кто может видеть ссылку на беседу
- Только создатель:
0
- Создатель и администраторы:
16777216
(1 << 24
) - Все участники:
33554432
(1 << 25
)
- Только создатель:
- Кто может начинать групповые звонки
- Только создатель:
134217728
(1 << 27
) - Создатель и администраторы:
67108864
(1 << 26
) - Все участники:
0
- Только создатель:
- Кто может назначать администраторов
- Только создатель:
0
- Создатель и администраторы:
16
(1 << 4
)
- Только создатель:
Дополнительные флаги:
- Имеется фантомная копия чата:
65536
(1 << 16
) - Является фантомным чатом:
32768
(1 << 15
)
random_id
начиная с версии API 5.90
стал обязательным параметром.
Но это не означает, что всем нужно генерировать уникальные значения для этого параметра.
Если вы не собираетесь его использовать, то можете указать в его значении число 0
.
Значение random_id
должно быть уникальным в течение одного часа в рамках app_id
, id
пользователя и peer_id
диалога.
Значение random_id
может принимать числа от -2147483648
(-(2^31)
) до 2147483647
(2^31 - 1
).
Если число будет больше или меньше данного порога, то из переданного числа отнимется этот лимит.
Основная задача этого параметра заключается в гарантировании идемпотентности API для отправки сообщений.
Это означает, что при отправке запросов с одинаковым random_id
и, чаще всего, одинаковыми параметрами,
сервер не будет создавать новое сообщение и вернет ID уже созданного.
Про необходимость данной технологии с убедительными примерами рассказывается в данной статье.
Наверняка вы уже видели, что во всех мессенджерах ВКонтакте при отправке сообщения само сообщение отображается сразу, но около сообщения некоторое время видно иконку часов. Эта иконка означает, что запрос на сервер с новым сообщением отправлен, но сообщение еще не пришло обратно через LongPoll.
Чтобы реализовать подобную фичу, нужно определить, какое именно сообщение, приходящее из LongPoll, было только что отправлено. Для этого нужно:
- Создать
random_id
и сохранить его в списке с отправляемыми сообщениями - Отправить сообщение
- Дождаться прихода из LongPoll сообщения с нашим
random_id
- Удалить
random_id
из списка, тем самым пометив сообщение как отправленное.