From bda101687273507911b7c829b78aab503ff6d158 Mon Sep 17 00:00:00 2001 From: Danil Nemov <d.nemov@corp.vk.com> Date: Sat, 26 Aug 2023 12:28:14 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B0=D0=BA=D1=82=D1=83=D0=B0=D0=BB=D0=B8?= =?UTF-8?q?=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B?= =?UTF-8?q?=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 102 +++++++++++++++++++----------------------------------- 1 file changed, 36 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index 1154ec0..063612d 100644 --- a/README.md +++ b/README.md @@ -77,41 +77,32 @@ __Long Polling__ — это технология, которая позволя `server`, `key` и `ts` получаются методом [`messages.getLongPollServer`](https://dev.vk.com/method/messages.getLongPollServer) -Важные параметры при вызове метода: -- в `need_pts` следует указать `1` -- в `lp_version` следует указать актуальную версию - 19 - -* `server` - Домен лонгполла. Индивидуален для каждого пользователя, а так же может различаться у некоторых приложений +* `server` - Домен лонгполла. Индивидуален для каждого пользователя. Может отличаться у некоторых приложений, от имени которых получался токен * `key` - Ключ для идентификации активной сессии. Протухает через час и привязан к айпи адресу * `ts` - Номер последнего события. Лонгполл будет возвращать события, следующие за ним -* `version` - Версия LongPoll, актуальная версия - 19. -Поведение предыдущих версий может измениться или их поддержка может быть прекращена +* `version` - Версия лонгполла * `wait` - Время ожидания нового события в секундах, максимум `90`. Рекомендуемое значение: `20` * `mode` - Настройка формата ответа. Рекомендуемое значение: `(1 << 1) | (1 << 3) | (1 << 5) | (1 << 7) | (1 << 9) | (1 << 10)` -TODO: информация о бизнес-уведомлениях - | Бит | Описание | |-----------|-------------------------------------------------------------------------------------------------------| | `1 << 1` | Возвращать кладжи (секции `additional` и `attachments` в [структуре сообщения](#структура-сообщения)) | | `1 << 3` | Возвращать расширенную информацию в [114], [115] и [119] событиях | | `1 << 5` | Возвращать `pts` | -| `1 << 6` | **[deprecated]** Возвращать события изменения онлайна друзей: [8] и [9] | +| `1 << 6` | **[неактуально]** Возвращать события изменения онлайна друзей: [8] и [9] | | `1 << 7` | Возвращать [`random_id`](#зачем-нужен-random_id) | | `1 << 9` | Возвращать события о бизнес-уведомлениях | | `1 << 10` | Возвращать тип `online` для `marked_users` при упоминании через `@online` | -**[deprecated]** у `1 << 6` бита выставлен, потому что события [8] и [9] больше не возвращаются из лонгполла - После выполнения запроса сервер вернет ответ следующего вида: ```ts type LongPollResult = // pts приходит, если в mode выставлен бит (1 << 5) // updates - массив событий, которые описаны в разделе "Описание событий" | { ts: number, pts: number, updates: unknown[] } - // ts слишком маленький (отстал более чем на 256 событий) или слишком большой. - // Необходимо воспользоваться переданным ts (лонгполл возвращает номер последнего события). + // ts слишком маленький (отстал более чем на 256 событий) или слишком большой (больше последнего существующего). + // Необходимо воспользоваться переданным ts (возвращается номер последнего события). // Как получить пропущенные события можно узнать в разделе "Получение истории событий" чуть ниже | { failed: 1, ts: number } // Ключ инвалидировался. Необходимо получить новый key, используя метод messages.getLongPollServer @@ -122,42 +113,17 @@ type LongPollResult = После обработки ответа нужно повторить запрос, но с новым значением `ts`. -Также необходимо сохранить полученный `pts`, чтобы при получении ошибки от лонгполла иметь возможность -[получить пропущенные события](#получение-истории-событий). - -Очень важно обработать описанные ошибки, потому что вы с ними обязательно столкнетесь. -Напомню, что время жизни ключа лонгполла всего час, и после его протухания придет 2 ошибка. -А с 1 ошибкой вы столкнетесь, когда на некоторое время приостановите походы в лонгполл, например когда -система уйдет в спящий режим, либо пропадет соединение с сетью по иным причинам. - ## Получение истории событий -Получать историю событий необходимо в том случае, когда лонгполл вернул `failed: 1` из-за слишком большого числа новых событий. +Если лонгполл вернул `failed: 1`, у вас все еще есть возможность получить пропущенные события. -Для получения истории событий нам необходим `pts` последнего известного вам события. С этим параметром нужно вызвать метод -[`messages.getLongPollHistory`](https://dev.vk.com/method/messages.getLongPollHistory). +1) Включите получение `pts` при [подключении](#подключение) к лонгполлу, добавив флаг `1 << 5` в `mode` +2) Всегда сохраняйте возвращаемый лонгполлом `pts` +3) С использованием `pts` и некоторых других параметров, описанных в документации, вызовите метод + [`messages.getLongPollHistory`](https://dev.vk.com/method/messages.getLongPollHistory). -Ответ выглядит следующим образом: -```ts -// TODO: новые тайпинги -// Описание типов: -// https://github.com/danyadev/vk-types#объекты -interface LongPollHistoryResult { - history: any[][] - from_pts: number - new_pts: number - conversations: VKConversation[] - messages: { - count: number - items: VKMessage[] - } - profiles?: VKUser[] - groups?: VKGroup[] - more?: true -} -``` - -TODO: рассказать подробнее про обработку поля `history` +Формат ответа метода описан здесь: +https://github.com/VKCOM/api-schema-typescript/blob/master/src/methods/messages.ts#L773 Поле `history` похоже на поле `updates` из лонгполла, но в нем будут находиться только персистентные события. Так же события из этого списка придут в урезанном виде: @@ -166,9 +132,12 @@ TODO: рассказать подробнее про обработку поля - `5` - [Редактирование сообщения](#событие-10005-редактирование-сообщения) - `18` - [Обновление сообщения](#событие-10018-обновление-сообщения) +Как можно заметить, вместо `10000 + eventId` событий приходят события 3, 4, 5 и 18. +Соответственно вместо `conversationMessageId` приходит просто `messageId`. +Сам `conversationMessageId` при необходимости можно достать из поля `messages` в ответе метода. + Структура этих событий выглядит так: ```ts -// TODO: да, здесь приходят такие события, а не 10000 + type type LongPollHistoryMessageEvent = [ type: 3 | 4 | 5 | 18, messageId: number, @@ -192,8 +161,6 @@ type LongPollHistoryMessageEvent = [ - [Вложения](#вложения) - [random_id](#зачем-нужен-random_id) -> Структура описывает массив, ключи используются для упрощения визуального восприятия - ```ts type LongPollMessage = [ type: 10003 | 10004 | 10005 | 10018, @@ -204,6 +171,7 @@ type LongPollMessage = [ minorId?: number, peerId: number, timestamp: number, + // Переносы строк обозначаются как <br>, а символы " & < > экранируются text: string, // Объект приходит только при указании флага 2 при подключении к LongPoll @@ -271,8 +239,6 @@ type LongPollMessage = [ ]; ``` -Стоит отметить, что в поле `text` переносы строк обозначаются как `<br>`, а символы `"`, `&`, `<` и `>` экранируются. - ### Короткий кортеж сообщения Существует возможность возвращения укороченного кортежа с сообщением. @@ -282,7 +248,7 @@ type LongPollMessage = [ `random_id`, то с этим событием определенно возникнут проблемы, потому что в нем отсутствует `random_id`. Чтобы получить `random_id`, нужно запросить сообщение через апи: в случае `10004` события по `minorId` (в данном случае это синоним `message_id`), -или в случае остальных событий по `cmid` (`conversationMessageId`) и `peerId`. +или в случае остальных событий по `conversationMessageId` и `peerId`. Далее, если сообщение было не нашим, апи вернет ошибку и мы ее проигнорируем, а если сообщение было нашим - вернет сообщение с полем `deleted: 1` и искомым `random_id`. @@ -526,6 +492,7 @@ type Event10013 = [ ``` ### Событие 10018. Обновление сообщения +[10018]: #событие-10018-обновление-сообщения Приходит при следующих событиях: 1. Добавился сниппет (ссылка) - к вложениям добавляется `link`. @@ -599,9 +566,9 @@ type Event21 = [ ### Событие 50. Перевод сообщения -Это событие приходит, если пользователь запросил перевод сообщения. Для этого надо выполнить метод `messages.translate` (доступен только официальным клиентам, параметры см. ниже). Метод возвращает `1`. +Это событие приходит, если пользователь запросил перевод сообщения. -Если для определённого сообщения перевод запрашивается в первый раз, то вместе с событием `50` приходит и [событие `10018`](#событие-10018-обновление-сообщения), где в объекте `additional` будет поле `is_translated`. +Если для определённого сообщения перевод запрашивается в первый раз, то вместе с событием `50` приходит и событие [10018], где в объекте `additional` будет поле `is_translated`. ```ts type Event50 = [ @@ -615,16 +582,13 @@ type Event50 = [ ]; ``` -Пример события: - -```js -[50, {"peer_id": 2000000026, "cmid":476587, "translation":"There are 253 reactions", "language":"ru-en"}] -``` - -Поле `language` представляет собой языковую пару, который выглядит так: `ru-en`. Перед дефисом прописан исходный язык переводимого текста, после дефиса — язык, на который будет переведён текст. +Поле `language` представляет собой языковую пару, который выглядит так: `ru-en`. +Перед дефисом прописан исходный язык переводимого текста, после дефиса — язык, на который будет переведён текст. Список поддерживаемых языковых пар можно получить, выполнив метод `account.getInfo`. В ответе будет поле `messages_translation_language_pairs`. +Чтобы запросить перевод сообщения, нужно выполнить метод `messages.translate` (доступен только официальным клиентам). + Параметры метода `messages.translate`: ```ts interface MessagesTranslateParams { @@ -661,7 +625,7 @@ type Event51 = [ | 7 | Выход из беседы | `id` вышедшего | | 8 | Исключение из беседы | `id` исключенного | | 9 | Разжалован администратор | `id` бывшего админа | -| 10 | Изменился баннер (какая-то инфа под шапкой в личке) | `0` | +| 10 | Изменился баннер | `0` | | 11 | Появление или скрытие клавиатуры | `peerId` | | 12 | Отозвано / подтверждено / отклонено / пришло приглашение в чат | `0` / `1` / `2` / `3` | | 13 | Контакт был сконвертирован в юзера (`contactId` -> `userId`) | `contactId` | @@ -673,6 +637,11 @@ type Event51 = [ | 19 | Начало или окончание группового звонка | `1` в начале, `0` в конце | | 22 | Чат больше не новый: пришло первое сообщение (только в лс) | `0` | | 23 | Изменено оформление чата | `0` | +| 24 | Изменилось описание чата | `0` | +| 25 | Изменилось состояние `chat_settings.short_poll_reactions` | `0` / `1` | +| 26 | _soon_ | _soon_ | +| 27 | _soon_ | _soon_ | +| 28 | _soon_ | _soon_ | При изменении названия (`1`) и обновлении аватарки беседы (`2`) нужные данные можно взять из [сервисного сообщения](#сервисные-сообщения) в [4 событии](#событие-4-новое-сообщение). @@ -680,7 +649,6 @@ type Event51 = [ ```ts type Event52 = [ type: 52, - // 0-19, 22, 23 updateType: number, peerId: number, extra: number @@ -1191,7 +1159,10 @@ interface LongPollKeyboard { #### Список существующих вложений -Список известных на данный момент вложений: `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`. +Список известных на данный момент вложений: +`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` @@ -1228,8 +1199,7 @@ const longpollAttachments = { и анализа списка вложений, чтобы в случае необходимости получить сообщение через [API](https://dev.vk.com/method/messages.getById). <details> -<summary markdown="span">Пример кода для получения массива с названиями вложений -</summary> +<summary markdown="span">Пример кода для получения массива с названиями вложений</summary> ```js function getAttachments(data) {